Exercise 13 · ContractGenerationRequested → ContractGenerated → ContractSigned
Document Service: delivery order
- out-of-order
- corrSeq
- resequencing
- rebuild from the event store
Principle
A broker in a distributed system doesn't guarantee global ordering. The ContractSigned event may reach the projection before ContractGenerated.
A projection that applies events in delivery order shows the wrong state (the state “goes backwards”) or loses information.
Solution: events carry an order within the correlation (corrSeq). The projection holds early events in a buffer and discards duplicates. And above all: the truth is in the event store, the projection can always be rebuilt.
What to watch
- The processing order in the broker timeline differs from the order in the event store.
- The projection's final state vs. the Process Manager.
- The “Rebuild projection from event store” button.
Without the solution / With the solution
Without the solution
Run with shuffled order
The projection receives events with a random delay of up to 5 s and applies them as they arrive.
Run in the simulator without the solutionWith the solution
Run with corrSeq ordering
The projection waits for missing events and applies them in the correct order. The result is the same as in the Process Manager.
Run in the simulator with the solutionWhat to do: Click “Run with shuffled order”. What to look at: The contract state in the view doesn't match the actual state. The simulator needs JavaScript and runs as a separate application.
Try it in your own project
Copy the prompt into your coding agent (Claude Code, Copilot, Codex…) and run it in your project's repository.
Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation.
# Task for a coding agent: Document Service: delivery order Stack: Java. Mode: guided. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (Java backend (typically Java 21, Spring Boot, JPA or JDBC). Use the build tool and style the project already has (Maven/Gradle, JUnit 5).). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Specific task Technical specifics (Java): Kafka: partition key = applicationId; elsewhere a buffer in the projection table. Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation: 1. Plan: where order really matters (per aggregate / correlation) and where it does not. 2. Add `sequence` (position in the application flow) to integration messages. If the contract must not get a new field, put it in the message header; process a message without it as before. 3. Projection and process alike: discard an event with a lower number than expected, defer a higher one, apply a matching one. A process with a side effect advances its state only after the effect succeeds; a rebuild does not repeat the effect. 4. Buffer timeout derived from the producer's retry window: after the deadline, raise an alarm and refill the message from the source; never skip the gap. 5. Test: every ordering of one application's messages (with duplicates, two applications interleaved) yields the same final state; at least one test fails without ordering. Watch out for: - A partition key keeps order only within a topic; a flow across several topics is ordered by the consumer. - A consumer ordering by sequence subscribes to all topics of the flow, otherwise it waits forever. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - JUnit 5 tests in the existing `src/test/java` structure pass with the project's build tool. - In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: Document Service: delivery order Stack: Java. Mode: challenge. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (Java backend (typically Java 21, Spring Boot, JPA or JDBC). Use the build tool and style the project already has (Maven/Gradle, JUnit 5).). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Brief (Challenge) Make sure the read model does not depend on the order in which events are delivered, and allow it to be rebuilt from the event store. Technical specifics (Java): Kafka: partition key = applicationId; elsewhere a buffer in the projection table. Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - JUnit 5 tests in the existing `src/test/java` structure pass with the project's build tool. - In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: Document Service: delivery order Stack: C#. Mode: guided. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (C# backend (.NET, ASP.NET Core, EF Core or Dapper). Use the solution, style and DI conventions the project already has (xUnit/NUnit).). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Specific task Technical specifics (C#): Service Bus sessions or partition key; elsewhere a buffer in the projection table. Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation: 1. Plan: where order really matters (per aggregate / correlation) and where it does not. 2. Add `sequence` (position in the application flow) to integration messages. If the contract must not get a new field, put it in the message header; process a message without it as before. 3. Projection and process alike: discard an event with a lower number than expected, defer a higher one, apply a matching one. A process with a side effect advances its state only after the effect succeeds; a rebuild does not repeat the effect. 4. Buffer timeout derived from the producer's retry window: after the deadline, raise an alarm and refill the message from the source; never skip the gap. 5. Test: every ordering of one application's messages (with duplicates, two applications interleaved) yields the same final state; at least one test fails without ordering. Watch out for: - A partition key keeps order only within a topic; a flow across several topics is ordered by the consumer. - A consumer ordering by sequence subscribes to all topics of the flow, otherwise it waits forever. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - Tests in the existing test project pass (`dotnet test`). - In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: Document Service: delivery order Stack: C#. Mode: challenge. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (C# backend (.NET, ASP.NET Core, EF Core or Dapper). Use the solution, style and DI conventions the project already has (xUnit/NUnit).). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Brief (Challenge) Make sure the read model does not depend on the order in which events are delivered, and allow it to be rebuilt from the event store. Technical specifics (C#): Service Bus sessions or partition key; elsewhere a buffer in the projection table. Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - Tests in the existing test project pass (`dotnet test`). - In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: Document Service: delivery order Stack: Web (HTML/JS or the app's stack). Mode: guided. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (web application: a browser frontend, possibly with its own backend. If the project has a backend, the event store, broker and services belong on the backend, and the points about endpoints and tables refer to it. If it has no backend, use IndexedDB in the browser and `fake-indexeddb` in tests. Keep the project's framework and test runner; add new dependencies only with a justification.). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Specific task Technical specifics (Web (HTML/JS or the app's stack)): Buffer in memory/IndexedDB and a pure function `applyInOrder(events)` tested on permutations; the rebuild source is the broker log or the message store. Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation: 1. Plan: where order really matters (per aggregate / correlation) and where it does not. 2. Add `sequence` (position in the application flow) to integration messages. If the contract must not get a new field, put it in the message header; process a message without it as before. 3. Projection and process alike: discard an event with a lower number than expected, defer a higher one, apply a matching one. A process with a side effect advances its state only after the effect succeeds; a rebuild does not repeat the effect. 4. Buffer timeout derived from the producer's retry window: after the deadline, raise an alarm and refill the message from the source; never skip the gap. 5. Test: every ordering of one application's messages (with duplicates, two applications interleaved) yields the same final state; at least one test fails without ordering. Watch out for: - A partition key keeps order only within a topic; a flow across several topics is ordered by the consumer. - A consumer ordering by sequence subscribes to all topics of the flow, otherwise it waits forever. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - Tests pass with the existing test runner; lint and build pass if the project has them. - In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: Document Service: delivery order Stack: Web (HTML/JS or the app's stack). Mode: challenge. Goal: Handle out-of-order delivery in the application-state read model and in the Document Service process (loan contract) using a sequence number within the correlation. ## Context of the existing code You are working in an existing project (web application: a browser frontend, possibly with its own backend. If the project has a backend, the event store, broker and services belong on the backend, and the points about endpoints and tables refer to it. If it has no backend, use IndexedDB in the browser and `fake-indexeddb` in tests. Keep the project's framework and test runner; add new dependencies only with a justification.). Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout). Prerequisite: the project has a broker and an application message flow with `correlationId`. If not, first set up a minimal in-memory broker and the flow submission → risk → approval → account. Read first, change nothing. Find and briefly summarise: - Where a projection or handler assumes event ordering. If Document Service is missing as a consumer, design it: it creates the contract once it has the application submission, the approval and `MortgageAccountOpened`. - Whether messages carry a stream sequence/version and a correlationId. - Broker options: partition key / session to preserve ordering. - Which of these parts already exist in the project and which are missing. Before you write your own infrastructure (broker client, outbox, retry), check whether the project or organisation already has it as a ready-made library, and use it. ## Expected behaviour The broker does not guarantee order: a projection that applies messages as they arrive “goes backwards” or loses data. A flow message carries a sequence within the correlation (first is 1, each follow-up 1 more than its cause); the consumer defers an early one and discards a duplicate. The truth lives in the message source, so the projection can be rebuilt. Example: approval (3) arrives before risk (2); without ordering the application state loses its score. ## Brief (Challenge) Make sure the read model does not depend on the order in which events are delivered, and allow it to be rebuilt from the event store. Technical specifics (Web (HTML/JS or the app's stack)): Buffer in memory/IndexedDB and a pure function `applyInOrder(events)` tested on permutations; the rebuild source is the broker log or the message store. Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open. ## What you must not change - The existing public API and the behaviour of the calculator endpoints that currently work. - The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not). - Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary. - Public contracts (APIs, event schemas) without backward compatibility. ## Done when - A test of all orderings (with duplicates) and a test that fails without ordering. - Duplicates are not applied twice; the process side effect runs once. - There is a command for rebuild (endpoint or CLI). - Tests pass with the existing test runner; lint and build pass if the project has them. - In the summary, state: what you changed, what you assumed, what remains.
Related parts of the presentation
Training for your team
Would you like this training for your team?
We run the training on your code and your domain problem. We agree the scope, dates and format with you.
Discuss the training