Cvičení 07 · IncomeChanged v1 → v2 a upcasting

Evoluce schématu událostí

  • verzování událostí
  • upcaster
  • zpětná kompatibilita

Princip

Události v event store se nepřepisují, ale jazyk se vyvíjí. IncomeChanged v1 nesl roční příjem (`income`), v2 nese měsíční (`monthlyIncome`).

Nový kód musí číst staré události. Řešení: upcaster převede v1 na v2 při načtení, uložená historie zůstane beze změny.

Co sledovat

  • Stav příjmu po přehrání: NaN vs. 100 000.
  • V detailu události je vidět schemaVersion 1.

Bez řešení / S řešením

Bez řešení

Spustit bez upcasteru

Do streamu přijde událost IncomeChanged v1 (roční příjem 1,2 M). Nový kód čeká pole `monthlyIncome`.

Spustit v simulátoru bez řešení

S řešením

Spustit s upcasterem

Upcaster převede roční příjem na měsíční (1 200 000 / 12). Uložená událost zůstala ve v1.

Spustit v simulátoru s řešením

Co udělat: Klikni na „Spustit bez upcasteru“. Na co se dívat: Příjem ve starém tvaru se změní na nesmyslnou hodnotu (NaN). Simulátor potřebuje JavaScript a běží jako samostatná aplikace.

Zkus to ve svém projektu

Zkopíruj prompt do svého coding agenta (Claude Code, Copilot, Codex…) a spusť ho v repozitáři svého projektu.

Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

Jazyk
Varianta
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: Java. Režim: s vedením.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (Java backend (typicky Java 21, Spring Boot, JPA nebo JDBC). Použij build nástroj a styl, který projekt už má (Maven/Gradle, JUnit 5).).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Konkrétní úkol

Technická specifika (Java): Upcaster jako samostatná třída v deserializaci (Jackson `JsonNode` transformace).

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: popiš změnu v1 → v2, zda je sémantická (jednotky) nebo jen strukturální a zda je bezeztrátová (zaokrouhlení mění uložená fakta).
2. Přidej `schemaVersion` do uložených událostí, pokud chybí (výchozí 1 pro stará data). Zvyš ji jen měněnému typu.
3. Implementuj upcaster `v1 -> v2` v čtecí cestě před `apply`.
4. Test: fixture se streamem zapsaným starým kódem se přehraje na správný stav; nová událost se zapisuje ve v2.
5. Ověř, že jiní čtenáři (projekce) dostávají už upcastnutou podobu.

Na co si dát pozor:
- Zpětně nelze „opravit“ historii změnou apply: potřebuješ verzi události.
- Upcaster musí být deterministický a bez I/O.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- JUnit 5 testy ve stávající struktuře `src/test/java` projdou build nástrojem projektu.
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: Java. Režim: challenge.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (Java backend (typicky Java 21, Spring Boot, JPA nebo JDBC). Použij build nástroj a styl, který projekt už má (Maven/Gradle, JUnit 5).).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Zadání (Challenge)

Změň význam pole IncomeChanged (roční → měsíční) tak, aby se stará historie přehrála správně a nic se nepřepsalo v databázi.

Technická specifika (Java): Upcaster jako samostatná třída v deserializaci (Jackson `JsonNode` transformace).

Postup si zvol sám. Pasti v tomto tématu nejsou vypsané: najdi je (souběh, opakované doručení, pořadí, selhání uprostřed) a ke každé napiš test, který by ji odhalil. Na konci vypiš, jaké pasti jsi našel a které jsi vědomě nechal otevřené.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- JUnit 5 testy ve stávající struktuře `src/test/java` projdou build nástrojem projektu.
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: C#. Režim: s vedením.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (C# backend (.NET, ASP.NET Core, EF Core nebo Dapper). Použij solution, styl a DI konvence, které projekt už má (xUnit/NUnit).).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Konkrétní úkol

Technická specifika (C#): Upcaster jako služba v DI, transformace přes `JsonNode` před deserializací.

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: popiš změnu v1 → v2, zda je sémantická (jednotky) nebo jen strukturální a zda je bezeztrátová (zaokrouhlení mění uložená fakta).
2. Přidej `schemaVersion` do uložených událostí, pokud chybí (výchozí 1 pro stará data). Zvyš ji jen měněnému typu.
3. Implementuj upcaster `v1 -> v2` v čtecí cestě před `apply`.
4. Test: fixture se streamem zapsaným starým kódem se přehraje na správný stav; nová událost se zapisuje ve v2.
5. Ověř, že jiní čtenáři (projekce) dostávají už upcastnutou podobu.

Na co si dát pozor:
- Zpětně nelze „opravit“ historii změnou apply: potřebuješ verzi události.
- Upcaster musí být deterministický a bez I/O.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- Testy ve stávajícím testovacím projektu projdou (`dotnet test`).
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: C#. Režim: challenge.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (C# backend (.NET, ASP.NET Core, EF Core nebo Dapper). Použij solution, styl a DI konvence, které projekt už má (xUnit/NUnit).).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Zadání (Challenge)

Změň význam pole IncomeChanged (roční → měsíční) tak, aby se stará historie přehrála správně a nic se nepřepsalo v databázi.

Technická specifika (C#): Upcaster jako služba v DI, transformace přes `JsonNode` před deserializací.

Postup si zvol sám. Pasti v tomto tématu nejsou vypsané: najdi je (souběh, opakované doručení, pořadí, selhání uprostřed) a ke každé napiš test, který by ji odhalil. Na konci vypiš, jaké pasti jsi našel a které jsi vědomě nechal otevřené.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- Testy ve stávajícím testovacím projektu projdou (`dotnet test`).
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: Web (HTML/JS nebo stack aplikace). Režim: s vedením.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (webová aplikace: frontend v prohlížeči, případně s vlastním backendem. Když má projekt backend, patří event store, broker a služby na backend a body o endpointech a tabulkách se týkají jeho. Když backend nemá, použij IndexedDB v prohlížeči a v testech `fake-indexeddb`. Framework a test runner projektu zachovej; nové závislosti přidávej jen s odůvodněním.).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Konkrétní úkol

Technická specifika (Web (HTML/JS nebo stack aplikace)): Čistá funkce `upcast(event)` ve všech čtecích cestách: načtení streamu (i od snapshotu) před `apply` a feed projekcí.

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: popiš změnu v1 → v2, zda je sémantická (jednotky) nebo jen strukturální a zda je bezeztrátová (zaokrouhlení mění uložená fakta).
2. Přidej `schemaVersion` do uložených událostí, pokud chybí (výchozí 1 pro stará data). Zvyš ji jen měněnému typu.
3. Implementuj upcaster `v1 -> v2` v čtecí cestě před `apply`.
4. Test: fixture se streamem zapsaným starým kódem se přehraje na správný stav; nová událost se zapisuje ve v2.
5. Ověř, že jiní čtenáři (projekce) dostávají už upcastnutou podobu.

Na co si dát pozor:
- Zpětně nelze „opravit“ historii změnou apply: potřebuješ verzi události.
- Upcaster musí být deterministický a bez I/O.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- Testy projdou stávajícím test runnerem; lint a build projdou, pokud je projekt má.
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.
# Úkol pro coding agenta: Evoluce schématu událostí

Stack: Web (HTML/JS nebo stack aplikace). Režim: challenge.
Cíl: Změnit tvar existující události bez přepisu uložených dat a bez rozbití přehrávání.

## Kontext existujícího kódu

Pracuješ v existujícím projektu (webová aplikace: frontend v prohlížeči, případně s vlastním backendem. Když má projekt backend, patří event store, broker a služby na backend a body o endpointech a tabulkách se týkají jeho. Když backend nemá, použij IndexedDB v prohlížeči a v testech `fake-indexeddb`. Framework a test runner projektu zachovej; nové závislosti přidávej jen s odůvodněním.).
Doména: hypoteční kalkulačka, která se postupně mění v event-driven systém (kalkulačka, rizika, schvalování, identita, účet, dokumenty, výplata).
Předpoklad: projekt má event store a projekce. Pokud ne, nejdřív založ minimální event store (append-only události, načtení streamu přes `apply`).

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Definice událostí a jejich serializaci.
- Kde se čtou události z event store (stream, snapshot, feed projekcí).
- Jestli události nesou verzi schématu.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (broker klient, outbox, retry), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

## Očekávané chování

Uložené události se nepřepisují, ale jazyk se vyvíjí: `IncomeChanged` v1 nesl roční příjem, v2 měsíční. Bez upcastu dá přehrání příjem `NaN`, s upcastem 100 000; uložená událost zůstane ve v1. Stream i projekce čtou přes stejný upcast, verzi novější, než kód zná, odmítne. Výchozí změna: `LoanTermEntered { termYears }` → `{ termMonths }` (jiná jednotka, bez ztráty).

## Zadání (Challenge)

Změň význam pole IncomeChanged (roční → měsíční) tak, aby se stará historie přehrála správně a nic se nepřepsalo v databázi.

Technická specifika (Web (HTML/JS nebo stack aplikace)): Čistá funkce `upcast(event)` ve všech čtecích cestách: načtení streamu (i od snapshotu) před `apply` a feed projekcí.

Postup si zvol sám. Pasti v tomto tématu nejsou vypsané: najdi je (souběh, opakované doručení, pořadí, selhání uprostřed) a ke každé napiš test, který by ji odhalil. Na konci vypiš, jaké pasti jsi našel a které jsi vědomě nechal otevřené.

## Co nesmíš měnit

- Stávající veřejné API a chování endpointů kalkulačky, které teď funguje.
- Databázové schéma stávajících tabulek (nové tabulky/migrace jsou v pořádku, přejmenování a mazání ne).
- Cizí testy, seed data a existující hodnoty. Když je změna nutná, dej ji do samostatného commitu a uveď ji. Pokud změna tvaru události nebo nový konzument nutně mění existující test, uprav ho v samostatném commitu a v závěru uveď proč.
- Veřejné kontrakty (API, schémata událostí) bez zpětné kompatibility.

## Hotovo, když

- Fixture se starou událostí se přehraje na očekávaný stav.
- Žádný skript nepřepisuje uložené události.
- Zápis nových událostí měněného typu používá v2.
- Projekce dostanou jen v2; neznámá novější verze skončí chybou.
- Testy projdou stávajícím test runnerem; lint a build projdou, pokud je projekt má.
- V závěru uveď: co jsi změnil, co jsi předpokládal, co zbývá.

Školení pro váš tým

Chcete školení pro svůj tým?

Školení vedeme na vašem kódu a vašem doménovém problému. Domluvíme rozsah, termín a formu.

Probrat školení