Glosář DDD terminologie
1. Strategické vzory DDD
Co jsou strategické vzory?
Strategické vzory DDD se zabývají vysokoúrovňovým rozdělením systému na soudržné celky a řízením vztahů mezi nimi. Tato vrstva návrhu odpovídá na otázku „co a kde" - kde leží hranice modelu, kdo za jakou část domény zodpovídá a jak spolu jednotlivé části komunikují. Strategický design je základem, na němž teprve spočívají taktické stavební bloky. Bez solidního strategického designu se taktický design stává náhodnou sbírkou vzorů bez kontextu.
- Doména (Domain)
-
Sféra znalostí a činností, kolem níž se točí logika aplikace. Doména je „proč" softwaru - problémový prostor, který software modeluje a pomáhá řešit. Eric Evans definuje doménu jako: „A sphere of knowledge, influence, or activity. The subject area to which the user applies a program." Doména není totéž co aplikace samotná - doména existuje i bez softwaru; software je jen jedním z prostředků, jak v doméně pracovat. Pochopení domény je předpokladem smysluplného softwarového návrhu.
Zdroj: Eric Evans, Domain-Driven Design (2003), str. 2–3
- Subdoména (Subdomain)
-
Část celkové domény, vymezená z pohledu problémového prostoru. Subdomény vznikají analýzou domény a rozdělením problémového prostoru na soudržné oblasti. Rozlišujeme tři typy: Klíčová subdoména (Core Domain) - zdroj konkurenční výhody; Podpůrná subdoména (Supporting Subdomain) - nutná pro fungování, ale ne zdrojem výhody; Generická subdoména (Generic Subdomain) - řešitelná hotovými produkty. Subdomény patří do problémového prostoru; Bounded Contexts jsou jejich protějškem v prostoru řešení. Jeden Bounded Context zpravidla implementuje jednu subdoménu, ale ne vždy v poměru 1:1.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 15; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2
- Klíčová doména (Core Domain)
-
Subdoména, která přináší největší konkurenční výhodu a je srdcem celého podnikání. Klíčová doména je oblast, v níž se organizace odlišuje od konkurence a kde leží primární důvod existence softwaru. Měla by dostávat největší investice, nejlepší vývojáře a plnou aplikaci taktických vzorů DDD. Všechny ostatní subdomény jsou periferní - slouží klíčové doméně, ale nejsou jejím cílem. Identifikace klíčové domény je jedním z nejdůležitějších výsledků strategické analýzy.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 15, str. 396–397; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2
- Podpůrná subdoména (Supporting Subdomain)
-
Subdoména nezbytná pro fungování klíčové domény, ale sama o sobě není zdrojem konkurenční výhody. Příklad: sledování objednávek v e-commerce systému, kde klíčovou doménou je doporučovací engine. Podpůrné subdomény se typicky vyvíjejí in-house, ale bez hluboké aplikace DDD - jejich složitost totiž nepřesahuje nutnou míru. Jsou nezbytné, nikoli strategické. Pokud by bylo možné podpůrnou subdoménu nahradit hotovým řešením, je vhodné to zvážit.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2, str. 52–54
- Generická subdoména (Generic Subdomain)
-
Subdoména, jejíž potřeby lze pokrýt existujícím hotovým řešením (off-the-shelf software). Příklady: autentizace a autorizace (Keycloak, Auth0), fakturace (Stripe), odesílání e-mailů (Mailgun, SendGrid). Generické subdomény nenesou žádnou specificitu daného podnikání - stejný problém řeší tisíce organizací. Investice do vývoje vlastního řešení je proto zpravidla plýtvání zdroji. Nejlepší strategie: outsourcing nebo integrace existujícího produktu.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2, str. 54–55
- Všudypřítomný jazyk (Ubiquitous Language)
-
Sdílený, přesný jazyk mezi vývojáři a doménovými experty, který se konzistentně používá v kódu, konverzacích, dokumentaci, testech i diagramech. Slova tohoto jazyka jsou pojmy z domény - ne technické termíny, ne databázový slang. Všudypřítomný jazyk je ohraničen jedním Bounded Contextem: stejné slovo může v různých kontextech znamenat různé věci (například „Zákazník" v kontextu prodeje vs. v kontextu podpory). Jde o nejzákladnější Evansův koncept - bez Ubiquitous Language je DDD jen sbírkou vzorů bez lepidla.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 2, str. 24–33
- Ohraničený kontext (Bounded Context)
-
Explicitní hranice, uvnitř níž je konkrétní doménový model definován a platný. Uvnitř Bounded Contextu je Ubiquitous Language konzistentní a jednoznačná - každý pojem má jeden přesný význam. Různé Bounded Contexty mohou používat stejné slovo pro odlišné koncepty, aniž by si odporovaly. Bounded Context je fundamentální jednotkou návrhu i nasazení v DDD. Určuje hranice týmů, nasazení, databázových schémat i kontraktů API. Nelze ho zaměňovat s modulem nebo namespace - jde o organizační i technický konstrukt.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 335–336; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2
- Mapa kontextů (Context Map)
-
Dokument nebo diagram zobrazující všechny Bounded Contexty v systému a vztahy mezi nimi. Mapa kontextů není primárně technický artefakt - zobrazuje organizační a týmové vztahy, mocenské dynamiky (kdo závisí na kom), kontrakty integrace a použité integrační vzory (Shared Kernel, Customer–Supplier, ACL apod.). Vytvoření mapy kontextů je zpravidla první hmatatelný výstup strategické analýzy DDD. Odráží realitu - ne ideál; zachycuje stav, který existuje, ne ten, který si přejeme.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 343–344
- Sdílené jádro (Shared Kernel)
-
Vzor mapování kontextů: dva Bounded Contexty sdílejí podmnožinu doménového modelu - společné entity, hodnotové objekty nebo doménové události. Změny ve sdíleném jádru musí být odsouhlaseny oběma týmy a testovány v obou kontextech. Sdílené jádro vytváří těsnou provázanost - koordinační náklady jsou nezanedbatelné. Používejte ho jen tehdy, kdy je přínos sdílení prokazatelně vyšší než náklady koordinace. Nejčastěji vhodné pro kontexty spravované jedním týmem.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 354–355
- Zákazník–Dodavatel (Customer–Supplier)
-
Vztah mapování kontextů, v němž downstream kontext (Zákazník) závisí na upstream kontextu (Dodavateli). Dodavatelský tým musí v rozumné míře zohledňovat potřeby zákazníka - existuje formální vztah a vyjednané rozhraní. Zákazník může zadávat požadavky na změny a Dodavatel je povinen je zvažovat. Tento vzor předpokládá, že oba týmy spolu aktivně komunikují a spolupracují. Pokud Dodavatel ignoruje potřeby Zákazníka, vztah degeneruje na Konformistu nebo vyžaduje Anti-korupční vrstvu.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 356–357
- Konformista (Conformist)
-
Vzor mapování kontextů: downstream kontext přebírá model upstream kontextu v plném rozsahu, bez překladu. Používá se tehdy, kdy upstream tým nemá motivaci ani povinnost přizpůsobovat se potřebám downstream, ale downstream shledá upstream model dostatečně přijatelným. Konformista eliminuje potřebu překladové vrstvy, ale za cenu závislosti na cizím modelu. Je opakem Anti-korupční vrstvy. Vhodný například při integraci s autoritativní platformou (interní ERP, firemní identity provider), jejíž model je kvalitní.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 357–358
- Anti-korupční vrstva (Anti-Corruption Layer - ACL)
-
Překladová vrstva, která izoluje Bounded Context od modelů jiných kontextů nebo legacy systémů. ACL překládá pojmy externího modelu do interního Ubiquitous Language, čímž brání „korupci" doménového modelu cizími koncepty. Implementuje se typicky jako sada adaptérů, fasád nebo překladačů na hranici kontextu. Je nezbytná při integraci s špatně navrženými legacy systémy nebo s externími API, která mají odlišnou sémantiku. ACL je jeden z nejdůležitějších obranných mechanismů v DDD.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 364–367
- Otevřený hostitelský servis (Open Host Service)
-
Vzor mapování kontextů: Bounded Context definuje formální protokol (API), prostřednictvím něhož mohou ostatní kontexty integrovat. Protokol je zveřejněn, dokumentován a aktivně udržován. Jiné kontexty se tomuto protokolu přizpůsobí, aniž by potřebovaly přímý přístup k internímu modelu. Open Host Service je vhodný pro kontexty, které slouží většímu počtu spotřebitelů - namísto individuálních překladů pro každého zákazníka se nabídne jeden dobře navržený protokol.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 362–363
- Zveřejněný jazyk (Published Language)
-
Dobře dokumentovaný sdílený jazyk pro komunikaci mezi Bounded Contexty - často průmyslový standard (XML schéma, JSON-LD, HL7 ve zdravotnictví, FIX ve financích). Zveřejněný jazyk se typicky používá v kombinaci s Open Host Service: hostitelský kontext publikuje svůj protokol prostřednictvím standardizovaného jazyka, který mohou jiné kontexty číst bez nutnosti znát interní model. Odděluje komunikační formát od vnitřní reprezentace.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 362–363
- Separate Ways (Separate Ways)
-
Rozhodnutí v mapování kontextů: dva Bounded Contexty nemají žádnou integraci. Každý tým řeší problém nezávisle. Toto je legitimní strategie tehdy, kdy náklady na integraci přesahují její přínos - například pokud sdílená funkcionalita není pro žádný kontext kritická, nebo pokud jsou komunikační bariéry mezi týmy příliš vysoké. Separate Ways je disciplinované rozhodnutí, ne selhání návrhu: uznáváme, že duplikace je v tomto případě levnější než koordinace.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 14, str. 359–360
2. Taktické vzory (stavební bloky DDD)
Co jsou taktické vzory?
Taktické vzory DDD jsou konkrétní objektově-orientované stavební bloky, s jejichž pomocí se doménový model implementuje uvnitř jednoho Bounded Contextu. Taktický design odpovídá na otázku „jak" - jak modelovat entitu, jak vynucovat invarianty, jak uchovávat stav, jak vyjádřit doménové operace. Tyto vzory nejsou samoúčelné - mají smysl pouze tehdy, jsou-li zasazeny do správně definovaného strategického rámce.
- Entita (Entity)
-
Doménový objekt s jedinečnou, přetrvávající identitou, která ho odlišuje i v případě, že se změní všechny jeho atributy. Příklad: uživatel identifikovaný hodnotou
UserId- i když změní jméno, e-mail i adresu, zůstává stejnou entitou. Dvě entity se stejnými daty, ale odlišnými identifikátory, jsou různé entity. Rovnost entit je určena identitou, nikoli stavem. Na rozdíl od Hodnotového objektu musí entita mít explicitní identifikátor, ideálně jako silně typovaný Value Object.Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 5, str. 89–97
- Hodnotový objekt (Value Object)
-
Doménový objekt bez identity - dva hodnotové objekty se stejnými atributy jsou rovnocenné a zaměnitelné. Hodnotové objekty jsou neměnné (immutable): jakákoli mutace vrací novou instanci, nikoliv modifikovanou stávající. Příklady:
Money,Email,Address,DateRange,ProductId. Návrhový princip: preferujte hodnotové objekty před primitivními typy - vyjadřují doménový záměr, centralizují validaci a předcházejí sémantickým chybám (argument confusion). Většina doménových konceptů, které vývojáři modelují jako řetězec nebo číslo, jsou ve skutečnosti hodnotové objekty.Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 5, str. 97–103
- Agregát (Aggregate)
-
Shluk doménových objektů (entit a hodnotových objektů) zacházený jako jediná jednotka pro změny dat. Agregát má právě jeden Kořen agregátu. Vynucuje invarianty uvnitř své hranice - žádná operace nesmí uvést agregát do neplatného stavu. Objekty mimo agregát na něj odkazují výhradně přes identifikátor kořene, nikdy přímou referencí na vnitřní entity. Agregát definuje transakční hranici: v jedné transakci se smí modifikovat maximálně jeden agregát. Správné dimenzování agregátů je jedním z nejtěžších aspektů DDD - příliš velké agregáty vedou k výkonostním problémům, příliš malé ke slabé doménové konzistenci.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 6, str. 125–136; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 10
- Kořen agregátu (Aggregate Root)
-
Jediná entita uvnitř agregátu, která slouží jako vstupní bod pro veškeré vnější interakce. Veškeré invarianty agregátu jsou vynucovány prostřednictvím kořene - žádný vnější objekt nesmí modifikovat vnitřní objekty agregátu přímo, ale vždy jen skrze kořen. Vnější objekty drží reference výhradně na kořen agregátu (nebo jeho identifikátor), nikdy na jeho vnitřní entity. Kořen agregátu je také jediný objekt, který Repozitář přímo ukládá a načítá.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 6, str. 125–128
- Repozitář (Repository)
-
Kolekci podobná abstrakce pro ukládání a načítání agregátů. Repozitář ukrývá detaily persistence před doménou - z pohledu domény jde o kolekci objektů v paměti. Rozhraní (interface) repozitáře patří do doménové vrstvy; jeho implementace (např. Doctrine, Eloquent, in-memory) patří do infrastrukturní vrstvy. Pravidlo: jeden repozitář na jeden typ agregátu. Repozitáře vrací celé agregáty - nenabízejí dotazy na vnitřní entity. Pro čtecí potřeby (CQRS Query side) se používají jiné mechanismy (projekce, čtecí modely).
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 6, str. 147–162
- Továrna (Factory)
-
Zapouzdřuje složitou konstrukční logiku agregátů nebo doménových objektů. Pokud je vytvoření objektu samo o sobě smysluplným doménovým aktem, továrna nebo pojmenovaný konstruktor vyjadřuje tento záměr. Továrna může být statická metoda na agregátu (pojmenovaný konstruktor), samostatná třída, nebo doménová služba. Odděluje zodpovědnost za vytvoření objektu od jeho použití. Neměla by obsahovat doménovou logiku - pouze koordinaci konstrukce.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 6, str. 136–147
- Doménová služba (Domain Service)
-
Bezstavová služba provádějící doménovou operaci, která přirozeně nepatří žádné konkrétní entitě ani hodnotovému objektu. Příklady: služba pro konverzi měn, kalkulace ceny s uplatněním slev ze dvou různých agregátů, koordinace transferu prostředků mezi účty. Doménová služba je součástí doménové vrstvy a mluví Ubiquitous Language. Nelze ji zaměňovat s Aplikační službou - Aplikační služba orchestruje use-case, Doménová služba vyjadřuje doménové pravidlo.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 5, str. 104–110
- Aplikační služba (Application Service)
-
Orchestruje provedení use-casu: načítá agregáty z repozitářů, volá doménové metody, persistuje výsledky a publikuje doménové události. Neobsahuje žádnou doménovou logiku - ta patří do entit, hodnotových objektů a doménových služeb. Aplikační služba je tenká vrstva mezi prezentací a doménou. V architektuře CQRS odpovídají aplikačním službám Command Handlery (pro příkazy) a Query Handlery (pro dotazy). V hexagonální architektuře jsou aplikační služby volány přes driving porty.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 4, str. 70–73; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 14
- Doménová událost (Domain Event)
-
Neměnný záznam o čemkoliv významném, co se v doméně stalo - vyjádřený minulým časem:
UserRegistered,OrderPlaced,PaymentFailed. Doménové události jsou prostředkem volného propojení mezi agregáty i Bounded Contexty. Musí obsahovat všechna data potřebná k rekonstrukci změny stavu - nespoléhají na pozdější dotazování. V Event Sourcingu jsou doménové události primárním způsobem persistence stavu. Název události musí být součástí Ubiquitous Language.Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 8; Eric Evans, Domain-Driven Design Reference (2015)
- Specifikace (Specification)
-
Zapouzdřuje obchodní pravidlo do znovupoužitelného, skládatelného objektu - predikátu, který určuje, zda doménový objekt splňuje určité kritérium. Specifikace lze skládat pomocí logických operátorů AND, OR, NOT a vytvářet tak komplexní podmínky z jednoduchých stavebních bloků. Vzor popsal Eric Evans (2003, kapitola 9) a podrobněji rozpracovali Evans & Fowler ve společném whitepaperu.
Specifikace má tři hlavní způsoby použití: validace - ověření, zda objekt splňuje obchodní pravidlo (splňuje objednávka podmínky k potvrzení?); selekce (dotazování) - výběr objektů z kolekce podle kritéria (vyber zákazníky splňující kritéria věrnostního programu); a konstrukce - specifikace požadavků na nově vytvářený objekt (smí být produkt přidán do košíku?). Centralizují pravidla, která by jinak byla rozptýlena po celé doméně, a umožňují jejich testování v izolaci.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 9, str. 224–237; Evans & Fowler, Specification (whitepaper, 1997)
- Invariant (Invariant)
-
Obchodní pravidlo, které musí vždy platit uvnitř hranice agregátu - nesmí být nikdy porušeno, bez ohledu na to, jakým způsobem je agregát modifikován. Kořen agregátu je zodpovědný za vynucování všech invariantů. Příklad: „Objednávka musí mít alespoň jednu položku, aby mohla být potvrzena." nebo „Celková cena objednávky nesmí být záporná." Invarianty jsou jádrem doménového modelu - definují, co je platný stav. Porušení invariantu musí vyhodit výjimku, nikdy tiše projít.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 6, str. 128–131
- Doménová výjimka (Domain Exception)
-
Vlastní třídy výjimek, které vyjadřují porušení doménových pravidel v terminologii Všudypřítomného jazyka. Namísto generických výjimek (
InvalidArgumentException,RuntimeException) doménový model vyhazuje specifické výjimky pojmenované podle porušeného pravidla:InsufficientFundsException,OrderAlreadyConfirmedException,InvalidEmailFormatException. Doménové výjimky zvyšují čitelnost kódu, usnadňují diagnostiku chyb a umožňují klientskému kódu reagovat na konkrétní typy selhání. Jsou přirozeným mechanismem signalizace porušení invariantů agregátu - pokud agregát nemůže provést požadovanou operaci, protože by tím porušil své obchodní pravidlo, vyhodí doménovou výjimku.Zdroj: Eric Evans, Domain-Driven Design (2003); Vaughn Vernon, Implementing Domain-Driven Design (2013)
3. Architektonické vzory
Co jsou architektonické vzory v kontextu DDD?
Architektonické vzory definují, jak jsou jednotlivé vrstvy a komponenty systému organizovány, jak mezi sebou komunikují a kde leží zodpovědnosti. DDD je architektonicky agnostické - lze ho kombinovat s vrstvenou architekturou, hexagonální architekturou i vertikálními řezy. Volba architektonického vzoru ovlivňuje testovatelnost, udržovatelnost a schopnost doménového modelu zůstat čistý od infrastrukturních závislostí.
- Vrstvená architektura (Layered Architecture)
-
Klasická čtyřvrstvá architektura (Evans 2003): Prezentační vrstva → Aplikační vrstva → Doménová vrstva → Infrastrukturní vrstva. Závislosti plynou striktně jedním směrem - každá vrstva závisí pouze na vrstvě pod ní. V Evansově původním modelu doménová vrstva závisí na infrastrukturní vrstvě; v modernějších variantách (Hexagonální, Onion architektura) se tato závislost invertuje pomocí rozhraní tak, aby doménová vrstva nezávisela na žádné technické vrstvě. Tato architektura je přirozeným výchozím bodem pro DDD a je explicitně popsána Evansem. Nevýhodou je, že organizuje kód podle technické role, nikoli podle byznysové funkce - což může ztěžovat pochopení, co systém dělá.
Zdroj: Eric Evans, Domain-Driven Design (2003), kap. 4, str. 68–76
- Hexagonální architektura (Hexagonal Architecture / Ports and Adapters)
-
Architektura navržená Alistairem Cockburnem: jádro aplikace (doména + aplikační vrstva) komunikuje s vnějším světem výhradně prostřednictvím Portů (rozhraní) a Adaptérů (implementací). Driving adaptéry (HTTP, CLI, konzole) volají aplikaci; driven adaptéry (databáze, messaging, e-mail) jsou aplikací volány. Klíčový přínos: doménový model je zcela izolován od infrastruktury - lze ho testovat bez databáze, HTTP frameworku ani externích systémů. Základní princip inverze závislostí (DIP) je zde aplikován systematicky.
Zdroj: Alistair Cockburn, Hexagonal Architecture (2005), alistair.cockburn.us; Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4
- Port (Port)
-
V Hexagonální architektuře: rozhraní definující, jak aplikace komunikuje s vnějším světem. Driving porty definují, jak vnější aktéři aplikaci volají (například rozhraní command handlerů). Driven porty definují, co aplikace potřebuje od infrastruktury (například
UserRepositoryInterface,EmailSenderInterface). Porty patří do aplikačního jádra - jsou součástí doménové nebo aplikační vrstvy, nikoli infrastruktury.Zdroj: Alistair Cockburn, Hexagonal Architecture (2005)
- Adaptér (Adapter)
-
V Hexagonální architektuře: konkrétní implementace Portu. Adaptér překládá mezi protokolem vnějšího světa a rozhraním aplikačního jádra. Příklady:
DoctrineUserRepositoryimplementuje portUserRepositoryInterface;SymfonyMailerEmailSenderimplementuje portEmailSenderInterface; HTTP controller implementuje driving port tím, že volá command handler. Adaptéry patří do infrastrukturní vrstvy a jsou zaměnitelné bez dotyku doménového modelu.Zdroj: Alistair Cockburn, Hexagonal Architecture (2005)
- Vertikální slice architektura (Vertical Slice Architecture)
-
Organizuje kód podle funkce/use-casu, nikoli podle technické vrstvy. Každý slice (například
UserRegistration) je vertikální řez celým stackem: controller, command, handler, doménová logika, repozitář - vše pohromadě. Snižuje coupling mezi funkcemi - změna v jedné funkci neovlivní ostatní. Liší se od klasického horizontálního vrstvení, kde jsou všechny controllery v jedném místě, všechny repozitáře v druhém atd. Vertikální slices přirozeně odpovídají use-casům a Bounded Contextům.Zdroj: Jimmy Bogard, Vertical Slice Architecture (2018), jimmybogard.com
- CQRS (Command Query Responsibility Segregation)
-
Architektonický vzor oddělující operace čtení (Queries) od operací zápisu (Commands). Každá strana má vlastní model: zápisový model používá bohaté agregáty a vynucuje invarianty; čtecí model používá optimalizované DTO nebo projekce. Zaveden Gregem Youngem jako rozšíření principu CQS Bertranda Meyera na architektonickou úroveň. CQRS je v DDD přirozeným doplňkem: doménový model je příliš bohatý na to, aby sloužil jako přímá projekce pro UI. Oddělení modelů umožňuje každou stranu optimalizovat nezávisle.
Zdroj: Greg Young, CQRS Documents (2010); Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4
- CQS (Command-Query Separation)
-
Princip Bertranda Meyera: metoda je buď Command (mění stav, vrací void), nebo Query (vrací data, nemá žádné vedlejší efekty) - nikdy obojí. Porušení CQS vede ke kódu, který je obtížně testovatelný a jehož chování je nepředvídatelné při vícenásobném volání. CQRS aplikuje tento princip na architektonické úrovni - celé modely jsou odděleny, nejen metody. CQS je podmínkou nutnou pro CQRS.
Zdroj: Bertrand Meyer, Object-Oriented Software Construction (1988), kap. 23
- Event Sourcing (Zdrojování událostí)
-
Persistence vzor, v němž je stav ukládán jako append-only sekvence doménových událostí, nikoli jako aktuální snímek. Aktuální stav agregátu se odvozuje přehráním celého streamu událostí od počátku (nebo od posledního Snapshotu). Přirozený audit log: každá změna je zaznamenaná, žádná není ztracena. Umožňuje temporální dotazy - rekonstrukci stavu v libovolném bodě minulosti. Přirozeně doplňuje CQRS: zápisový model generuje události; čtecí model je z nich vybudován projektory.
Zdroj: Greg Young, CQRS Documents (2010); Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4
- Event Store (Event Store)
-
Append-only persistence úložiště pro doménové události v Event Sourcingu. Ukládá události per-stream agregátu se zárukou pořadí. Záznamy se nikdy nemutují ani nemažou - je to základní invariant Event Sourcingu. Event Store typicky nabízí: zápis nových událostí do streamu (s kontrolou verze pro optimistické zamykání), čtení streamu od libovolné pozice a subscribování na nové události. Příklady: EventStoreDB, Marten (PostgreSQL), vlastní implementace nad relační databází.
Zdroj: Greg Young, CQRS Documents (2010)
- Projekce (Projection)
-
Čtecí model vybudovaný ze streamu událostí. Projektor naslouchá doménovým událostem a aktualizuje denormalizovanou, čtecí-optimalizovanou datovou strukturu (tabulku, dokument, cache). Projekce lze kdykoliv zrušit a přebudovat přehráním všech událostí z Event Store - jsou odvozené, nikoli zdrojové pravdy. Jedna sada událostí může napájet více projektů s různou strukturou pro různé uživatelské scénáře.
Zdroj: Greg Young, CQRS Documents (2010); Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4
- Snímek / Snapshot (Snapshot)
-
V Event Sourcingu: periodicky uložený stav agregátu, který slouží jako zkratka při přehrávání streamu událostí. Namísto přehrání všech událostí od počátku (což může být tisíce operací) se načte nejnovější Snapshot a přehrají se pouze události po něm. Snapshot je optimalizace výkonu - neovlivňuje správnost modelu. Snímky jsou nepovinné a nelze na ně spoléhat jako na zdrojovou pravdu.
Zdroj: Greg Young, CQRS Documents (2010)
- Sága / Process Manager (Saga / Process Manager)
-
Koordinuje dlouhodobě běžící byznysový proces, který zahrnuje více agregátů nebo Bounded Contextů. Sága naslouchá doménovým událostem, vydává příkazy (Commands) a udržuje stav procesu. Pro případ selhání definuje kompenzační transakce - akce, které vrátí systém do konzistentního stavu (obdoba rollbacku v distribuovaném prostředí). Příklad: proces dokončení objednávky zahrnující platbu, rezervaci skladu a odeslání notifikace. Termín „Saga" pochází z databázového výzkumu (Hector Garcia-Molina, 1987); „Process Manager" je alternativní název z CQRS komunity.
Rozlišujeme dva přístupy: choreografie - ságy řízené výhradně doménovými událostmi bez centrálního koordinátora; každý účastník reaguje na události ostatních a publikuje vlastní. Orchestrace - centrální správce procesů (Process Manager) řídí průběh ságy, přijímá události, rozhoduje o dalším kroku a vydává příkazy. Choreografie je jednodušší pro krátké procesy; orchestrace je vhodnější pro komplexní procesy s mnoha kroky a podmínkami.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 8 & 10; Garcia-Molina & Salem, Sagas (1987)
4. Integrační vzory a komunikace
Co jsou integrační vzory?
Integrační vzory a pojmy popisují, jak spolu různé části systému komunikují, jak se udržuje konzistence v distribuovaném prostředí a jak se předcházejí souběžnostním konfliktům. Zahrnují také důležité anti-vzory - běžné chyby v modelování domény, na které je třeba dávat pozor. Bez porozumění těmto pojmům se systémy, které musí správně fungovat při souběžném přístupu nebo dočasné nedostupnosti komponent, navrhují obtížně.
- Eventual Consistency (Výsledná konzistence)
-
Model konzistence, v němž jsou repliky dat zaručeně konvergovat ke stejné hodnotě, pokud nejsou prováděny žádné další aktualizace - avšak ne nutně okamžitě. V architektuře DDD + CQRS: po zpracování příkazu a publikování události může čtecí model dočasně zaostávat za zápisovým modelem. Toto zpoždění je akceptovatelné a musí být komunikováno s byznysem - stakeholdeři musí rozumět, že „čerstvost" dat má meze. Eventual consistency je nutný kompromis v distribuovaných systémech (CAP teorém) a v CQRS architekturách.
Zdroj: Werner Vogels, Eventually Consistent (ACM Queue, 2008); Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4
- Optimistické zamykání (Optimistic Locking)
-
Mechanismus řízení souběžnosti: namísto uzamykání zdrojů se používá číslo verze (version number). Při aktualizaci se kontroluje, zda verze uložená v databázi odpovídá verzi načtené při čtení. Pokud mezitím jiný proces agregát změnil, vznikne konflikt souběžnosti a operace selže - klient musí načíst nový stav a operaci opakovat. V Event Sourcingu se verze odvozuje z pozice v event streamu. Optimistické zamykání je vhodné pro nízkou konfliktvnost; pesimistické zamykání (skutečné uzamykání) pro vysokou konfliktvnost.
Zdroj: Martin Fowler, Patterns of Enterprise Application Architecture (2002), str. 416–417
- Anémický model (Anemic Domain Model - anti-vzor)
-
Anti-vzor, v němž doménové třídy obsahují pouze data (gettery/settery) a byznysová logika je umístěna do servisních tříd. Porušuje zapouzdření objektově-orientovaného programování a odporuje principu bohatého doménového modelu (Rich Domain Model) v DDD. Výsledkem jsou třídy, které jsou pouze datovými strukturami - ne doménovými objekty. Byznysová logika je rozptýlena a obtížně lokalizovatelná. Martin Fowler ho označil za anti-vzor v roce 2003 (martinfowler.com/bliki/AnemicDomainModel.html). Ironicky jde o nejrozšířenější „DDD" implementaci v praxi.
Zdroj: Martin Fowler, AnemicDomainModel (2003); Eric Evans, Domain-Driven Design (2003), kap. 5
- Primitive Obsession (Primitive Obsession - anti-vzor)
-
Anti-vzor spočívající v používání primitivních typů (
string,int,float) pro doménové koncepty namísto hodnotových objektů. Vede k rozptýlené validaci (každý, kdo přijmestringpro e-mail, musí validovat sám), sémantické ztrátě a argumentové záměně (funkce přijímajícíint orderIdaint userIdmůže přijmout argumenty v nesprávném pořadí bez chyby překladače). Lékem je zavedení hodnotových objektů:OrderId,UserId,Email,Money- typy nesoucí validaci a sémantiku.Zdroj: Martin Fowler et al., Refactoring (1999), kap. 3 (code smell); Eric Evans, Domain-Driven Design (2003), kap. 5
- Korelační ID (Correlation ID)
-
Jedinečný identifikátor připojený k požadavku nebo příkazu, který se propaguje skrze všechny související události, příkazy a záznamy logů. Umožňuje distribuované trasování - propojení všech operací spuštěných jednou uživatelskou akcí. V systému s asynchronní komunikací nebo mikroslužbami je korelační ID nezbytné pro diagnostiku a ladění. Standardně se přenáší v HTTP hlavičkách (např.
X-Correlation-ID) a vkládá do každého vydaného eventu.Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 8; Gregor Hohpe & Bobby Woolf, Enterprise Integration Patterns (2003)
- Causation ID (Causation ID)
-
Podobný Korelačnímu ID: identifikuje konkrétní událost nebo příkaz, který zapříčinil vydání aktuální události. Zatímco Korelační ID propojuje celý kauzální řetěz od kořenové příčiny, Causation ID ukazuje přímého předchůdce v kauzálním grafu. Oba identifikátory dohromady umožňují plnou rekonstrukci kauzálního řetězu - který příkaz co způsobil, které události z něj vzešly a které další příkazy tyto události iniciovaly.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 8
5. Pojmy z testování DDD
Testování v kontextu DDD
Správně implementovaný DDD model je přirozeně testovatelný - izolace doménové logiky od infrastruktury umožňuje psát rychlé, deterministické unit testy bez databáze, HTTP ani externích systémů. Testovací pojmy v této sekci pokrývají nástroje a strategie relevantní pro ověřování doménových pravidel, architektonické správnosti a správného chování systému při migraci ze starého kódu.
- Testovací dvojník (Test Double)
-
Obecný termín pro jakýkoliv objekt, který v testu nahrazuje reálnou závislost. Pochází z filmového světa - „stunt double" je herec, který zastupuje hvězdu v nebezpečných scénách. Taxonomie dle Gerarda Meszarose (xUnit Test Patterns, 2007): Dummy, Stub, Fake, Mock, Spy. Každý typ má jiné vlastnosti a jiný účel. Používání správného typu testovacího dvojníku je podstatné pro testovatelný a udržovatelný testovací kód.
Zdroj: Gerard Meszaros, xUnit Test Patterns (2007), kap. 11
- Stub (Stub)
-
Testovací dvojník, který vrací přednastavené odpovědi bez jakékoliv logiky. Stub neověřuje volání - jeho jedinou rolí je poskytnout testovanému kódu konkrétní hodnotu tam, kde by reálná závislost volala databázi, síť nebo jiný externí zdroj. Používá se tehdy, kdy závislost musí vrátit specifickou hodnotu, aby test mohl pokračovat. Příklad:
StubExchangeRateProvidervždy vrátí kurz 25 CZK/EUR. Nezajímá nás, zda byl volán - jen co vrátil.Zdroj: Gerard Meszaros, xUnit Test Patterns (2007), kap. 11; Martin Fowler, Mocks Aren't Stubs (martinfowler.com, 2007)
- Mock (Mock)
-
Testovací dvojník, který ověřuje volání: očekává, že konkrétní metody byly zavolány s konkrétními argumenty. Mock je neúspěšný, pokud se volání neuskutečnilo nebo proběhlo jinak, než bylo nastaveno. Riziko přespecifikace - testy se stávají křehkými: testují implementační detaily, ne chování. V DDD jsou Mocky vhodné pro ověření vydávání doménových událostí nebo volání externích portů, nikoli pro interní volání v rámci agregátu.
Zdroj: Gerard Meszaros, xUnit Test Patterns (2007), kap. 11; Martin Fowler, Mocks Aren't Stubs (2007)
- Fake (Fake)
-
Fungující, avšak zjednodušená implementace rozhraní. Příklad:
InMemoryUserRepository- plně funkční, ukládá data do pole v paměti, nevyžaduje databázi. Fake je preferovaný testovací dvojník pro repozitáře v DDD: na rozdíl od Mocku neváže testy na implementační detaily; na rozdíl od Stubů skutečně funguje a odhalí chyby v doménové logice, která závisí na výsledcích dotazů. Faky vyžadují více práce na implementaci, ale výrazně zvyšují hodnotu testů.Zdroj: Gerard Meszaros, xUnit Test Patterns (2007), kap. 11
- Charakterizační test (Characterization Test)
-
Test napsaný za účelem zachycení existujícího chování legacy kódu před jeho refactoringem. Popisuje, co kód dělá, nikoli co by měl dělat. Slouží jako bezpečnostní síť během migrace z CRUD na DDD: zajišťuje, že refactoring nezmění pozorované chování systému. Termín zavedl Michael Feathers v knize Working Effectively with Legacy Code (2004). Charakterizační testy jsou dočasné - po dokončení migrace a napsání specifikačních testů je lze postupně nahradit.
Zdroj: Michael Feathers, Working Effectively with Legacy Code (2004), kap. 13
- Architektonický test (Architectural Test)
-
Automatizovaný test ověřující pravidla závislostí mezi vrstvami nebo moduly - například: „Doménová vrstva nesmí záviset na infrastrukturní vrstvě", nebo „Žádná třída v namespace
Domain\nesmí importovat třídu z namespaceInfrastructure\". Architektonické testy jsou spustitelnou dokumentací architektonických rozhodnutí. Zabraňují architektonické erozi - postupnému porušování pravidel, ke kterému dochází bez automatické kontroly. Nástroje pro PHP: Deptrac (qossmic/deptrac), PHP-Arkitekt (phparkitect/arkitect).Zdroj: Václav Makeš a kol.; Deptrac dokumentace (github.com/qossmic/deptrac)
Primární literatura
Definice v tomto glosáři vycházejí z následujících primárních zdrojů:
- Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley, 2003. ISBN 978-0-321-12521-7
- Vernon, Vaughn. Implementing Domain-Driven Design. Addison-Wesley, 2013. ISBN 978-0-321-83457-7
- Vernon, Vaughn. Domain-Driven Design Distilled. Addison-Wesley, 2016. ISBN 978-0-13-443442-1
- Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2002. ISBN 978-0-321-12742-6
- Young, Greg. CQRS Documents. 2010. Dostupné z: cqrs.files.wordpress.com