Model Event Sourcing
Namísto toho, abyste uchovávali jen aktuální stav dat, můžete pro záznam úplné posloupnosti akcí prováděných u dat využívat úložiště, které nabízí jen možnost připojovat. Toto úložiště funguje jako záznamový systém a je možné ho použít pro materializaci doménových objektů. To může zjednodušit úlohy ve složitých doménách díky tomu, že odpadá nutnost synchronizovat datový model a firemní doménu a současně se zvýší výkon, škálovatelnost a odezva. Může se tím také zajistit konzistence transakčních dat a je možné uchovávat úplné záznamy pro audit a historii, což umožní provádět kompenzační akce.
Kontext a problém
Většina aplikací pracuje s daty a typický přístup je, že aplikace udržuje aktuální stav dat tím, že je průběžně aktualizuje, jak je uživatelé mění. Například v tradičním modelu CRUD (z anglických slov create, read, update, delete – vytvoření, čtení, aktualizace, odstranění) spočívá typický proces v tom, že se data z úložiště přečtou, provedou se nějaké změny a současný stav dat se aktualizuje novými hodnotami – často s využitím transakcí, které data uzamykají.
Metoda CRUD má určitá omezení:
Systémy CRUD provádějí aktualizační operace přímo v úložišti dat, což může zpomalit výkon a rychlost odezvy a omezit škálovatelnost kvůli nárokům na nutné zpracování.
V doméně pro spolupráci s mnoha souběžnými uživateli je vyšší pravděpodobnost konfliktů při aktualizacích dat, protože aktualizační operace můžou probíhat u stejné datové položky.
Pokud není k dispozici další auditovací mechanismus, který by zaznamenával podrobnosti o jednotlivých operacích v samostatném protokolu, bude historie ztracena.
Řešení
Model Event Sourcing definuje metodu zpracovávání datových operací, která vychází z posloupnosti událostí – přičemž každá z nich je zaznamenána v úložišti, které nabízí jen možnost připojovat. Aplikační kód odesílá řadu událostí, které imperativně popisují každou akci, k níž u dat dojde, do úložiště událostí, kde se trvale uloží. Každá událost představuje sadu změn dat (například AddedItemToOrder).
Události se uchovávají v úložišti událostí, které funguje jako záznamový systém (autoritativní zdroj dat) aktuálního stavu dat. Úložiště událostí obvykle tyto události publikuje, aby příjemci měli potřebné informace a mohli je v případě potřeby zpracovat. Příjemci můžou například spouštět úlohy, které operace v událostech aplikují v jiných systémech nebo provedou jakoukoli související akci, která je nutná pro dokončení operace. Všimněte si, že aplikační kód, který generuje události, je oddělený od systémů, které události odebírají.
Typické použití událostí, které publikuje úložiště událostí, spočívá v udržování materializovaných zobrazení entit, jak je mění akce v aplikaci, a v jejich využití pro integraci s externími systémy. Systém například může udržovat materializované zobrazení všech objednávek zákazníků, které slouží k dosazování hodnot do částí uživatelského rozhraní. Jak aplikace přidává nové objednávky, přidává nebo odebírá položky v objednávce a přidává informace o expedici, můžou se události, které tyto změny popisují, zpracovávat a používat k aktualizaci materializovaného zobrazení.
Kromě toho si můžou aplikace kdykoli přečíst historii událostí a materializovat na jejím základě aktuální stav entity, a to přehráním a přijetím všech událostí souvisejících s touto entitou. Toto může proběhnout na vyžádání s cílem materializovat objekt domény při zpracovávání konkrétní žádosti nebo prostřednictvím naplánované úlohy, aby bylo možné stav entity uložit jako materializované zobrazení pro podporu prezentační vrstvy.
Na obrázku vidíme schéma tohoto modelu, včetně některých možností pro použití datového proudu událostí, jako je například vytvoření materializovaného zobrazení, integrace událostí s externími aplikacemi a systémy a přehrávání událostí za účelem vytváření projekcí aktuálního stavu konkrétních entit.

Model Event Sourcing má následující výhody:
Události jsou neměnné a je možné je uložit pomocí operace spočívající v pouhém připojení. Uživatelské rozhraní, pracovní postup nebo proces, jenž událost spustil, může pokračovat a úlohy, které zpracovávají události, můžou běžet na pozadí. To v kombinaci se skutečností, že během zpracování transakcí nedochází ke konfliktům, může významně zlepšit výkon a škálovatelnost aplikací, zejména pro prezentační úroveň nebo uživatelské rozhraní.
Události jsou jednoduché objekty, které popisují určitou akci, k níž došlo, a zahrnují všechna související data nutná k popisu akce představované událostí. Události neaktualizují úložiště dat přímo. Jednoduše se zaznamenají, aby byly zpracovány v příslušnou dobu. To může zjednodušit implementaci a správu.
Expertovi na doménu budou události obvykle dávat smysl, ale neshoda v oblasti objektově-relační impedance může zhoršit srozumitelnost u složitých databázových tabulek. Tabulky jsou umělé konstrukce, které představují aktuální stav systému, nikoli události, k nimž došlo.
Model Event Sourcing může pomoct předcházet konfliktům vyplývajícím ze souběžných aktualizací, protože se eliminuje nutnost přímo aktualizovat objekty v úložišti dat. I přesto ale musí být model domény navržen tak, aby se chránil před žádostmi, které by mohly vést k nekonzistentnímu stavu.
Úložiště jen s možností připojovat poskytuje záznam pro audit, který může sloužit k monitorování akcí prováděných u úložiště dat, k opakovanému generování aktuálního stavu jako materializovaných zobrazení nebo projekcí (a to kdykoli přehráním příslušných událostí) a k usnadnění testování a ladění systému. Kromě toho nutnost používat kompenzační události pro rušení změn vytváří historii změn, které byly vráceny zpět, což by neplatilo, pokud by model jednoduše uchovával jen aktuální stav. Seznam událostí může také sloužit k analýze výkonu aplikace a zjišťování trendů v chování uživatelů nebo k získání dalších užitečných obchodních informací.
Úložiště událostí vytváří události a úlohy v reakci na tyto události provádějí operace. Toto oddělení úloh od události zajišťuje flexibilitu a rozšiřitelnost. Úlohy znají typ události a data události, ale neví o operaci, která událost spustila. Kromě toho může každou událost zpracovávat více úloh. To umožňuje snadnou integraci s dalšími službami a systémy, které naslouchají jen novým událostem vytvořeným v úložišti událostí. Ale události v modelu Event Sourcing jsou často na velmi nízké úrovni a možná bude nezbytné generovat místo nich specifické integrační události.
Model Event Sourcing se často kombinuje s modelem CQRS, takže se v reakci na události provádějí úlohy správy dat a z uložených událostí se materializují zobrazení.
Problémy a důležité informace
Když se budete rozhodovat, jak tento model implementovat, měli byste vzít v úvahu následující skutečnosti:
Vzhledem k vytváření materializovaných zobrazení nebo generování projekcí dat přehráváním událostí se systém vyznačuje konzistencí s prodlevou (eventual consistency). Mezi přidáním událostí do úložiště událostí ze strany aplikace jako výsledků zpracování žádostí, publikováním událostí a zpracováním událostí příjemci je určitá prodleva. Během této doby se v úložišti událostí mohou objevit nové události, které popisují další změny entity.
Poznámka
Informace o konzistenci s prodlevou najdete v tématu Úvod do konzistence dat.
Úložiště událostí je trvalý zdroj informací, takže data událostí by se nikdy neměla aktualizovat. Jediný způsob, jak aktualizovat entitu, aby se změna zrušila, je přidat do úložiště událostí kompenzační událost. Pokud je u trvale uložených událostí potřeba změnit formát (nikoli data), třeba při migraci, může být obtížné kombinovat existující události v úložišti s novou verzí. Možná bude nutné iterovat všechny tyto události provedením příslušných změn, aby byly kompatibilní s novým formátem, nebo přidat nové události, které používají nový formát. Zvažte použití razítka verze v každé verzi schématu událostí, aby se zachoval starý i nový formát událostí.
Do úložiště událostí můžou události ukládat vícevláknové aplikace a více instancí aplikací. Konzistence událostí v úložišti událostí je klíčová, stejně jako pořadí událostí, které se týkají konkrétní entity (pořadí, v jakém u entity ke změnám dochází, ovlivňuje její aktuální stav). Přidání časového razítka ke každé události může pomoct předcházet problémům. Dalším běžným postupem je opatřit každou událost vyplývající z žádosti přírůstkovým identifikátorem. Pokud se dvě akce současně pokusí o přidání událostí pro stejnou entitu, úložiště událostí může zamítnout událost, která odpovídá existujícímu identifikátoru entity a identifikátoru události.
Neexistuje žádná standardní metoda nebo mechanismy, jako jsou dotazy SQL, pro čtení událostí s cílem získat informace. Jediná data, která je možné extrahovat, je datový proud událostí, a to použitím identifikátoru události jako kritéria. ID události se obvykle mapuje k jednotlivým entitám. Aktuální stav entity se dá určit jedině přehráním všech událostí, které se k ní vztahují, vůči jejímu původnímu stavu.
Délka každého datového proudu událostí ovlivňuje správu a aktualizaci systému. Pokud jsou datové proudy velké, zvažte vytváření snímků v určitých intervalech, například po určitém počtu událostí. Aktuální stav entity je možné získat ze snímku a po přehrání všech událostí, které nastaly po tomto bodu v čase. Další informace o vytváření snímků dat najdete v tématu replikace snímků s primárním podřízeným.
I když Event Sourcing minimalizuje možnost konfliktních aktualizací dat, přesto musí být aplikace schopna řešit nekonzistence, které způsobuje konzistence s prodlevou (eventual consistency) a absence transakcí. Do úložiště dat může například přijít událost, která indikuje snížení zásob, zatímco probíhá zadání objednávky na danou položku. Výsledkem je nutnost tyto dvě operace uvést do souladu, a to buď upozorněním zákazníka nebo vytvořením předobjednávky.
Publikace události může být alespoň jednou, a proto musí být příjemci událostí idempotentní. Aktualizaci popsanou v události nesmí aplikovat opakovaně, pokud se událost zpracovává více než jednou. Pokud například více instancí uživatele udržuje a agreguje vlastnost entity, například celkový počet podaných objednávek, musí při zvyšování agregace při výskytu události dojít k úspěšnému provedení pouze jedné instance. Ačkoli nejde o klíčovou vlastnost modelu Event Sourcing, je to obvyklé rozhodnutí při implementaci.
Kdy se má tento model použít
Tento model se používá v následujících scénářích:
Pokud chcete v datech zaznamenat záměr, účel nebo důvod. Například změny entity zákazníka se můžou zachytit jako řada konkrétních typů událostí, jako je například přesunutí domů, uzavřený účet nebo ukončení.
Když je důležité minimalizovat nebo zcela vyloučit výskyt konfliktních aktualizací dat.
Když chcete zaznamenávat události, ke kterým dochází, a mít možnost přehrát je, aby se obnovil stav systému nebo se vrátily provedené změny a aby se uchovávala historie a záznam pro audit. Pokud například úloha zahrnuje několik kroků, možná budete muset provést akce, aby se aktualizace vrátily, a pak znovu přehrát některé kroky, aby se data uvedla do konzistentního stavu.
Pokud je používání událostí přirozenou součástí operací aplikace a vyžaduje jen trochu dodatečného úsilí v oblasti vývoje nebo implementace.
Když potřebujete oddělit proces vkládání a aktualizací dat od úloh, u kterých se vyžaduje použití těchto akcí. To může vést ke zlepšení výkonu uživatelského rozhraní nebo k distribuci událostí do jiných naslouchacích procesů, když k těmto událostem dojde. Například může jít o integraci výplatního systému s webem pro vykazování výdajů, aby události vytvořené úložištěm událostí v reakci na aktualizace dat provedené na webu přijímal web i výplatní systém.
Když požadujete flexibilitu, pokud jde o možnost změny formátu materializovaných modelů a dat entit v případě změny požadavků, nebo v případě, že potřebujete – při použití ve spojení s CQRS – adaptovat model pro čtení nebo zobrazení, přes která se data zveřejňují.
Při použití ve spojení s CQRS a za předpokladu, že je konzistence s prodlevou (eventual consistency) přijatelná v případě aktualizace modelu čtení nebo když je dopad rehydratace entit a dat z datového proudu událostí na výkon přijatelný.
Tento model nemusí být vhodný v následujících situacích:
Malé nebo jednoduché domény, systémy s minimální nebo vůbec žádnou obchodní logikou nebo jiné nedoménové systémy, které přirozeně fungují dobře s tradičními mechanismy správy dat CRUD.
Systémy, kde je vyžadována konzistence a aktualizace zobrazení dat v reálném čase.
Systémy, kde nejsou vyžadovány záznamy pro audit, historie a možnosti pro vrácení a opakované přehrání akcí.
Systémy, ve kterých je jen velmi nízký výskyt konfliktních aktualizací v základních datech. Například systémy, ve kterých se data převážně přidávají a neprovádějí se tolik jejich aktualizace.
Příklad
Systém správy konferencí potřebuje sledovat počet dokončených rezervací míst na konferenci, aby když se potenciální účastník pokusí vytvořit rezervaci, mohl zkontrolovat, jestli jsou stále k dispozici volná místa. Systém může celkový počet rezervací u konference uchovávat minimálně dvěma způsoby:
Systém může uchovávat informace o celkovém počtu rezervací jako samostatnou entitu v databázi, která obsahuje informace o rezervacích. Jak dochází k zadávání a rušení rezervací, může systém toto číslo podle potřeby zvyšovat nebo snižovat. Tato metoda je teoreticky jednoduchá, ale může způsobit problémy se škálovatelností, pokud se o rezervaci pokusí velký počet účastníků během krátké doby. Například poslední den před uzavřením období pro rezervace.
Systém může informace o rezervacích a zrušeních uchovávat jako události v samostatném úložišti událostí. Pak může počet volných míst vypočítat přehráním těchto událostí. Tato metoda může nabízet větší škálovatelnost vzhledem k neměnnosti událostí. Systém potřebuje jen schopnost číst data z úložiště událostí nebo do něho připojovat data. Informace o událostech představujících jednotlivé rezervace a zrušení se nikdy nemění.
Následující diagram znázorňuje, jak je možné subsystém pro rezervaci míst v systému správy konferencí implementovat na základě modelu Event Sourcing.

Posloupnost akcí pro rezervaci dvou míst je následující:
Uživatelské rozhraní vydá příkaz k rezervaci míst pro dva účastníky. Příkaz je zpracován samostatnou obslužnou rutinou příkazu. Jde o logický zápis, který je oddělený od uživatelského rozhraní a zodpovídá za zpracování žádostí zadaných jako příkazy.
Sestaví se agregace obsahující informace o všech rezervacích pro danou konferenci, a to provedením dotazu na události, které popisují rezervace a zrušení. Tato agregace se nazývá
SeatAvailabilitya je obsažená v rámci modelu domény, který poskytuje metody pro dotazování a úpravy dat v agregaci.Mezi optimalizace, které je dobré zvážit, patří snímky (abyste se nemuseli dotazovat na úplný seznam událostí a přehrávat ho s cílem získat aktuální stav agregace) a ukládání kopie agregace do mezipaměti.
Obslužná rutina příkazu vyvolá metodu poskytovanou modelem domény pro vytváření rezervací.
Agregace
SeatAvailabilityzaznamená událost obsahující počet míst, které byly rezervovány. Až agregace příště aplikuje události, použijí se při výpočtu, kolik volných míst zbývá, všechny rezervace.Systém připojí novou událost k seznamu událostí v úložišti událostí.
Pokud uživatel rezervaci místa zruší, systém postupuje podobným způsobem až na to, že obslužná rutina vydá příkaz, který vygeneruje událost zrušení rezervace a připojí ji k úložišti událostí.
Kromě zajištění většího prostoru pro škálovatelnost poskytuje úložiště událostí také úplnou historii, neboli záznam pro audit, všech rezervací a zrušení u konference. Události v úložišti události představují přesný záznam. Agregace není nutné trvale uchovávat žádným jiným způsobem, protože systém může události snadno přehrát a stav kdykoli obnovit do libovolného bodu v čase.
Další informace o tomto příkladu najdete v tématu Seznámení s modelem Event Sourcing.
Další kroky
Blog pro Martin Fowlera:
Související informace
Při implementaci tohoto modelu můžou být relevantní také následující modely a pokyny:
CQRS (Command and Query Responsibility segregation) (CQRS) vzor. Zápisové úložiště, které poskytuje trvalý zdroj informací pro implementaci CQRS, často vychází z implementace modelu Event Sourcing. Popisuje, jak oddělit operace, které načítají data v aplikaci, od operací, které aktualizují data, a to s využitím samostatných rozhraní.
Model materializované zobrazení. Datové úložiště používané v systému založeném na modelu Event Sourcing není obvykle vhodné pro efektivní dotazování. Místo toho se obvykle postupuje tak, že se v pravidelných intervalech nebo při změně dat generují předem vyplněná zobrazení dat. Ukazuje, jak to lze provést.
Vzor kompenzační transakce. Stávající data v úložišti na bázi modelu Event Sourcing se neaktualizují – místo toho jsou přidávány nové položky, které mění stav entit na nové hodnoty. Pokud je třeba změnu vrátit, použijí se kompenzační položky, protože předchozí změnu není možné jednoduše vrátit. Popisuje, jak vrátit akci provedenou pomocí předchozí operace.
Úvod do konzistence dat Při použití modelu Event Sourcing se samostatným úložištěm pro čtení nebo materializovanými zobrazeními nebudou data ke čtení konzistentní okamžitě, ale až s prodlevou (eventual consistency). Shrnuje problémy kolem údržby konzistence napříč distribuovanými daty.
Pokyny k dělení dat. Při použití modelu Event Sourcing se data často dělí s cílem vylepšit škálovatelnost, omezit kolize a optimalizovat výkon Popisuje, jak data rozdělit do menších oddílů a jaké problémy se můžou objevit.