Cvičení 05 · Zkrácení načtení dlouhého streamu

Snapshot

  • snapshot
  • cena replaye
  • verze kódu agregátu

Princip

Načtení agregátu přehrává všechny události. U dlouhého streamu to stojí čas (v panelu vidíš „přehráno N událostí“).

Snapshot je uložený stav agregátu v konkrétní verzi streamu. Při načtení se vezme snapshot a přehrají se jen novější události.

Snapshot je cache, ne zdroj pravdy. Když se změní kód agregátu, starý snapshot se musí zahodit a stream přehrát znovu.

Co sledovat

  • Počet přehraných událostí při načtení.
  • Po „Nasadit novou verzi agregátu“ se snapshot ignoruje a přehraje se vše.

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

Bez řešení

Spustit 300 změn bez snapshotu

Poradce udělal 300 drobných změn. Každé načtení agregátu přehraje všech 301 událostí.

Spustit v simulátoru bez řešení

S řešením

Spustit se snapshotem každých 50 událostí

Stejných 300 změn, ale načtení přehraje jen události od posledního snapshotu. Zkus „Nasadit novou verzi agregátu“: snapshot se zneplatní.

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

Co udělat: Klikni na „Spustit 300 změn bez snapshotu“. Na co se dívat: Načtení agregátu přehrálo 301 událostí (panel Agregát). 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.

Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

Jazyk
Varianta
# Úkol pro coding agenta: Snapshot

Stack: Java. Režim: s vedením.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Konkrétní úkol

Technická specifika (Java): Snapshot jako JSON sloupec (Jackson), verzi kódu drž v konstantě agregátu.

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: snapshot vznikne po zápisu, když od posledního přibylo aspoň N událostí (N v konfiguraci), a kde je uložený. Použij ho pro příkazy; čtení, které potřebuje celou historii, nech beze změny.
2. Ulož snapshot (streamId, version, stateJson, aggregateCodeVersion).
3. Načítání: použij snapshot jen když sedí aggregateCodeVersion, jinak plný replay.
4. Property test: pro náhodné sekvence příkazů a malé N (1–8) je stav i verze ze snapshotu rovny plnému přehrání; ověř, že se snapshoty použily (stačí generátor se seedem).
5. Změř počet přehraných událostí před a po, pro běžný výpočet i pro dlouhý stream (např. 100 událostí).

Na co si dát pozor:
- Snapshot nikdy nenahrazuje stream: po snapshotu se ze streamu nic nemaže ani nezkracuje.
- Serializace stavu musí přežít přidání pole do agregátu (výchozí hodnota); mění-li se výsledek `apply` pro staré události, zvyš verzi kódu.

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

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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: Snapshot

Stack: Java. Režim: challenge.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Zadání (Challenge)

Zaveď snapshoty agregátu kalkulačky tak, aby výsledek byl vždy shodný s plným přehráním, a změř úsporu.

Technická specifika (Java): Snapshot jako JSON sloupec (Jackson), verzi kódu drž v konstantě agregátu.

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ž

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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: Snapshot

Stack: C#. Režim: s vedením.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Konkrétní úkol

Technická specifika (C#): Snapshot přes `System.Text.Json`, verzi kódu jako konstanta typu agregátu.

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: snapshot vznikne po zápisu, když od posledního přibylo aspoň N událostí (N v konfiguraci), a kde je uložený. Použij ho pro příkazy; čtení, které potřebuje celou historii, nech beze změny.
2. Ulož snapshot (streamId, version, stateJson, aggregateCodeVersion).
3. Načítání: použij snapshot jen když sedí aggregateCodeVersion, jinak plný replay.
4. Property test: pro náhodné sekvence příkazů a malé N (1–8) je stav i verze ze snapshotu rovny plnému přehrání; ověř, že se snapshoty použily (stačí generátor se seedem).
5. Změř počet přehraných událostí před a po, pro běžný výpočet i pro dlouhý stream (např. 100 událostí).

Na co si dát pozor:
- Snapshot nikdy nenahrazuje stream: po snapshotu se ze streamu nic nemaže ani nezkracuje.
- Serializace stavu musí přežít přidání pole do agregátu (výchozí hodnota); mění-li se výsledek `apply` pro staré události, zvyš verzi kódu.

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

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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: Snapshot

Stack: C#. Režim: challenge.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Zadání (Challenge)

Zaveď snapshoty agregátu kalkulačky tak, aby výsledek byl vždy shodný s plným přehráním, a změř úsporu.

Technická specifika (C#): Snapshot přes `System.Text.Json`, verzi kódu jako konstanta typu agregátu.

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ž

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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: Snapshot

Stack: Web (HTML/JS nebo stack aplikace). Režim: s vedením.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Konkrétní úkol

Technická specifika (Web (HTML/JS nebo stack aplikace)): Snapshot vedle event store (na backendu tabulka, bez backendu IndexedDB object store `snapshots`), klíč streamId. Event store musí umět číst stream od verze; když neumí, doplň to.

Postupuj po krocích. Po bodu 1 (plán) napiš, co zjistíš a co navrhuješ, a počkej na potvrzení:
1. Plán: snapshot vznikne po zápisu, když od posledního přibylo aspoň N událostí (N v konfiguraci), a kde je uložený. Použij ho pro příkazy; čtení, které potřebuje celou historii, nech beze změny.
2. Ulož snapshot (streamId, version, stateJson, aggregateCodeVersion).
3. Načítání: použij snapshot jen když sedí aggregateCodeVersion, jinak plný replay.
4. Property test: pro náhodné sekvence příkazů a malé N (1–8) je stav i verze ze snapshotu rovny plnému přehrání; ověř, že se snapshoty použily (stačí generátor se seedem).
5. Změř počet přehraných událostí před a po, pro běžný výpočet i pro dlouhý stream (např. 100 událostí).

Na co si dát pozor:
- Snapshot nikdy nenahrazuje stream: po snapshotu se ze streamu nic nemaže ani nezkracuje.
- Serializace stavu musí přežít přidání pole do agregátu (výchozí hodnota); mění-li se výsledek `apply` pro staré události, zvyš verzi kódu.

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

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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: Snapshot

Stack: Web (HTML/JS nebo stack aplikace). Režim: challenge.
Cíl: Přidat snapshoty agregátu a ověřit, že stav ze snapshotu + novějších událostí je shodný s plným přehráním.

## 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 výpočtů (Event Sourcing) a agregát načítaný přehráním streamu. Pokud ne, nejdřív založ minimální: události jen přidávané na konec a `load(streamId)` jako fold přes čistou `apply`.

Nejdřív čti, nic neměň. Najdi a stručně shrň:
- Kód načítání agregátu (fold), kdo ho volá a co z výsledku používá, a typickou i nejdelší délku streamů v datech.
- Verzování kódu agregátu (jak poznáš, že se změnil apply?). Pokud chybí, navrhni konstantu verze a test nad kanonickou historií, který při změně výsledku `apply` spadne.
- Které z těchto částí už v projektu existují a které chybí. Než napíšeš vlastní infrastrukturu (úložiště snapshotů), ověř, jestli ji projekt nebo organizace nemá jako hotovou knihovnu, a použij ji.

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

Snapshot je uložený stav agregátu v konkrétní verzi streamu: načtení vezme snapshot a přehraje jen novější události. Snapshot je cache, ne zdroj pravdy; po změně kódu agregátu se ignoruje a stream se přehraje celý. Příklad: po 300 změnách přehraje načtení bez snapshotu 301 událostí, se snapshotem každých 10 událostí nejvýš 9.

## Zadání (Challenge)

Zaveď snapshoty agregátu kalkulačky tak, aby výsledek byl vždy shodný s plným přehráním, a změř úsporu.

Technická specifika (Web (HTML/JS nebo stack aplikace)): Snapshot vedle event store (na backendu tabulka, bez backendu IndexedDB object store `snapshots`), klíč streamId. Event store musí umět číst stream od verze; když neumí, doplň to.

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ž

- Test shody snapshot vs. plný replay.
- Neplatný snapshot (jiná verze kódu) se ignoruje; změna výsledku `apply` bez zvýšení verze shodí test.
- Úsporu dokládá měření počtu přehraných událostí.
- 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í