Slovník pojmů
Event Sourcing
Systém nepřepisuje jen poslední stav. Ukládá, co se v doméně stalo, v jakém pořadí, a současnost dokáže z této historie znovu odvodit.
Stručná definice
Zdrojem pravdy je event stream, nikoli pouze poslední řádek se stavem.
Místo aktualizace objednávky z draft přímo na shipped aplikace připojí neměnné události OrderCreated, ItemAdded, PaymentConfirmed a OrderShipped. Každá popisuje již nastalou doménovou skutečnost. Přehráním událostí v pořadí vznikne současný stav konkrétní objednávky.
Event Sourcing mění model zápisu i provozní postupy. Historické události jsou dlouhodobý kontrakt, projekce mohou být dočasně opožděné a běžné dotazy se obvykle neprovádějí opakovaným přehráním všech streamů. Proto je vhodný jen tam, kde hodnota historie, doménového záměru nebo obnovitelných pohledů převáží nad touto složitostí.
Jaký problém řeší
Pouhý aktuální stav neříká, jak a proč systém k výsledku dospěl.
U procesů s významnou historií může být změna sama důležitější než poslední hodnota. Event Sourcing zachovává pořadí rozhodnutí a dovoluje z něj odvozovat více pohledů.
- rekonstrukce stavu objednávky nebo jiného agregátu k současnému okamžiku
- dohledání posloupnosti významných stavových změn bez vedlejšího auditního mechanismu
- sestavení nové projekce z již uložené historie po změně čtecích požadavků
- explicitní zachycení záměru, například ItemAdded místo neurčitého QuantityChanged
- optimistická ochrana před souběžným zápisem do stejného streamu
- časové analýzy a doménové scénáře, pro které samotný aktuální stav nestačí
Praktický příklad
Objednávka rekonstruovaná ze čtyř událostí
Stream Order-8421 začne událostí OrderCreated. ItemAdded přidá položku a cenu, PaymentConfirmed označí zaplacenou objednávku a OrderShipped uloží předání dopravci. Když aplikace potřebuje rozhodnout o další změně, načte události tohoto streamu v pořadí a zavolá na novém objektu jejich apply metody. Výsledkem je objednávka se správnými položkami, platbou a stavem shipped.
Pro seznam objednávek není účelné rekonstruovat každý agregát při každém HTTP požadavku. Projekční handler proto z událostí průběžně udržuje OrderList read model. Jeho krátké zpoždění je konkrétním příkladem eventual consistency: autoritativní historie zůstává v event store a projekci lze dohnat nebo znovu vytvořit. Nový zápis se připojí pouze při shodě očekávané verze streamu.
Textový diagram a jeho alternativa
event stream: Order-8421
0 OrderCreated
1 ItemAdded
2 PaymentConfirmed
3 OrderShipped
│
├── replay ──→ Order (state: shipped)
└── projection ─→ OrderList read model
append: aggregate ID + expected stream version
Jak funguje
Události se připojují do streamu a znovu aplikují ve stejném pořadí.
Textová alternativa diagramu: command načte event stream podle aggregate ID → události rekonstruují agregát → agregát ověří pravidlo a vytvoří nové události → event store je připojí s očekávanou verzí → projekční handlery aktualizují read model.
- Načtení streamu Repository vyžádá události podle aggregate ID, například Order-8421. Stream obsahuje jejich jednoznačné pořadí a průběžnou verzi.
- Rekonstrukce stavu Nová instance agregátu postupně aplikuje OrderCreated, ItemAdded a další události. Apply mění stav v paměti, ale při replayi nevytváří znovu e-maily ani platby.
- Rozhodnutí a nové události Command zavolá doménovou operaci. Agregát ověří invarianty podle rekonstruovaného stavu a pokud změnu přijme, vytvoří jednu či více nových událostí.
- Optimistic concurrency Event store připojí události jen k očekávané verzi. Pokud mezitím jiný proces stream změnil, zápis odmítne a aplikace musí stav znovu načíst a rozhodnutí přehodnotit.
- Projekce Handlery převádějí události do read modelů pro seznamy, reporting nebo vyhledávání. Asynchronní projekce může krátce zaostávat, aniž by změnila autoritativní stream.
Hlavní části a principy
Správnost závisí na identitě streamu, pořadí a dlouhodobě čitelných událostech.
Event store musí umožnit bezpečně načíst a doplnit historii jednoho agregátu. Okolní distribuce událostí je samostatná odpovědnost.
Událost a event stream
Událost je neměnný záznam skutečnosti v minulém čase a obsahuje data potřebná k jejímu pozdějšímu použití. Stream seskupuje události jednoho agregátu a určuje jejich pořadí.
Aggregate ID a verze
Aggregate ID vybere historii konkrétní objednávky. Verze nebo pořadové číslo události chrání pořadí a slouží jako expected version při optimistickém souběhu; samotný timestamp obvykle nestačí.
Event store není message broker
Event store je databáze autoritativních streamů s načtením podle agregátu a podmíněným appendem. RabbitMQ nebo jiný broker distribuuje zprávy příjemcům. Event Sourcing neznamená, že se každá interní událost musí automaticky publikovat do brokeru.
Doménová a integrační událost
Doménová událost zachycuje fakt uvnitř modelu a může obsahovat detaily nutné pro jeho rekonstrukci. Integrační událost je veřejnější, stabilní zpráva pro jiný systém; často se vytvoří z doménové změny, ale nemusí být její kopií jedna ku jedné.
Projekce a read model
Projekce převádí streamy na strukturu vhodnou pro konkrétní dotaz. Často tvoří čtecí stranu CQRS, ale Event Sourcing a CQRS nejsou synonyma ani vzájemná povinnost.
Snapshot
Snapshot je výkonnostní optimalizace dlouhého streamu: uloží stav k určité verzi a při načtení se přehrají jen novější události. Nenahrazuje event stream ani se z něj nestává nový zdroj pravdy.
Výhody a omezení
Úplná historie otevírá nové možnosti a zároveň se stává dlouhodobým závazkem.
Možné přínosy
- současný stav lze z autoritativní posloupnosti znovu sestavit
- historie obsahuje doménový záměr a podporuje dohledání významných změn
- nové read modely lze vytvořit replayem bez změny původního zápisu
- append a expected version poskytují přirozenou optimistickou ochranu jednoho streamu
- doménovou logiku lze testovat stylem given události – when command – then nové události
Omezení a časté chyby
- historické události se obtížně opravují, mažou a přizpůsobují novému schématu
- projekce, jejich checkpointy, deduplikace a obnova zvětšují provozní i testovací plochu
- dlouhé streamy mohou zpomalit rekonstrukci a vyžádat si měřené použití snapshotů
- citlivé nebo osobní údaje v neměnných událostech komplikují retenci a právo na výmaz
- špatně pojmenované události jako EntityUpdated redukují historii na technický change log
- pořadí a konflikty přes více agregátů nevyřeší optimistic concurrency jednoho streamu
Odlišení a vhodnost
Event Sourcing není auditní tabulka ani univerzální náhrada CRUD.
Audit log je obvykle vedlejší záznam změn systému, jehož ztráta nemusí zabránit načtení aktuálních dat. V Event Sourcingu je event stream samotný autoritativní model zápisu a bez něj stav správně nezrekonstruujeme. Replay záměrně znovu aplikuje historické události na stav nebo projekci; běžný retry opakuje neúspěšnou operaci a musí řešit její idempotenci.
Event Sourcing se hodí pro domény s hodnotnou historií, složitými stavovými přechody a potřebou nových časových pohledů. Běžný katalog, jednoduché profily nebo referenční data bývají přehlednější jako aktuální stav v relační databázi. Pokud tým nepotřebuje historii jako zdroj pravdy a neumí provozovat projekce a evoluci událostí, jsou jeho náklady pravděpodobně vyšší než přínos.
Evoluce a provoz
Staré události musí zůstat srozumitelné novému kódu.
Schéma události je dlouhodobý kontrakt. Přidání pole, přejmenování typu nebo oprava významu se musí ověřit i proti celé uložené historii.
- navrhovat události podle doménové skutečnosti a uložit data potřebná pro budoucí replay
- verzovat event envelope nebo typ a podporovat tolerantní čtení starších verzí
- při změně schématu preferovat nové verze nebo upcaster při čtení před přepisem historie
- ukládat checkpoint projekce a zpracovávat opakované doručení idempotentně
- měřit délku streamu a zavést snapshot až při prokázaném problému s rekonstrukcí
- oddělit replay projekce od vedlejších účinků, aby znovu neodesílal platby nebo e-maily
- testovat optimistic concurrency, pořadí, obnovu projekcí i skutečnou evoluci starých událostí
Časté otázky
Event Sourcing v praxi
Je Event Sourcing jen podrobný audit log?
Ne. Audit log je obvykle vedlejší stopa. U Event Sourcingu je stream událostí autoritativním zdrojem, ze kterého se rekonstruuje stav; auditovatelnost je jeden z jeho důsledků.
Vyžaduje Event Sourcing CQRS?
Ne jako definici. Event-sourced zápis se s CQRS často kombinuje, protože běžné dotazy obslouží projekce, ale oddělení command a query operací je samostatné rozhodnutí.
Je event store totéž co Kafka nebo RabbitMQ?
Ne. Event store musí spolehlivě uchovat a načíst pořadí událostí konkrétního agregátu a hlídat očekávanou verzi. Broker řeší především distribuci zpráv odběratelům.
Mění snapshot uloženou historii?
Ne. Snapshot pouze zrychlí načtení stavu k určité verzi. Události zůstávají zdrojem pravdy a snapshot lze znovu vytvořit.
Co se stane, když se změní schéma události?
Nový kód musí umět přečíst staré záznamy, například tolerantní deserializací, verzovaným handlerem nebo upcasterem. Přepsání historických událostí je riziková krajní možnost.
Jak přistupuji k architektuře aplikací
Historii volím jako zdroj pravdy jen tam, kde její hodnota ospravedlní dlouhodobý provozní závazek.
U stavových procesů posuzuji význam historie, hranice agregátů, souběh i obnovu projekcí. Složitost má chránit konkrétní pravidla, ne pouze napodobit známý vzor.