Slovník pojmů
Domain-Driven Design
DDD pomáhá tam, kde je problém složitější než samotné uložení formuláře. Kód pak může mluvit o objednávkách, rezervacích a pravidlech stejným jazykem jako lidé, kteří jim rozumějí.
Stručná definice
Návrh kolem významu problému, ne kolem technické vrstvy.
Doména je část reality, kterou software řeší: v e-shopu například prodej, cena, sklad, doprava a platba. DDD vede vývojáře a doménové experty k tomu, aby pro ni vytvořili přesný model a používali jej v rozhovoru, požadavcích, testech i názvech v kódu.
Nejde o ORM, sadu složek ani povinnost zavést každý známý vzor. DDD je užitečné především tehdy, když nejasný význam slov, mnoho výjimek nebo měnící se pravidla začínají způsobovat chyby a drahé domluvy.
Jaký problém řeší
Nejasné pojmy a pravidla schovaná v technických detailech
Model založený jen na tabulkách často pojmenuje důležité rozhodnutí neurčitě jako status, flag nebo service. DDD hledá význam, hranici a vlastníka pravidla.
- různý výklad téhož slova mezi obchodem, zákaznickou podporou a vývojem
- pravidla objednávek, cen, rezervací nebo vratek rozesetá v controllerech a SQL
- integrace marketplace, která používá stejné slovo v jiném významu než interní aplikace
- změny business pravidel, jež se těžko dohledávají a testují
- potřeba oddělit jádro důležité domény od obecné infrastruktury
Praktický příklad
Potvrzení marketplace objednávky a rezervace skladu
V kontextu Prodej může Order obsahovat OrderItem, Money a OrderStatus. Operace confirm() ověří, že objednávka má alespoň jednu položku, není zrušená a její cena odpovídá měně prodeje. Samotné volání skladu nepatří do objektu Order, protože vyžaduje externí systém; aplikační služba proto po ověření modelu požádá sklad o rezervaci a teprve pak změnu uloží v transakci.
Marketplace však může slovem „confirmed“ označit okamžik převzetí požadavku, zatímco interní Prodej jím označuje obchodně platnou objednávku. Překlad na hranici integrace zachová oba významy a zabrání tomu, aby externí status přímo přepsal interní pravidlo.
PHP
final class Order
{
public function confirm(): void
{
if ($this->items === [] || $this->status !== OrderStatus::draft()) {
throw new DomainException('Objednávku nelze potvrdit.');
}
$this->status = OrderStatus::confirmed();
}
}
Zjednodušený diagram
Od doménové otázky k proveditelnému modelu
DDD není lineární proces; tento tok pouze ukazuje, jak se význam pravidla přenáší do kódu a zpět k lidem, kteří jej ověřují.
- Doména Lidé popíšou problém, například kdy smí e-shop potvrdit objednávku a rezervovat sklad.
- Ubiquitous language Tým zpřesní slova Order, reservation, payment authorization a jejich význam.
- Bounded context Význam platí uvnitř vymezené části, například Prodej; jiný systém může mít jiný model.
- Model a invarianty Entity, hodnoty a služby vyjadřují pravidla, která musí zůstat pravdivá.
- Aplikace a integrace Aplikační služba model použije, uloží změnu a přeloží ji pro databázi nebo cizí systém.
Hlavní části
Strategický pohled určuje hranice, taktický model pravidla uvnitř nich
Ne každá aplikace potřebuje všechny tyto stavební prvky. Důležitější je, aby zvolený model chránil skutečná rozhodnutí.
Doména a subdomény
Doména je celá oblast problému, kterou software řeší. Subdomény ji dělí podle odlišného účelu a složitosti, například na prodej, sklad a platby; největší pozornost obvykle potřebuje část s vlastním konkurenčním pravidlem, ne nutně každá podpůrná funkce.
Ubiquitous language
Sdílený jazyk je pracovní dohoda o významu slov. Pokud „objednávka“ znamená pro sklad něco jiného než pro platby, samotné stejné jméno chybu nevyřeší; je potřeba hranice.
Bounded context
Vymezuje, kde má model a jeho názvy platnost. Prodej může chápat zákazníka jako kupujícího, zatímco CRM jako kontakt s historií komunikace. Context není automaticky mikroservisa ani databáze.
Entity a value object
Entita má průběžnou identitu, například Order. Value object popisuje hodnotu a obvykle se porovnává obsahem, například Money nebo OrderStatus. Doctrine entita je technický způsob mapování, ne automatická DDD entita.
Aggregate a aggregate root
Aggregate je hranice konzistence pro malou skupinu objektů. Root je vstupní bod, přes který se mění stav a hlídají invarianty. Nevytváří se proto, že každá tabulka má dostat vlastní aggregate.
Domain service, event a repository
Domain service nese doménovou operaci, která přirozeně nepatří jednomu objektu. Domain event zaznamená významnou skutečnost. Repository v DDD poskytuje kolekci doménových objektů; není to Git repository ani nutně univerzální CRUD vrstva.
Jak funguje v aplikaci
Doménový model, aplikační postup a infrastruktura mají odlišnou úlohu
Taktický DDD model není náhradou všech vrstev aplikace. Pomáhá určit, kam patří rozhodnutí a kam jen převod dat nebo provedení příkazu.
- Doménový model
- Drží pojmy a invarianty, například že potvrzená objednávka musí mít položky, cenu v určené měně a úspěšně vytvořenou rezervaci.
- Application service
- Koordinuje use case: načte aggregate, zavolá doménovou operaci, zajistí transakci a předá výsledek dalšímu kroku. Nemá se změnit v druhé doméně plné pravidel.
- Repository
- Načítá a ukládá aggregate podle potřeb use case. Zda pod ním je Doctrine, SQL nebo externí zdroj, je implementační detail.
- Anti-corruption layer
- Překladová vrstva chrání vlastní model před cizím slovníkem a datovým formátem marketplace nebo účetního systému. Není nutně samostatný velký modul.
Výhody a omezení
Model má snižovat nedorozumění, ne přidávat ceremonie
Přínosy
- pojmenovaná pravidla blízko modelu, který jim dává význam
- lepší rozhovor s lidmi, kteří doménu znají
- jasnější hranice mezi částmi systému s odlišným významem
- testy mohou číst jako konkrétní obchodní scénáře místo technických kroků
Omezení a časté chyby
- DDD není adresářová struktura ani povinnost přejmenovat CRUD na commandy
- Value Object pro každou primitivní hodnotu může zhoršit čitelnost bez ochrany pravidla
- přehnaně velké agregáty zvyšují konflikty a složitost transakcí
- zavedení DDD bez kontaktu s doménovým expertem vytváří jen domnělý model
Pragmatičnost
Hloubku modelu vybírá složitost domény, ne ambice architektury.
Jednoduchý katalog, administrace obsahu nebo jednorázový import mohou být nejčitelnější s přímým formulářem, validací a několika tabulkami. Začít s malým názvoslovím a několika jasnými pravidly je často lepší než předem vytvořit entity, repository a eventy pro každé pole.
DDD pomáhá, když se pravidla opakují, mají výjimky a jejich porušení stojí peníze nebo důvěru. Struktura má odpovídat skutečné složitosti: model se může vyvíjet spolu s porozuměním týmu a nemusí v každém contextu vypadat stejně.
Na co myslet
Model ověřuje společný jazyk i případy na hraně
Dobré otázky ke změně odhalí více než mechanické použití vzoru.
- pojmenovat pravidlo slovy, kterým rozumí doménový expert i vývojář
- zapsat invarianty a příklady, při nichž musí operace selhat
- oddělit stejné slovo s různým významem do vlastních contextů
- nenutit databázovou strukturu, aby určovala API doménového modelu
- testovat rozhodnutí modelu bez závislosti na HTTP nebo konkrétním ORM
- průběžně revidovat, zda abstrakce stále chrání reálnou složitost
Časté otázky
DDD v praxi
Je DDD totéž co Clean Architecture?
Ne. DDD se soustředí na porozumění doméně, její jazyk a model. Clean Architecture řeší zejména směr závislostí a hranici technických detailů. Mohou se rozumně doplnit, ale každý přístup řeší jiný problém.
Musí být každá Doctrine entita DDD entitou?
Ne. Doctrine entita je objekt mapovaný na perzistenci. DDD entita je objekt s identitou a doménovým významem; někdy se kryjí, jindy je vhodné je oddělit.
Jsou agregáty nutné pro každou tabulku?
Ne. Aggregate je hranice konzistence pro konkrétní pravidla. Jeho velikost a existence se odvozují od invariantů a souběhu, ne od počtu tabulek.
Je bounded context samostatná mikroservisa?
Ne nutně. Je to především hranice modelu a jazyka. Může být modul v monolitu, samostatná služba nebo jen jasně oddělená část práce týmu.
Jak přistupuji k návrhu aplikací
Nejdřív potřebuji rozumět pravidlům, teprve potom volit technické hranice.
U složitějších procesů vyjasňuji význam pojmů, neobvyklé stavy a odpovědnost jednotlivých částí aplikace. Teprve podle nich volím míru modelu a automatizovaných kontrol.