Exercise 16 · The whole process from approval to disbursement, compensation

Saga / Process Manager

  • Process Manager
  • orchestration vs choreography
  • kompenzace
  • timeout

Principle

Mortgage onboarding is a long process across several services: identity → account → contract → signature → disbursement. It cannot be wrapped in a single transaction.

The Process Manager (Saga) holds the process state, issues commands (…Requested) and reacts to results. Risk and Approval are choreography (services react to events), onboarding is orchestration.

When a step fails permanently, there is no “rollback”. The saga triggers compensation: it closes the account already opened. Without it the bank is left with an account and no payout.

What to watch

  • Process steps and their states in the Process Manager panel.
  • Process state: failed vs. compensated.
  • The MortgageAccountClosed event as a compensating action.

Without the solution / With the solution

Without the solution

Run: payout is rejected, without compensation

Disbursement rejects the payout (limit/AML). The account and contract already exist and nobody cleans them up.

Run in the simulator without the solution

With the solution

Run with compensation

After MortgageDisbursementFailed the saga issues a command to close the account. The process ends in the compensated state and the system is consistent.

Run in the simulator with the solution

What to do: Click “Run: payout is rejected, without compensation”. What to look at: The account stays open and the process ends in the failed 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.

Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

Language
Variant
# Task for a coding agent: Saga / Process Manager

Stack: Java. Mode: guided.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Specific task

Technical specifics (Java): Spring State Machine/Temporal/Axon Saga, if available in the organisation; otherwise a state table + handler.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: a table of steps (trigger, completion, timeout × attempts, compensation) and transitions. Final states: completed, rejected, failed, `compensated`, manual intervention.
2. Process Manager: persisted state per application (table with a version), reacts to events, issues commands (retry step, compensate, create task) only after saving the state.
3. Every step has a timeout and a retry policy; the attempt timeout is longer than the time the worker retries on its own. A step fails only after the PM's last attempt; a message in the DLQ is not a process failure.
4. Compensation for reversible steps (close the account) by application id, and manual intervention for irreversible ones (contract, payout). Once the process is stopped, workers skip the application's step.
5. Test: every step fails → the process ends in a defined state; compensation fails → manual intervention.

Watch out for:
- A saga is not a distributed transaction: inconsistency between steps is visible, and a late result may arrive even after compensation.
- Compensation can fail too.

## 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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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: Saga / Process Manager

Stack: Java. Mode: challenge.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Brief (Challenge)

Introduce a Process Manager for mortgage onboarding and ensure that a failure of any step ends in a consistent state.

Technical specifics (Java): Spring State Machine/Temporal/Axon Saga, if available in the organisation; otherwise a state table + handler.

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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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: Saga / Process Manager

Stack: C#. Mode: guided.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Specific task

Technical specifics (C#): MassTransit Saga/NServiceBus/Durable Functions, if available in the organisation; otherwise a state table + handler.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: a table of steps (trigger, completion, timeout × attempts, compensation) and transitions. Final states: completed, rejected, failed, `compensated`, manual intervention.
2. Process Manager: persisted state per application (table with a version), reacts to events, issues commands (retry step, compensate, create task) only after saving the state.
3. Every step has a timeout and a retry policy; the attempt timeout is longer than the time the worker retries on its own. A step fails only after the PM's last attempt; a message in the DLQ is not a process failure.
4. Compensation for reversible steps (close the account) by application id, and manual intervention for irreversible ones (contract, payout). Once the process is stopped, workers skip the application's step.
5. Test: every step fails → the process ends in a defined state; compensation fails → manual intervention.

Watch out for:
- A saga is not a distributed transaction: inconsistency between steps is visible, and a late result may arrive even after compensation.
- Compensation can fail too.

## 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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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: Saga / Process Manager

Stack: C#. Mode: challenge.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Brief (Challenge)

Introduce a Process Manager for mortgage onboarding and ensure that a failure of any step ends in a consistent state.

Technical specifics (C#): MassTransit Saga/NServiceBus/Durable Functions, if available in the organisation; otherwise a state table + handler.

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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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: Saga / Process Manager

Stack: Web (HTML/JS or the app's stack). Mode: guided.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Specific task

Technical specifics (Web (HTML/JS or the app's stack)): Orchestration and durable state on the server; the UI only reads the state and displays it as a state machine (xstate, if the project may add a dependency; otherwise a pure function).

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: a table of steps (trigger, completion, timeout × attempts, compensation) and transitions. Final states: completed, rejected, failed, `compensated`, manual intervention.
2. Process Manager: persisted state per application (table with a version), reacts to events, issues commands (retry step, compensate, create task) only after saving the state.
3. Every step has a timeout and a retry policy; the attempt timeout is longer than the time the worker retries on its own. A step fails only after the PM's last attempt; a message in the DLQ is not a process failure.
4. Compensation for reversible steps (close the account) by application id, and manual intervention for irreversible ones (contract, payout). Once the process is stopped, workers skip the application's step.
5. Test: every step fails → the process ends in a defined state; compensation fails → manual intervention.

Watch out for:
- A saga is not a distributed transaction: inconsistency between steps is visible, and a late result may arrive even after compensation.
- Compensation can fail too.

## 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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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: Saga / Process Manager

Stack: Web (HTML/JS or the app's stack). Mode: challenge.
Goal: Introduce a Process Manager for mortgage onboarding with state, timeouts and compensation.

## 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 asynchronous onboarding via a broker (identity → account → contract → payout). If not, first create a minimal in-memory flow of these steps.

Read first, change nothing. Find and briefly summarise:
- Where onboarding is currently coordinated by one large service/handler, or whether it is choreography. Do not change a synchronous handler with a public API.
- Which steps are reversible and by what (compensating action). Propose missing ones as an idempotent endpoint.
- Storage for the process state.
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (process manager, saga), check whether the project or organisation already has it as a ready-made library, and use it. If it is missing, propose it in the plan.

## Expected behaviour

Onboarding spans several services and cannot be wrapped in a single transaction. The Process Manager (Saga) holds state per application, issues commands and reacts to results; a step that does not finish within its deadline is retried. When a step fails permanently, rollback is impossible: the PM reverts the completed reversible steps and creates a task for a human for the irreversible ones. Example: the payout fails (limit, AML); the PM issues a command to close the account (result `MortgageAccountClosed`), creates a task for the issued contract, and once it is resolved the process ends as `compensated`.

## Brief (Challenge)

Introduce a Process Manager for mortgage onboarding and ensure that a failure of any step ends in a consistent state.

Technical specifics (Web (HTML/JS or the app's stack)): Orchestration and durable state on the server; the UI only reads the state and displays it as a state machine (xstate, if the project may add a dependency; otherwise a pure function).

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 transition table and a test for the failure of each step.
- Process state survives a restart (test: a new instance over the same store continues).
- Compensations are idempotent; once the process is stopped, further steps are not executed.
- 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.

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