Skip to content

Fundamentals

From events to results

How a source, a filter, a transformation, and a sink turn mixed events into one result.

Study with an AI assistant 🤖

Copy the prompt and paste it into a chat with an AI assistant.

You are a tutor for the course "Stream Processing".
Topic: "From events to results"
In short: How a source, a filter, a transformation, and a sink turn mixed events into one result.
Article text in Markdown: https://stream-processing-systems.github.io/en/course/fundamentals/from-events-to-results.md
Base your explanations on this article. First ask how I want to study: a short summary, a step-by-step walkthrough, or a deep dive with questions that check my understanding. Use the article's example and terms. If you add anything the article does not say, point it out.

In our grocery service, each change to an order is recorded as an event. At the store, the courier desk needs to know when an order is packed, so that a courier can collect it. The order’s events also include proposals, answers, and deliveries. Sending all of them to the desk would bury the one fact it needs. How do we turn a flow of mixed events into the result the desk needs?

Processing starts from a source: the place where it gets its input records. Here the source hands over the order events from the store’s system as they arrive. The source is not the store floor where the picker works. It only provides records about what happened there.

For order ord-1042, the source provides seven events, from order_placed at 10:00 to order_delivered at 10:25. Only one of them matters to the courier desk.

The first step is a filter. It asks one question about each record: is its event_type equal to order_packed? If yes, the record moves on. If not, the filter drops it from this path. For ord-1042, six events are dropped and one moves on:

{
"event_id": "evt-005",
"event_type": "order_packed",
"order_id": "ord-1042",
"store_id": "store-418",
"occurred_at": "2026-10-21T10:09:00Z",
"data": {}
}

Dropping a record here does not delete it. Other parts of the system can still use the proposal and your answer; they just do not belong in the courier desk’s feed.

The packing event still carries more than the desk needs. The desk does not use the event’s identity or type, and data is empty. It needs to know which order, at which store, and when packing finished.

The second step is a transformation: a rule that builds a new record from the input. It copies order_id and store_id and renames occurred_at to packed_at. Every event has an occurred_at field, so the name is general. In the output, packing is the only thing that happened, so packed_at says exactly what the time means:

{
"order_id": "ord-1042",
"store_id": "store-418",
"packed_at": "2026-10-21T10:09:00Z"
}

Together, the filter and the transformation make up a processor: the component that applies rules to each record. Its output goes to a sink: the place where the results end up. Here the sink is the courier desk’s feed. In other systems, it could be a database, a file, or the input of another processor.

From events to results Order events go from the source to a processor, where a filter keeps the packing events and a transformation builds packing updates for the sink. PROCESSOR ORDER EVENTS ORDER_PACKED PACKING UPDATES Source the store's system Filter event_type = "order_packed" Transformation order_id, store_id, packed_at Sink the courier desk's feed

Source, processor, and sink are roles. A small program can play all three, and a large system can spread them across many machines.

The processor handles each packing event on its own: it does not look at earlier events and remembers nothing between them.

If the same packing event reaches the processor twice, the desk gets two identical updates. The rule has no way to tell that it has already seen this record.

The feed is a list of things that happened, not a table of current statuses. It says that ord-1042 was packed at 10:09. It does not say that the order is still waiting for a courier, and in fact a courier collected it at 10:12. The sink receives each update, but showing it to a courier is a separate job.

Some questions need exactly the memory this rule lacks. The store keeps a list of orders waiting for a customer’s answer. The proposal at 10:04 puts ord-1042 on that list, and your acceptance at 10:06 takes it off. Looking at the acceptance alone, the processor cannot tell whether the order was on the list at all. What does it need to keep between events to answer that?

  • A source provides input records, a processor applies rules to them, and a sink receives the results.
  • A filter keeps the records a result needs. A transformation builds the result from them.
  • This packing rule looks at one event at a time and remembers nothing between events.
  • Such a rule cannot notice a repeated event or answer a question that depends on earlier events.

© 2026 Grig Alex · CC BY-SA 4.0