Forward-deployed / AI Knowledge Graph
Flowable

The event registry: Kafka in, process out

Motto — The event registry turns “write a consumer, parse the payload, call the engine” into three declarative files: a channel, an event definition, and a start event in the model.

Part of Phase 07 — Events, timers & messaging.

The Problem

Lesson 02’s webhook pattern assumed the world speaks HTTP to you. Increasingly it doesn’t — it speaks topics. Partner applications land on Kafka, payment confirmations on RabbitMQ, and core-banking events on JMS. The hand-rolled answer is a consumer service per topic: deserialize, validate, map fields, find-or-start the process instance, handle redelivery. Multiply that by every topic, and you’ve built a glue layer nobody budgeted, with its own deploys, bugs, and drift from the models it feeds. Flowable’s event registry deletes that layer. The mapping from broker events to process actions becomes deployable configuration.

The Concept

Three declarative pieces, deployed like models, versioned like models:

<style> .dgm-er{background:var(–color-surface);border:1px solid var(–color-border);border-top:3px solid var(–color-border-danger-outline); border-radius:16px;padding:28px 26px 22px;box-shadow:0 1px 3px rgb(var(–shadow-color) / 0.04),0 8px 24px rgb(var(–shadow-color) / 0.04); margin:26px 0;max-width:100%;font-family:inherit;text-align:left;position:relative} .dgm-er-chip{position:absolute;top:-11px;left:24px;background:var(–color-background-danger-solid);color:var(–white); font-size:10.5px;font-weight:700;letter-spacing:1px;text-transform:uppercase; border-radius:20px;padding:3px 12px;box-shadow:0 2px 6px color-mix(in oklab, var(–color-background-danger-solid) 28%, transparent)} .dgm-er h4{margin:0 0 4px;font-size:11px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;color:var(–color-text-secondary)} .dgm-er-sub{font-size:12px;color:var(–color-text-secondary);margin:0 0 16px} .dgm-er-chain{display:flex;flex-direction:column;gap:2px} .dgm-er-node{background:var(–color-background-danger-surface);border:1.5px solid var(–color-border-danger-surface);border-radius:10px;padding:9px 14px; font-weight:700;font-size:11px;color:var(–color-text-danger)} .dgm-er-node.alt{background:var(–color-surface-tertiary);border-color:var(–color-border);color:var(–color-text)} .dgm-er-arr{display:flex;justify-content:center;color:var(–color-text-tertiary);padding:1px 0;font-size:9.5px;text-align:center} @media (max-width:640px){.dgm-er{padding:20px 16px 18px}} </style> <div class=“dgm-er”> <span class=“dgm-er-chip”>From broker message to BPMN event</span> <h4>Channel, then event, then model — three translations from Kafka to a token</h4> <p class=“dgm-er-sub”>Each layer answers a different question.</p> <div class=“dgm-er-chain”> <div class=“dgm-er-node alt”>Kafka topic — loan-applications</div> <div class=“dgm-er-arr”>↓</div> <div class=“dgm-er-node blue”>Channel (.channel) — WHERE events come from: broker, topic, serialization</div> <div class=“dgm-er-arr”>↓</div> <div class=“dgm-er-node accent”>Event definition (.event) — WHAT an event is: key, payload fields, correlation params</div> <div class=“dgm-er-arr”>↓</div> <div class=“dgm-er-node green”>Model reference — WHAT HAPPENS: start event or intermediate catch, eventType=applicationReceived</div> </div> </div>

Piece Owns Analogy
Channel transport: inbound/outbound, kafka/jms/rabbit, topic, JSON mapping a typed socket
Event definition schema: payload fields + which of them are correlation parameters the message contract
Model reference reaction: start a new instance, or wake a waiting catch lesson 02, generalised

The registry rides on machinery you already own. An inbound event with correlation parameters behaves like lesson 02’s message: it finds the one instance where applicationId matches. An event start creates instances the way message starts do. Outbound channels run the other way — a process step publishes an event instead of calling an API. What’s genuinely new is where the integration logic lives: in deployable artifacts the same review/version pipeline covers, not in a sidecar service.

Here is the honest boundary: the registry does transport, schema, and correlation. It does not do content-based transformation, enrichment, aggregation of three topics into one event, or ordering guarantees beyond the broker’s. If the mapping needs logic, you still want a thin stream processor in front. The registry then consumes its clean output topic.

Use It

The three artifacts for the capstone’s intake, all shipped under this lesson’s outputs/ folder:

loan-applications.channel — where:

{
  "key": "loanApplicationsChannel",
  "channelType": "inbound",
  "type": "kafka",
  "topics": ["loan-applications"],
  "deserializerType": "json",
  "channelEventKeyDetection": { "fixedValue": "applicationReceived" }
}

application-received.event — what:

{
  "key": "applicationReceived",
  "correlationParameters": [ { "name": "applicationId", "type": "string" } ],
  "payload": [
    { "name": "applicationId", "type": "string" },
    { "name": "pan", "type": "string" },
    { "name": "amount", "type": "double" },
    { "name": "channel", "type": "string" }
  ]
}

And the reaction, in the BPMN model — a start event referencing the event type:

<startEvent id="applicationArrives" flowable:eventType="applicationReceived">
  <extensionElements>
    <flowable:eventOutParameter source="applicationId" target="applicationId"/>
    <flowable:eventOutParameter source="amount" target="amount"/>
  </extensionElements>
</startEvent>

Deploy all three — in Spring Boot, drop them in src/main/resources/eventregistry/; the Kafka connection itself is spring.kafka.* configuration. Then publish a test event with outputs/producer.py:

$ python3 producer.py APP-1042
published: {'applicationId': 'APP-1042', 'pan': 'ABCDE1234F', 'amount': 750000.0, ...}
# engine side: a new loanOrigination instance exists, variables populated

No controller, no consumer service, no glue — the diagram now starts from a topic.

Ship It

This lesson ships the intake trio — loan-applications.channel, application-received.event, producer.py — reused by the capstone as its event-driven front door.

Check Yourself

Q1. The registry’s three pieces split responsibilities as…

  • A) channel = what, event = where, model = when
  • B) channel = where (transport/topic), event = what (schema + correlation), model = what happens (start/continue)
  • C) all three describe the payload
  • D) the split is arbitrary

<details><summary>Answer</summary>B — transport, contract, reaction. Each changes on a different cadence. That’s why they’re separate deployable files.</details>

Q2. An inbound event should continue a waiting instance rather than start one. What makes that work?

  • A) the topic name
  • B) correlation parameters on the event definition matching the instance — lesson 02’s mechanism, fed from a topic instead of a webhook
  • C) the instance polls the topic
  • D) it can’t; registry events only start instances

<details><summary>Answer</summary>B — the registry is a transport adapter. The find-the-one-instance semantics underneath is exactly the message correlation you built.</details>

Q3. Events on the topic need enrichment from a CRM before they’re usable. Where does that logic go?

  • A) in the channel definition
  • B) in a stream processor in front; the registry consumes its clean output topic
  • C) in the correlation parameters
  • D) in a script task after the start

<details><summary>Answer</summary>B — the registry is deliberately logic-free: transport, schema, correlation. Transformation is a job for code that can be tested as code.</details>

Challenge. Design, on paper, the outbound half: a channel + event pair that publishes decisionMade {applicationId, decision, rate} after the capstone’s DMN task. Downstream systems then consume decisions from a topic instead of polling history. Decide which field is the Kafka record key, and why that choice matters for consumers’ ordering.

Related