Glosář DDD terminologie
Akademický slovník hlavních pojmů Domain-Driven Design – od strategických vzorů přes taktické stavební bloky až po architektonické a integrační vzory. Definice vycházejí z primární literatury (Evans 2003, Vernon 2013).
Obsah kapitoly
01 Strategické vzory DDD
Co jsou strategické vzory?
Strategické vzory DDD řeší vysokoúrovňové rozdělení systému na soudržné celky a řízení 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 tvoří základ, na němž teprve stojí taktické stavební bloky. Bez 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á – existuje i bez softwaru; software je jen jedním z prostředků, jak v ní pracovat. Bez pochopení domény nelze software smysluplně navrhnout.
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. Ostatní subdomény jsou periferní – slouží Core Doméně, ale nejsou jejím cílem. Identifikace klíčové domény patří k podstatným výsledkům 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í Core Domény, ale sama o sobě není zdrojem konkurenční výhody. Příklad: sledování objednávek v e-commerce systému, kde primárním diferenciátorem je doporučovací engine. Podpůrné subdomény se typicky vyvíjejí in-house, ale bez hluboké aplikace DDD – jejich složitost nepřesahuje nutnou míru. Jsou nezbytné, nikoli strategické. Pokud lze podpůrnou subdoménu nahradit hotovým řešením, zvažte to.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 2, str. 52–54
- Generická subdoména (Generic Subdomain)
-
Subdoména, jejíž potřeby pokryje existující hotové řešení (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í proto zpravidla plýtvá zdroji. Vhodná 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íž platí konkrétní doménový model. 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. Nezaměňujte ho s modulem nebo namespacem – 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, závislostní dynamiky (kdo závisí na kom), kontrakty integrace a použité integrační vzory (Shared Kernel, Customer–Supplier, ACL apod.). Mapa kontextů je zpravidla prvním hmatatelným výstupem 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í odsouhlasit oba týmy a testovat se musí v obou kontextech. Sdílené jádro vytváří těsnou provázanost – koordinační náklady jsou nezanedbatelné. Používejte ho jen tehdy, kdy přínos sdílení prokazatelně převažuje náklady koordinace. Nejčastěji se hodí 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 musí 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 odstraňuje potřebu překladové vrstvy, ale za cenu závislosti na cizím modelu. Je opakem Anti-korupční vrstvy. Hodí se 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. Hodí se při integraci se špatně navrženými legacy systémy nebo s externími API, která mají odlišnou sémantiku. ACL patří k základním obranným mechanismům 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), přes který mohou ostatní kontexty integrovat. Protokol je zveřejněný, dokumentovaný a aktivně udržovaný. Jiné kontexty se tomuto protokolu přizpůsobí, aniž by potřebovaly přímý přístup k internímu modelu. Open Host Service se hodí 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 nabídneme 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 pomocí 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 vědomé 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
02 Taktické vzory (stavební bloky DDD)
Co jsou taktické vzory?
Taktické vzory DDD jsou konkrétní objektově-orientované stavební bloky, kterými 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 určuje identita, nikoli stav. Na rozdíl od Hodnotového objektu musí entita mít explicitní identifikátor, nejlépe 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, ne 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ů patří k nejtěžším aspektům DDD. Příliš velké agregáty vedou k výkonnostní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. Všechny invarianty agregátu vynucuje kořen – žá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) slouží 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 tento záměr vyjadřuje. 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á 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. Nezaměňujte ji 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 use-case: 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 aplikačním službám odpovídají Command Handlery (pro příkazy) a Query Handlery (pro dotazy). V hexagonální architektuře aplikační služby volá driving port.
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 slouží k volnému 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 doménové události tvoří primární způsob 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 jednodušší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ýlená 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 – nikdy ho nelze porušit, bez ohledu na to, jakým způsobem se agregát modifikuje. Za vynucování všech invariantů odpovídá kořen agregátu. 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 tvoří jádro 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í. Slouží k signalizaci 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)
03 Architektonické vzory
Co jsou architektonické vzory v kontextu DDD?
Architektonické vzory definují, jak se organizují jednotlivé vrstvy a komponenty systému, 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 pozdě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 tvoří přirozený výchozí bod pro DDD a Evans ji explicitně popisuje. 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ě přes Porty (rozhraní) a Adaptéry (implementace). Driving adaptéry (HTTP, CLI, konzole) volají aplikaci; driven adaptéry (databáze, messaging, e-mail) volá aplikace. Hlavní přínos: doménový model je plně izolován od infrastruktury – lze ho testovat bez databáze, HTTP frameworku ani externích systémů. Princip inverze závislostí (DIP) se zde aplikuje 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
MessageBusInterfacenebo explicitní use-case rozhraní jakoPlaceOrderUseCase). Driven porty definují, co aplikace potřebuje od infrastruktury (napříkladUserRepositoryInterface,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 je driving adaptér, který volá aplikaci přes driving port (napříkladMessageBusInterface). Adaptéry patří do infrastrukturní vrstvy a lze je zaměnit 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 všechny controllery leží v jednom místě, všechny repozitáře v druhém atd. Vertikální slices 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. Zavedl ho Greg Young jako rozšíření principu CQS Bertranda Meyera na architektonickou úroveň. CQRS v DDD doplňuje doménový model: ten je příliš bohatý na to, aby sloužil jako přímá projekce pro UI. Oddělení modelů umožňuje optimalizovat každou stranu 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ý se obtížně testuje a jehož chování je nepředvídatelné při vícenásobném volání. CQRS aplikuje tento princip na architektonické úrovni – odděluje celé modely, nejen metody. CQRS na CQS navazuje, ale není to striktní podmínka: CQRS lze zavést i bez důsledného dodržování CQS na úrovni jednotlivých metod.
Zdroj: Bertrand Meyer, Object-Oriented Software Construction (1988), kap. 23
- Event Sourcing (Zdrojování událostí)
-
Persistence vzor, v němž se stav ukládá jako append-only sekvence doménových událostí, ne 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). Funguje jako audit log: každá změna je zaznamenaná, žádná se neztrácí. Umožňuje temporální dotazy – rekonstrukci stavu v libovolném bodě minulosti. Doplňuje CQRS: zápisový model generuje události; čtecí model z nich staví 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 – to je 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é, ne zdrojové pravdy. Jedna sada událostí může napájet více projekcí 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 volitelné 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í obchodní 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 se hodí pro krátké procesy; orchestrace 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)
04 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é anti-vzory – běžné chyby v modelování domény, kterým se vyhněte. 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ž repliky dat konvergují ke stejné hodnotě, pokud neprobíhají žá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 přijatelné, ale musíte ho komunikovat 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í se hodí pro nízkou konfliktnost.
Zdroj: Martin Fowler, Patterns of Enterprise Application Architecture (2002), str. 416–417
- Pesimistické zamykání (Pessimistic Locking)
-
Mechanismus řízení souběžnosti, který skutečně uzamyká zdroj na úrovni databáze (typicky
SELECT … FOR UPDATEneboLOCK TABLES) na celou dobu transakce. Souběžné operace nad stejným záznamem se zablokují a čekají ve frontě. Hodí se pro scénáře s vysokou konfliktností, krátkými transakcemi a vyšší cenou opakování operace než cenou čekání. Daň: snížená propustnost a riziko uváznutí (deadlocků); kompenzuje se pečlivým pořadím zamykání a krátkými držbami zámků. V Event Sourcingu se zpravidla nepoužívá – události streamu jsou seřazené a verze + optimistický check stačí.Zdroj: Martin Fowler, Patterns of Enterprise Application Architecture (2002), str. 426–427; Bernstein & Newcomer, Principles of Transaction Processing (2009), kap. 6
- Čtecí model (Read Model)
-
Datová struktura optimalizovaná pro čtení, oddělená od zápisového (doménového) modelu. Vzniká jako důsledek CQRS: zatímco zápisová strana drží invarianty na agregátech a pracuje skrze repozitáře, čtecí strana plní obrazovky a reporty předpočítanými, denormalizovanými projekcemi. Read Model lze udržovat synchronně (v téže transakci jako zápis) nebo asynchronně (přes projekce naslouchající doménovým událostem). Typicky obsahuje data napříč více agregáty, jejichž společné dotazování by na zápisové straně porušovalo izolaci agregátů. Důležitá vlastnost: čtecí model může být pro různé use-case různý – jeden agregát přispívá do více čtecích modelů.
Zdroj: Greg Young, CQRS Documents (2010); Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 4 a 8
- Kompenzace (Compensation / Compensating Action)
-
Operace, která sémanticky vrací efekt dříve provedeného kroku ságy, aniž by se spoléhala na ACID rollback. Příklad:
CancelPaymentjako kompenzaceAuthorizePayment,RestoreInventoryjako kompenzaceReserveInventory. Kompenzační kroky musí být idempotentní (opakované volání má stejný efekt jako jediné), tolerantní k absenci kompenzovaného kroku (např. když se selhalo ještě před rezervací) a uvnitř ságy by měly být commutative, aby pořadí kompenzací při paralelním běhu neovlivnilo výsledek. Některé efekty technicky vrátit nelze (odeslaný e-mail, fyzický výdej zboží) – pak se kompenzuje jinak (omluvný e-mail, vrácení peněz, dobropis).Zdroj: Hector Garcia-Molina & Kenneth Salem, Sagas (ACM SIGMOD, 1987); Chris Richardson, Microservices Patterns (2018), kap. 4
- Anémický model (Anemic Domain Model – anti-vzor)
-
Anti-vzor, v němž doménové třídy obsahují pouze data (gettery/settery) a doménová logika sídlí v servisních třídách. 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. Doménová logika je rozptýlená a obtížně dohledatelná. Martin Fowler ho označil za anti-vzor v roce 2003 (martinfowler.com/bliki/AnemicDomainModel.html). Ironicky jde o nejrozšířenější „DDD“ implementaci.
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). Řešením 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é vydané události.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 spustily.
Zdroj: Vaughn Vernon, Implementing Domain-Driven Design (2013), kap. 8
05 Pojmy z testování DDD
Testování v kontextu DDD
Správně implementovaný DDD model se dobře testuje – 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 pro ověřování doménových pravidel, architektonické správnosti a 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žití správného typu testovacího dvojníku ovlivňuje testovatelnost a udržovatelnost testovacího kódu.
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 konkrétní 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, 2004)
- Mock (Mock)
-
Testovací dvojník, který ověřuje volání: očekává, že se konkrétní metody zavolaly s konkrétními argumenty. Mock selže, 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 se Mocky hodí pro ověření vydávání doménových událostí nebo volání externích portů, ne pro interní volání uvnitř agregátu.
Zdroj: Gerard Meszaros, xUnit Test Patterns (2007), kap. 11; Martin Fowler, Mocks Aren't Stubs (2004)
- 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ý k zachycení existujícího chování legacy kódu před jeho refactoringem. Popisuje, co kód dělá, ne 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í. Brání 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: Deptrac (github.com/qossmic/deptrac), PHP-Arkitect (github.com/phparkitect/arkitect); širší kontext: Mark Richards & Neal Ford, Fundamentals of Software Architecture (2020), kap. „Architecture Decision Records and Fitness Functions“.
06 Slovní pasti – false friends
Proč to vzniká
DDD termíny se sémanticky překrývají s mnoha pojmy z PHP/Symfony světa, ze Symfony bezpečnosti, z Doctrine, ze Spring/Java kultury. Stejné slovo má v různých kontextech různý význam – a běžný code review tu kontradikci přehlíží. Tato sekce rozplete pasti, které se v DDD projektech opakují napříč týmy a frameworky.
- ACL
-
V DDD: Anti-Corruption Layer – překládací vrstva mezi BC a cizím modelem. Mimo DDD: Access Control List – autorizační seznam (kdo smí na co). V DDD literatuře se „ACL“ vždy myslí to první. Detail.
- Repository
-
V DDD: doménová abstrakce, interface v
App\<BC>\Domaindeklarujícíget()/save()agregátu, jako by žil v paměti. V Symfony/Doctrine:Doctrine\ORM\EntityRepository– třída sfindBy/findAll. Pravidlo: DDD Repository je vždy interface; DoctrineEntityRepositoryje vždy implementace vApp\<BC>\Infrastructure. - Service
-
V DDD: Domain Service (doménová pravidla bez vlastníka), Application Service (use case koordinace), Infrastructure Service (porty/adaptéry). V Symfony: jakákoli třída v DI kontejneru. Pravidlo: nikdy nepojmenovávejte třídu
FooServicebez kvalifikace, jaký typ. - Event
-
V DDD: Domain Event (
OrderPlaced– fakt, který nastal v doméně). V Symfony: objekt předávanýEventDispatcheru – typicky framework / kernel události (KernelEvents::REQUEST). Třídy se nemají sdílet – Domain Events nesmí extendsSymfony\Contracts\EventDispatcher\Event, jinak doména závisí na frameworku. - Entity
-
V DDD: objekt s identitou (Customer, Order). Může být kořenem agregátu nebo uvnitř agregátu. V Doctrine: třída s
#[ORM\Entity]anotací mapovaná na databázový řádek. DDD Entity může (ale nemusí) být současně Doctrine Entity. - Domain
-
V DDD: oblast podnikání, kterou software modeluje (Ordering, Billing, Catalog). V Twig/i18n: translation domain (
messages,validators). Mimo DDD je „Domain“ v PHP velmi vzácné, takže matení bývá jen v Twig. - Controller
-
V Symfony: třída obsluhující HTTP požadavek. V DDD pojmosloví: infrastructure adapter – patří do
App\<BC>\Infrastructure\Http\. Nikoli do Application a už vůbec ne do Domain. - Handler
-
V Symfony Messenger: třída implementující doménovou logiku spuštěnou message busem. Typicky
#[AsMessageHandler]. V DDD: Application Service (Use Case) – pokud zpracovává Command; Domain Event Handler – pokud reaguje na Domain Event. Stejná třída, různé pojmenování v různém kontextu. - Aggregate
-
V DDD: shluk objektů, které se mění atomicky. V SQL/reportech: agregační funkce (
SUM,COUNT). Pojmy se neprolínají, ale juniora to často mate.
07 Symfony ↔ DDD mapping (rozšířená tabulka)
Krátká verze této tabulky je v Cheat Sheetu. Tato je rozšířená – s odkazy na konkrétní kapitoly a anti-vzory.
| Symfony konstrukt | DDD pojem | Kde to v projektu sedí | Detail / anti-vzor |
|---|---|---|---|
Controller (HTTP) |
Infrastructure adapter | App\<BC>\Infrastructure\Http\ |
Hexagonal – Adapter · Anti-vzor: doménová logika v controlleru |
#[AsMessageHandler] handler |
Application Service / Use Case | App\<BC>\Application\Handler\ |
CQRS |
EntityRepository (Doctrine) |
Repository – implementace | App\<BC>\Infrastructure\Doctrine\Doctrine<X>Repository.php |
Interface je v doméně! |
Voter |
Use case authorization | App\<BC>\Infrastructure\Security\ |
Autorizace – Voter |
Form |
Input mapping (DTO) | App\<BC>\Infrastructure\Http\Form\ |
Form getData() → Command DTO, ne přímo Aggregate |
| Twig template | View / Read Model | templates/<bc>/ |
Read model, ne write model |
| Messenger transport (sync) | In-process command/event bus | monolith | – |
| Messenger transport (AMQP) | Integration event channel | cross-BC nebo cross-service | Outbox + Microservices |
Console Command |
Driving adapter (alternativa HTTP) | App\<BC>\Infrastructure\Cli\ |
Volá stejné Application Services jako HTTP Controller |
| Doctrine Migration | Schema evolution | migrations/ |
Žádný DDD ekvivalent – čistě infrastruktura |
EventSubscriber (Doctrine postPersist) |
Infrastructure (NE Domain Event Handler) | App\<BC>\Infrastructure\Doctrine\EventListener\ |
Anti-vzor: doménová pravidla v Doctrine listeneru |
★ 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