Exercise 01 · Frontend → Mortgage Calculator API → database

Synchronous start

  • request/response
  • CRUD
  • temporal coupling

Principle

The mortgage calculator starts as an ordinary CRUD application: the frontend calls the API, the API writes a row to the database and waits for the response.

It is simple and right for a start. But it has two costs: UPDATE overwrites the previous value (history disappears) and the caller is tied to the availability and speed of every service in the chain (temporal coupling).

What to watch

  • After the second save, only the latest income is in the database. Who changed it, and from what? We don't know.
  • When the application is submitted, all services are called one after another. One offline service brings down the whole call, but the steps before it have already changed data.

Without the solution / With the solution

Without the solution

Run synchronous scenario

We save the calculation twice (income changes), then submit the application through the whole synchronous chain while the payout service is offline.

Run in the simulator without the solution

With the solution

Show what recording events brings

The solution is not a faster database but a different model: instead of overwriting state we record facts about changes (Event Sourcing, step 2) and send follow-up work asynchronously (steps 9 onwards). Here it is enough to see that the “offline” response is unavoidable in a synchronous chain.

Run in the simulator with the solution

What to do: Click “Run synchronous scenario”. What to look at: Only the latest income stays in the database. The previous one is gone. 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.

Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

Language
Variant
# Task for a coding agent: Synchronous start

Stack: Java. Mode: guided.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Specific task

Technical specifics (Java): Spring MVC controller and `@Transactional` service; simulate the outage with a client mock (MockWebServer/WireMock).

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: draw the call sequence from the UI to the database and list how many synchronous hops submitting the application has.
2. Verify three cases where information is lost: overwritten income, overwritten user-entered interest rate, who/when made the change.
3. Write a test that pins the current behaviour (characterization test): saving two changes in a row returns only the last value.
4. Write a test that simulates an outage of the last service in the chain (service not running; service returns 5xx) and verifies what remains written in the preceding services, what state the application is left in and what a repeated submission does.
5. Summarise the findings in a short document in `docs/` and propose where an event stream would arise. Implement nothing.

Watch out for:
- Do not confuse “the test passed” with “the behaviour is correct”: you are capturing the current state, not the ideal.
- A partial outage also includes steps that have already written data.
- Name characterization tests `*.characterization.test.*`; a later intentional behaviour change may modify or replace them.
- A service that does not respond hangs a test without a timeout: record it as a finding, do not test it.

## 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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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: Synchronous start

Stack: Java. Mode: challenge.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Brief (Challenge)

Document the save and submit flow of the application, pin it with tests and show where history is lost and where the chain is fragile. No change to production code.

Technical specifics (Java): Spring MVC controller and `@Transactional` service; simulate the outage with a client mock (MockWebServer/WireMock).

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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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: Synchronous start

Stack: C#. Mode: guided.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Specific task

Technical specifics (C#): ASP.NET Core controller and EF Core `DbContext`; simulate the outage with an `HttpMessageHandler` stub.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: draw the call sequence from the UI to the database and list how many synchronous hops submitting the application has.
2. Verify three cases where information is lost: overwritten income, overwritten user-entered interest rate, who/when made the change.
3. Write a test that pins the current behaviour (characterization test): saving two changes in a row returns only the last value.
4. Write a test that simulates an outage of the last service in the chain (service not running; service returns 5xx) and verifies what remains written in the preceding services, what state the application is left in and what a repeated submission does.
5. Summarise the findings in a short document in `docs/` and propose where an event stream would arise. Implement nothing.

Watch out for:
- Do not confuse “the test passed” with “the behaviour is correct”: you are capturing the current state, not the ideal.
- A partial outage also includes steps that have already written data.
- Name characterization tests `*.characterization.test.*`; a later intentional behaviour change may modify or replace them.
- A service that does not respond hangs a test without a timeout: record it as a finding, do not test it.

## 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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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: Synchronous start

Stack: C#. Mode: challenge.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Brief (Challenge)

Document the save and submit flow of the application, pin it with tests and show where history is lost and where the chain is fragile. No change to production code.

Technical specifics (C#): ASP.NET Core controller and EF Core `DbContext`; simulate the outage with an `HttpMessageHandler` stub.

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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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: Synchronous start

Stack: Web (HTML/JS or the app's stack). Mode: guided.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Specific task

Technical specifics (Web (HTML/JS or the app's stack)): Find the `fetch`/`axios` calls from the calculator form and trace them to where other services are called. Simulate the outage where the chain runs (a stopped service on the backend or a client double via `vi.fn()`); use MSW only if the project already has it.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: draw the call sequence from the UI to the database and list how many synchronous hops submitting the application has.
2. Verify three cases where information is lost: overwritten income, overwritten user-entered interest rate, who/when made the change.
3. Write a test that pins the current behaviour (characterization test): saving two changes in a row returns only the last value.
4. Write a test that simulates an outage of the last service in the chain (service not running; service returns 5xx) and verifies what remains written in the preceding services, what state the application is left in and what a repeated submission does.
5. Summarise the findings in a short document in `docs/` and propose where an event stream would arise. Implement nothing.

Watch out for:
- Do not confuse “the test passed” with “the behaviour is correct”: you are capturing the current state, not the ideal.
- A partial outage also includes steps that have already written data.
- Name characterization tests `*.characterization.test.*`; a later intentional behaviour change may modify or replace them.
- A service that does not respond hangs a test without a timeout: record it as a finding, do not test it.

## 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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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: Synchronous start

Stack: Web (HTML/JS or the app's stack). Mode: challenge.
Goal: Map the calculator's synchronous flow and find where history is lost and where temporal coupling arises. Nothing is rewritten yet.

## 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).

Read first, change nothing. Find and briefly summarise:
- The controller/endpoint that saves the calculation, and the persistence layer (entity, repository, table, or IndexedDB).
- All places where other services are called synchronously when the application is submitted (HTTP/gRPC clients).
- Where UPDATE (or `put` in IndexedDB) overwrites the previous value (income, price, interest rate).
- Which of these parts exist in the project and which are missing, including the broker, outbox, retry, timeout and idempotency. Write none of it now.

## Expected behaviour

The calculator starts as CRUD: the API overwrites a row in the database and the caller waits for every service in the chain. The cost: UPDATE erases history and one offline service brings down the whole submission (temporal coupling). Example: after two saves (income 80 000 → 95 000 CZK) only 95 000 remains and nowhere does it say who changed it or when. If the payout service is offline at submission, the call fails, but the services before it have already written data; a repeated submission can create duplicate records (e.g. a second account).

## Brief (Challenge)

Document the save and submit flow of the application, pin it with tests and show where history is lost and where the chain is fragile. No change to production code.

Technical specifics (Web (HTML/JS or the app's stack)): Find the `fetch`/`axios` calls from the calculator form and trace them to where other services are called. Simulate the outage where the chain runs (a stopped service on the backend or a client double via `vi.fn()`); use MSW only if the project already has it.

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

- Characterization tests exist for state overwriting and for a partial chain outage.
- The document describes the findings and a proposal for the next step.
- Production code is unchanged.
- 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