Upravit

Sdílet prostřednictvím


Model Event Sourcing

Rezervace

Místo uložení pouze aktuálního stavu dat v relační databázi uložte celou řadu akcí provedených u objektu v úložišti jen pro připojení. Toto úložiště funguje jako záznamový systém a je možné ho použít pro materializaci doménových objektů. Tento přístup může zlepšit výkon, škálovatelnost a auditovatelnost ve složitých systémech.

Důležité

Event Sourcing je složitý model, který prostupuje celou architekturou a zavádí kompromisy k dosažení vyššího výkonu, škálovatelnosti a auditovatelnosti. Jakmile se váš systém stane systémem event sourcing, všechna budoucí rozhodnutí o návrhu jsou omezena skutečností, že se jedná o systém event sourcing. Migrace do nebo ze systému event Sourcing má vysoké náklady. Tento model je nejvhodnější pro systémy, ve kterých jsou nejvyšší požadavky na výkon a škálovatelnost. Složitost, kterou event Sourcing přidává do systému, není pro většinu systémů odůvodněná.

Kontext a problém

Většina aplikací pracuje s daty a typickým přístupem je, aby aplikace ukládala nejnovější stav dat do relační databáze, vkládala nebo aktualizovala data podle potřeby. Například v tradičním modelu vytvoření, čtení, aktualizace a odstranění (CRUD) je typickým procesem dat čtení dat z úložiště, provedení některých úprav a aktualizace aktuálního stavu dat novými hodnotami – často pomocí transakcí, které data zamknou.

Přístup CRUD je pro většinu scénářů jednoduchý a rychlý. V systémech s vysokým zatížením ale tento přístup má několik výzev:

  • Výkon: Při škálování systému se výkon sníží kvůli kolizím prostředků a problémům s uzamčením.

  • Škálovatelnost: Systémy CRUD jsou synchronní a blokují datové operace u aktualizací. To může vést k kritickým bodům a vyšší latenci při zatížení systému.

  • Auditovatelnost: Systémy CRUD ukládají pouze nejnovější stav dat. Pokud neexistuje mechanismus auditování, který zaznamenává podrobnosti o každé operaci v samostatném protokolu, historie se ztratí.

Ř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. Kód aplikace vyvolá události, které imperativní popisují akci prováděnou u objektu. Události se obvykle odesílají do fronty, kde samostatný proces, obslužná rutina události, naslouchá frontě a uchovává události v úložišti událostí. Každá událost představuje logickou změnu objektu, například AddedItemToOrder nebo OrderCanceled.

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. Další obslužné rutiny událostí můžou naslouchat událostem, které mají zájem, a provést odpovídající akci. 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í.

V každém okamžiku je možné, aby aplikace četly historii událostí. Události pak můžete použít k materializaci aktuálního stavu entity tím, že přehrajete a využijete všechny události, které souvisejí s danou entitou. Tento proces může nastat na vyžádání k materializaci objektu domény při zpracování požadavku.

Vzhledem k tomu, že čtení a přehrání událostí je poměrně nákladné, aplikace obvykle implementují materializovaná zobrazení, projekce jen pro čtení úložiště událostí, které jsou optimalizované pro dotazování. Systém může například udržovat materializované zobrazení všech objednávek zákazníků, které se používají k naplnění uživatelského rozhraní. Když aplikace přidá nové objednávky, přidá nebo odebere položky v objednávce nebo přidá informace o expedici, vyvolá se události a obslužná rutina aktualizuje materializované zobrazení.

Obrázek znázorňuje přehled modelu, včetně některých typických implementací se vzorem, včetně použití fronty, úložiště jen pro čtení, integrace událostí s externími aplikacemi a systémy a přehrání událostí za účelem vytvoření projekcí aktuálního stavu konkrétních entit.

Přehled a příklad modelu Event Sourcing

Pracovní postup

Následující popis typického pracovního postupu pro tento vzor:

  1. Prezentační vrstva volá objekt zodpovědný za čtení z úložiště jen pro čtení. Vrácená data se používají k naplnění uživatelského rozhraní.
  2. Prezentační vrstva volá obslužné rutiny příkazů k provádění akcí, jako je vytvoření košíku, nebo přidání položky do košíku.
  3. Obslužná rutina příkazu volá úložiště událostí, aby získala historické události entity. Může například načíst všechny události košíku. Tyto události se přehrají zpět v objektu, aby se před provedením jakékoli akce materializoval aktuální stav entity.
  4. Obchodní logika se spouští a vyvolává se události. Ve většině implementací se události oddělují do fronty nebo tématu, aby se oddělily producenti událostí a příjemci událostí.
  5. Obslužné rutiny událostí naslouchají událostem, které mají zájem, a provádějí příslušnou akci pro tuto obslužnou rutinu. Mezi typické akce obslužné rutiny událostí patří:
    1. Zápis událostí do úložiště událostí
    2. Aktualizace úložiště jen pro čtení optimalizovaného pro dotazy
    3. Integrace s externími systémy

Výhody vzorů

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í. Tento proces v kombinaci se skutečností, že během zpracování transakcí neexistuje kolize, může výrazně zlepšit výkon a škálovatelnost aplikací, zejména pro prezentační vrstvu.

  • Události jsou jednoduché objekty, které popisují určitou akci, ke které došlo, spolu se všemi přidruženými daty, která jsou nutná k popisu akce reprezentované událostí. Události neaktualizují úložiště dat přímo. Jednoduše se zaznamenají, aby byly zpracovány v příslušnou dobu. Použití událostí 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ě událostí pouze s připojením poskytuje záznam auditu, který lze použít k monitorování akcí provedených v úložišti dat. Aktuální stav může kdykoli znovu vygenerovat jako materializovaná zobrazení nebo projekce tím, že události přehraje a může pomoct s testováním a laděním systému. Kromě toho požadavek na použití kompenzačních událostí ke zrušení změn může poskytnout historii změn, které byly obráceny. Tato funkce by nebyla případem, kdy model uložil aktuální stav. Seznam událostí lze také použít k analýze výkonu aplikace a k detekci trendů chování uživatelů. Nebo se dá použít k získání dalších užitečných obchodních informací.

  • Obslužné rutiny příkazů vyvolávají události a úlohy provádějí operace v reakci na tyto události. 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 běžně kombinuje se vzorem CQRS tím, že provádí úlohy správy dat v reakci na události a materializuje zobrazení z uložených událostí.

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:

  • Konečná konzistence – Systém bude nakonec konzistentní pouze při vytváření materializovaných zobrazení nebo generování projekcí dat přehráváním událostí. Mezi aplikací, která přidává události do úložiště událostí v důsledku zpracování požadavku, publikování událostí a příjemců událostí, které s nimi zpracovávají, dochází ke zpoždění. Během této doby se v úložišti událostí mohou objevit nové události, které popisují další změny entity. Vaši zákazníci musí být v pořádku se skutečností, že data jsou nakonec konzistentní a systém by měl být navržen tak, aby v těchto scénářích odpovídal konečné konzistenci.

    Poznámka:

    Informace o konzistenci s prodlevou najdete v tématu Úvod do konzistence dat.

  • Události správy verzí – Úložiště událostí je trvalým zdrojem informací, takže data událostí by se nikdy neměla aktualizovat. Jediným způsobem, jak aktualizovat entitu nebo vrátit zpět změnu, je přidat do úložiště událostí kompenzační událost. Pokud se schéma (místo dat) trvalých událostí musí změnit, třeba během migrace, může být obtížné kombinovat existující události v úložišti s novou verzí. Vaše aplikace bude muset podporovat změny struktur událostí. To lze provést několika způsoby.

    • Zajistěte, aby obslužné rutiny událostí podporovaly všechny verze událostí. To může být výzva k údržbě a testování. To vyžaduje implementaci razítka verze pro každou verzi schématu událostí, aby se zachovaly staré i nové formáty událostí.
    • Implementujte obslužnou rutinu události pro zpracování konkrétních verzí událostí. To může být problém údržby v tom, že změny opravy chyb se můžou provádět v různých obslužných rutinách. To vyžaduje implementaci razítka verze pro každou verzi schématu událostí, aby se zachovaly staré i nové formáty událostí.
    • Při implementaci nového schématu aktualizujte historické události na nové schéma. Tím se přeruší neměnnost událostí.
  • Řazení událostí – Aplikace s více vlákny a více instancí aplikací můžou ukládat události do úložiště událostí. 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.

  • Dotazování událostí – Neexistuje žádný standardní přístup nebo existující mechanismy, jako jsou dotazy SQL, pro čtení událostí pro získání informací. 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.

  • Náklady na opětovné vytvoření stavu entit – 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 naleznete v tématu Replikace primárních podřízených snímků.

  • Konflikty – I když event Sourcing minimalizuje riziko konfliktních aktualizací dat, musí být aplikace stále schopná řešit nekonzistence, které jsou výsledkem konečné konzistence a chybějící transakce. Například událost, která indikuje snížení zásob zásob, může přicházet do úložiště dat, zatímco je objednávka pro danou položku umístěna. Výsledkem této situace je požadavek na odsouhlasení těchto dvou operací, a to buď doporučením zákazníka, nebo vytvořením objednávky zpět.

  • Potřeba idempotentní – Publikování událostí může být alespoň jednou, takže příjemci událostí musí být idempotentní. Aktualizaci popsanou v události nesmí aplikovat opakovaně, pokud se událost zpracovává více než jednou. Více instancí příjemce může udržovat a agregovat vlastnost entity, například celkový počet zadaných objednávek. Pouze jeden musí být úspěšný při zvýšení agregace v případě, že dojde k události umístěné v objednávce. I když tento výsledek není klíčovou charakteristikou zdroje událostí, jedná se o obvyklé rozhodnutí o implementaci.

  • Cyklickou logiku – mějte na paměti scénáře, kdy zpracování jedné události zahrnuje vytvoření jedné nebo více nových událostí, protože to může způsobit nekonečné smyčky.

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 lze zaznamenat jako řadu konkrétních typů událostí, jako je Přesunuto domů, Uzavřený účet nebo Zůsnulý.

  • Když je důležité minimalizovat nebo zcela vyloučit výskyt konfliktních aktualizací dat.

  • Pokud chcete zaznamenávat události, ke kterým dochází, abyste je přehráli za účelem obnovení stavu systému, vrácení změn zpět nebo uchování historie a protokolu auditování. Pokud například úkol zahrnuje více kroků, budete možná muset provést akce, aby se aktualizace vrátily, a pak znovu přehrát některé kroky, aby se data vrátila do konzistentního stavu.

  • Při použití událostí. Je to přirozená funkce provozu aplikace a vyžaduje málo dalšího úsilí o vývoj nebo implementaci.

  • Pokud potřebujete oddělit proces zadávání nebo aktualizovat data od úloh potřebných k použití těchto akcí. Touto změnou může být zvýšení výkonu uživatelského rozhraní nebo distribuce událostí jiným naslouchacím procesům, které při výskytu událostí podniknou akci. Můžete například integrovat mzdový systém s webem pro odeslání výdajů. Události, které jsou vyvolány úložištěm událostí v reakci na aktualizace dat provedených na webu, by byly spotřebovány webem i systémem mzdy.

  • Pokud chcete mít flexibilitu, abyste mohli změnit formát materializovaných modelů a dat entit, pokud se změní požadavky nebo při použití s CQRS, musíte přizpůsobit model čtení nebo zobrazení, která data zveřejňují.

  • Při použití s CQRS a konečná konzistence je přijatelná, když se aktualizuje model čtení, nebo je přijatelný dopad na výkon dosazování entit a dat z datového proudu událostí.

Tento model nemusí být vhodný v následujících situacích:

  • Aplikace, které nevyžadují hyper-škálování nebo výkon.

  • 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 dochází pouze k nízkému výskytu konfliktních aktualizací podkladových dat. Například systémy, ve kterých se data převážně přidávají a neprovádějí se tolik jejich aktualizace.

Návrh úloh

Architekt by měl vyhodnotit způsob použití modelu Event Sourcing v návrhu úlohy k řešení cílů a principů popsaných v pilířích architektury Azure Well-Architected Framework. Příklad:

Pilíř Jak tento model podporuje cíle pilíře
Rozhodnutí o návrhu spolehlivosti pomáhají vaší úloze stát se odolnou proti selhání a zajistit, aby se po selhání obnovila do plně funkčního stavu. Kvůli zaznamenání historie změn v komplexním obchodním procesu může usnadnit obnovení stavu, pokud potřebujete obnovit úložiště stavů.

- RE:06 Dělení dat
- RE:09 Zotavení po havárii
Efektivita výkonu pomáhá vaší úloze efektivně splňovat požadavky prostřednictvím optimalizací škálování, dat a kódu. Tento model, obvykle v kombinaci s CQRS, vhodným návrhem domény a strategickým snímkováním, může zlepšit výkon úloh z důvodu atomických operací jen pro připojení a zabránění uzamčení databáze pro zápisy a čtení.

- Výkon dat PE:08

Stejně jako u jakéhokoli rozhodnutí o návrhu zvažte jakékoli kompromisy proti cílům ostatních pilířů, které by mohly být s tímto vzorem zavedeny.

Příklad

Systém řízení konferencí musí sledovat počet dokončených rezervací pro konferenci. Tímto způsobem může zkontrolovat, jestli jsou stále k dispozici místa, když se potenciální účastník pokusí provést rezervaci. 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.

Zaznamenávání informací o rezervacích míst v systému pro správu konferencí založeném na modelu Event Sourcing

Posloupnost akcí pro rezervaci dvou míst je následující:

  1. 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.

  2. Entita obsahující informace o všech rezervacích konference je vytvořena dotazováním událostí, které popisují rezervace a zrušení. Tato entita se nazývá SeatAvailabilitya je obsažena v doménovém modelu, který zveřejňuje metody pro dotazování a úpravu dat v entitě.

    Některé optimalizace, které je potřeba zvážit, používají snímky (takže nemusíte dotazovat a přehrávat úplný seznam událostí, abyste získali aktuální stav entity) a zachovali kopii entity v mezipaměti v paměti.

  3. Obslužná rutina příkazu vyvolá metodu poskytovanou modelem domény pro vytváření rezervací.

  4. Entita SeatAvailability vyvolá událost obsahující počet rezervovaných licencí. Při příštím použití událostí se všechny rezervace použijí k výpočtu počtu licencí.

  5. 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 rozsahu škálovatelnosti poskytuje úložiště událostí také kompletní historii nebo záznam auditu rezervací a zrušení konference. Události v úložišti události představují přesný záznam. Agregace není nutné uchovávat jiným způsobem, protože systém může události snadno přehrát a obnovit stav k libovolnému bodu v čase.

Další kroky

Při implementaci tohoto modelu můžou být relevantní také následující modely a pokyny:

  • Model dělení zodpovědnosti příkazů a dotazů (CQRS): 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í. Úložiště dat používané v systému založeném na modelu Event Sourcing se obvykle nehodí k efektivnímu 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.

  • Model kompenzační transakce: Existující data v úložišti event sourcing se neaktualizují. Místo toho se přidají nové položky, které přejdou stav entit na nové hodnoty. Pokud chcete změnit změnu, použijí se kompenzační položky, protože předchozí změnu není možné vrátit zpět. Popisuje, jak vrátit akci provedenou pomocí předchozí operace.