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ímCo 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í.
# Ú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á.Související části prezentace
Š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í