Event Storming a Domain Storytelling
Před první řádkou kódu byste měli odejít od počítače. Event Storming Alberta Brandoliniho a Domain Storytelling Hofera & Schwentnera jsou dvě prověřené workshopové techniky, jak v jedné místnosti dostat do shody vývojáře s doménovými experty. Průvodce, který v Symfony projektu funguje.
Obsah kapitoly
DDD nezačíná u kódu. Začíná v místnosti, ve které proti sobě sedí lidé, kteří kód píší, a lidé, kteří doménu reálně provozují. Tato kapitola popisuje dvě konkrétní techniky, jak takovou místnost zařídit. Cílem je strávit v ní dvě až čtyři hodiny smysluplně a odejít s něčím, co se dá zítra otevřít v IDE. Půjde o Event Storming Alberta Brandoliniho (2013) a Domain Storytelling Stefana Hofera a Henninga Schwentnera (2021). Obě techniky řeší stejný problém, totiž extrakci tacitních doménových znalostí. Liší se cestou. Po této kapitole budete vědět, kterou kdy zvolit a jak ji prakticky uřídit.
04.01 Proč workshop, proč ne čtení dokumentace#
Standardní reakce vývojářského týmu, který má zahájit nový projekt nebo přepsat existující, je „dejte nám specifikaci a my to naprogramujeme“. Specifikace ale typicky neexistuje ve formě, která by stačila. Existují wiki stránky staré tři roky, e-mailová vlákna, ticketovací systém s 1 800 issues a čtyři lidé, kteří „to vědí“. Žádný z těchto zdrojů není autoritativní. Každý zachycuje doménu z jiného úhlu, v jiné době a často si protiřečí.
To je v pořádku. Doména žije v hlavách doménových expertů jako znalostní síť; přečíst ji jako knihu nelze. Když se obchodní ředitel a šéf logistiky rozcházejí v tom, co znamená „odeslaná objednávka“, je to signál. Existují dva pohledy, a tedy pravděpodobně i dva Bounded Contexty. Workshop je formát, ve kterém tyto kontradikce vidíte v reálném čase a řešíte je společně. Wiki vám je nikdy neukáže; vždy zachytí pohled toho, kdo ji psal.
Eric Evans v Domain-Driven Design (2003) píše, že Ubiquitous Language nelze odvodit z dokumentů; vzniká pouze v dialogu. Brandolini, Hofer a Schwentner přidávají k tomuto pozorování praktickou metodologii: konkrétní notaci, konkrétní harmonogram, konkrétní role v místnosti.
04.02 Event Storming – co to je a co umí#
Event Storming zavedl italský konzultant Alberto Brandolini v roce 2013. Princip je přímočarý: účastníci v reálném čase pokládají na dlouhou stěnu (nebo Miro/Mural board) oranžové sticky notes s doménovými událostmi vyjádřenými v minulém čase. Postupně z nich vzniká časová osa toho, co se v doméně děje. Jak osa roste, přidávají se další barvy: modrá pro Commands, žlutá pro Actors, růžová pro Hot Spots. Obraz domény se postupně vyjasňuje.
Vznik techniky byl pragmatický. V roce 2012 ji Brandolini předvedl na Italian Agile Day jako event-based modelling workshop, tedy jako zkratku místo kreslení přesného UML diagramu. Jméno EventStorming jí dal až v létě 2013 po experimentech v Belgii a Polsku; v listopadu téhož roku vyšel první blogový post. Původní účel byl taktický: rychle najít hranice agregátů a kontextů. Strategické použití přišlo později. V přednášce 50.000 Orange Stickies Later (2017) autor tu trajektorii shrnuje sám: z náhrady za diagram se stala učební pomůcka a nakonec platforma pro kolaborativní modelování od byznysu po implementaci.
Dnes technika existuje ve třech formátech. Kanonické názvy uvádí web eventstorming.com i Brandoliniho firma Avanscoperta; pro druhý a třetí se v komunitě vžily kratší zkratky Process Level a Design Level.
- Big Picture EventStorming – strategická úroveň. Otázka: „Co se v naší doméně vůbec děje?“ Cílem je objevit Bounded Contexty a hlavní procesy. Brandolini s ním počítá jako s celodenním formátem pro 20–30 lidí; 2–4 hodiny je zkrácená varianta této knihy pro menší doménu.
- Process Modelling EventStorming (komunitně Process Level) – operační úroveň. Otázka: „Jak konkrétně běží jeden zvolený proces?“ Cílem je popsat jeden Bounded Context detailněji, včetně Commands, Actors, Policies a externích systémů. Zavádí přísnější gramatiku notace, do návrhu softwaru ale nevstupuje. Trvání 4–8 h.
- Software Design EventStorming (komunitně Design Level) – taktická úroveň. Otázka: „Jak se tato část modelu přeloží do tříd?“ Cílem jsou kandidáti na agregáty, invariantní pravidla a první draft API. Trvání 2–6 h, typicky per BC.
Vaughn Vernon v Domain-Driven Design Distilled (Addison-Wesley, 2016, kap. 7) řadí Event Storming mezi nástroje, které urychlují učení a cestu k pracovnímu modelu domény. V DDD Distilled mu patří poslední kapitola, tedy až za agregáty a doménovými událostmi; jako první technika ho doporučuje tahle kniha, ne Vernon.
04.03 Notace – barvy a tvary#
Paletu barev popisuje Brandolini v knize Introducing EventStorming. Ta vychází na Leanpubu průběžně od roku 2013 a hotová dodnes není: k datu psaní uvádí Leanpub 70 % obsahu a poslední aktualizaci ze srpna 2021. Nejúplnější veřejně dostupnou legendu proto udržuje ddd-crew v EventStorming Glossary & Cheat Sheet. Každá barva má jeden význam a tým by se ho měl držet – jakmile začnete improvizovat, ztrácíte schopnost rychle „číst“ cizí mapu.
| Barva a tvar | Prvek | Formát | Příklad | Význam |
|---|---|---|---|---|
| Oranžová sticky | Domain Event | všechny | OrderPlaced |
Něco, co se v doméně stalo. Vždy v minulém čase. |
| Modrá sticky | Command / Action | Process Modelling a výš | PlaceOrder |
Záměr, který vede k eventu. Imperativ. Se stakeholdery se lépe drží slovo Action. |
| Malá žlutá sticky | Actor / Agent | všechny | Customer, Cashier |
Kdo command iniciuje. Osoba, role, tým i celé oddělení. |
| Šedá sticky (kanonicky široká růžová) | External System | všechny | Stripe, SendGrid |
Systém mimo naši doménu, se kterým komunikujeme. Patří sem i sdílený excelový soubor. |
| Růžová, natočená do kosočtverce | Hot Spot | všechny | „Co když platba selže?“ | Otázka, kontroverze, nevyjasněné místo. Nediskutuje se hned, jen se zaznamenává. Natočení je součást notace. |
| Zelená sticky | Opportunity | Big Picture | „Refund by mohl být samoobslužný“ | Pozitivní protějšek hot spotu: místo, kde vidíte příležitost. |
| Malá červená / zelená | Value | Big Picture | „−2 dny čekání“ | Záporná nebo kladná hodnota kroku pro zákazníka. |
| Lila větší sticky | Policy / Reactive logic | Process Modelling | „When OrderPlaced ⇒ send confirmation“ | Reaktivní pravidlo: „kdykoliv se stane X, udělej Y“. |
| Zelená sticky | Query Model / Read Model | Process Modelling | „Order detail page“ | Projekce, na základě které se actor rozhodne pro command. |
| Velká žlutá sticky | Constraint (dříve Aggregate) | Software Design | Order |
Konzistenční hranice a pravidlo, které v ní musí platit. |
| Fialová sticky / čára | Bounded Context | Big Picture | „Ordering BC“ | Hranice mezi modely. Kanonicky se kreslí páskou nebo čarou, ne lepí. |
Barevné konvence se mezi facilitátory liší. Brandolini ve své knize značí hot spoty fialovou; tabulka výše používá růžovou a fialovou vyhrazuje pro hranice kontextů. Před workshopem se proto vyplatí legendu vyvěsit na stěnu, ať se skupina nedohaduje o významu barvy místo o doméně.
Dvě položky v tabulce potřebují komentář. Zelená nese ve dvou formátech dva různé významy, Opportunity v Big Picture a Query Model v Process Modellingu; v jednom workshopu se oba prvky nepotkají, takže záměna nehrozí. A velká žlutá lepka se v glosáři ddd-crew jmenuje Constraint, ne Aggregate. Posun je jazykový, protože slovo agregát doménovému expertovi nic neříká. Tato kniha u pojmu agregát zůstává, protože ho čtenář potřebuje pro kód; na cizí mapě se tentýž prvek jmenuje Constraint.
Pro online workshopy má Brandolini na Miroverse dvě vlastní šablony, Process Modelling a Software Design. Komunitních šablon je v Miru víc, barvy v nich ale nemusí odpovídat legendě výše. Pro offline workshop odpovídají stejné barvy balení Post-It 3M (oranžová má kód Vital Orange, růžová Power Pink). Workshop spotřebuje stovky sticky notes, zásoba proto musí být velká.
04.04 Big Picture workshop – návod krok za krokem#
Big Picture je první workshop, který tým s novou doménou (nebo s migrací z existujícího CRUD systému, viz kapitola o migraci) udělá. Cílem není dokonalý model, ale společná mapa toho, co se v doméně děje, a identifikace 3–7 Bounded Contextů.
04.04.1 Příprava (-1 týden)
Přípravu nelze obejít:
- Místnost a stěna. 6–8 m dlouhá rovná stěna bez dveří a nábytku v cestě. Brandolini požaduje Unlimited Modelling Space, souvislou plochu, kterou workshop nesmí vyčerpat. Místnost naopak potřebuje otevíratelné okno; skupina dvaceti lidí vydýchá vzduch dřív, než se čeká. Online varianta stojí na frame 12 000 × 4 000 px v Miro nebo Mural.
- Účastníci. Primární zdroje uvádějí pro Big Picture 15–30 lidí, typicky 25–30; ddd-crew mluví o 10 až 30 a více u jednoho papírového rolu. Velká skupina se neřeší redukcí lidí, ale tím, že se u stěny sama rozpadne na hloučky, které pracují paralelně. Musí tam být alespoň 2 doménoví experti (lidé, kteří doménu reálně provozují, ne PM-ové). Z vývojářské strany 3–5 vývojářů včetně tech leada, plus jeden facilitátor (viz níže). Sestava kolem deseti lidí se uřídí snadněji, je to ale vědomý kompromis: část pohledů na doménu v místnosti chybí.
- Materiál. 5–10 balíčků oranžových stickies (3M Post-It, 76×76 mm), 2 balíčky růžových, 2 modrých, 1 malý žlutý, 1 velký žlutý (Constraint), 1 šedý, 1 zelený, 1 lila (světle fialový), 1 tmavě fialový. Černé fixy Sharpie pro každého (žádná kuličková pera, text nebude čitelný z 2 m).
- Catering. Káva, voda, ovoce, oběd. Workshop unaví – bez cateringu padá energie po 90 minutách.
- Pozvánka. Účastníci dostanou předem jednostránkovou agendu. Doménoví experti se z ní dozvědí, že nebudou prezentovat slidy, ale budou „vyprávět příběh“.
04.04.2 Postup workshopu (zkrácená varianta, 2–4 hodiny)
- (10 min) Brief a startovací event. Facilitátor v 5 minutách vysvětlí pravidla: oranžová = co se stalo, minulý čas, lepit kamkoliv. Pak workshop odstartuje tím, že napíše první event, o kterém ví, že nastává v doméně, a nalepí ho doprostřed stěny, například
OrderPlaced. - (20–30 min) Chaotic exploration. Všichni dostanou stejně oranžových stickies (~15 každý) a píší události, které je napadnou. Lepí kamkoliv bez pořadí. Jde o záměrný chaos – chcete, aby si lidé vzpomněli na vše, ne aby okamžitě strukturovali. Facilitátor sbírá poznámky a tlačí lidi: „a co se stane potom? a předtím?“.
- (30 min) Enforcing the timeline. Facilitátor začne přesouvat eventy doleva (dříve) a doprava (později). Vznikne časová osa. Účastníci do toho mluví: „ne, refund je až po reklamaci, posuň to“. Duplicitní eventy se slučují, ale jen se souhlasem účastníků.
- (30–45 min) Pivotal Events. Facilitátor identifikuje zlomové body, tedy eventy, kolem kterých se přirozeně sdružuje skupina ostatních. V e-shopu typicky:
CustomerRegistered,OrderPlaced,PaymentSettled,ShipmentDispatched,OrderClosed. Značí se svislou čarou napříč celou časovou osou, která ji rozdělí na úseky. Typicky 3–7 pivotal events. - (30–45 min) Hot Spots. Kdykoliv během workshopu zazní otázka, kterou nikdo neumí hned zodpovědět („Co když zákazník zaplatí dvakrát?“), nediskutuje se. Místo toho se napíše na růžovou sticky a nalepí přesně tam, kde otázka vznikla. Po 45 minutách máte typicky 8–15 hot spotů. To je nejcennější výstup Big Picture.
- (20–30 min) Bounded Context boundaries. Facilitátor s týmem hledá místa, kde se mění slovník: kde tentýž pojem znamená něco jiného, kde končí jeden příběh a začíná jiný. Označí je fialovými stickies nebo silnými fialovými čarami. Typicky 3–7 BC.
- (15 min) Foto a transkripce. Širokoúhlé foto stěny v originálu, pak detailní fotky po sekcích. Vše uložit do
docs/discovery/<datum>/v repu. Online workshop: Miro export jako PNG i jako board (link).
04.04.3 Jak poznat hranici kontextu
Krok 6 stojí a padá na tom, zda hranici poznáte, když na ni narazíte. Čtyři heuristiky, které na stěně fungují nejspolehlivěji:
- Lingvistické švy. Stejné slovo, jiný význam. „Objednávka“ pro prodejce znamená košík se slevami, pro sklad seznam položek k vychystání a pro účtárnu podklad faktury. Jakmile jedno slovo nese tři definice, máte před sebou tři kontexty, ne jeden.
- Pivotní eventy (pivotal events). Zlomová událost mění význam entity. Před
OrderPlacedje objednávka editovatelným návrhem; po něm je závazkem vůči zákazníkovi. Entita, která událostí mění povahu, typicky překračuje hranici: z jednoho kontextu vstupuje do druhého. - Hranice oddělení. Levný první odhad. Tam, kde si firma předává práci (prodej → sklad → účtárna), se obvykle mění slovník i pravidla. Slepě se ale přebírat nedají; org chart bývá historický, ne doménový.
- Vlastnictví dat. Otázka „kdo smí tohle pole změnit?“ má uvnitř jednoho kontextu jedinou odpověď. Pokud cenu produktu mění dva týmy podle dvou různých pravidel, nejde o jedno pole se dvěma editory, ale o dva koncepty ve dvou kontextech.
Žádná z heuristik není sama o sobě rozhodující. Hledáte místa, kde se jich protne víc najednou – lingvistický šev na hranici oddělení s vlastním vlastnictvím dat je téměř jistá hranice BC. Vazbu mezi pivotními událostmi a hranicemi kontextů rozebírá Brandolini v eseji Discovering Bounded Contexts with EventStorming ve sborníku Domain-Driven Design: The First 15 Years (Leanpub, 2019). Pojmenované vztahy mezi nalezenými kontexty pak popisuje kapitola Context Mapping.
04.04.4 Co máte na konci Big Picture
- Časová osa s 30–100 doménovými eventy.
- 3–7 identifikovaných pivotal eventů.
- 3–7 vyznačených Bounded Contextů.
- 8–15 hot spotů jako budoucí tickety.
- Foto / Miro export.
Co nemáte a ani by nemělo být cílem: kompletní model, schéma databáze, finální seznam tříd. Big Picture je strategický nástroj – taktiku řeší až Software Design.
04.04.5 Online varianta – nastavení Miro/Mural
Kolik se online ztratí, záleží na tom, který ze tří formátů děláte. Brandolini to rozepsal v textu Remote EventStorming (březen 2020). Software Design online snese nejvíc: malý rozsah, 90 minut, technické publikum. Process Modelling jde podmínečně: půlden, 5–15 lidí, tým už formát zná z prezenční verze a každá třetí session je naživo. K Big Picture má jedinou větu: „Don't even try.“ Vlastní pokus označil za dysfunkční i s expertními účastníky, protože online mizí paralelní konverzace u části stěny, řeč těla i celodenní ponoření. Doporučuje také remote sezení vůbec nenazývat EventStormingem, aby si tým se jménem techniky nespojil špatnou zkušenost.
Přesto se online Big Picture dělá, protože doménoví experti sedí ve třech městech a alternativou nebývá offline workshop, ale žádný workshop. Následující postup je vědomý kompromis se známou cenou. Co se dodržet dá:
- Frame 12 000 × 4 000 px. Týmy často podcení velikost plátna. Big Picture na 50+ eventů potřebuje hodně horizontálního prostoru, jinak se účastníci začnou navzájem překrývat. V Miro založte nový board a první frame udělejte explicitně s těmito rozměry, parametr Frame size.
- Předpřipravená paleta. Vlevo na boardu položte 7–9 zdrojových stickies (jednu od každé barvy) a kolem nich rámeček s popiskem „Kopírujte odsud (Ctrl+D duplikuje)“. Účastníci si stickies kopírují, místo aby pracně otevírali sticky picker.
- Voice-only, kamery vypnuté. Kamery odvádějí pozornost od boardu; všichni se musí dívat na stejné plátno. Výjimka: úvodních 5 minut představení a pak při hot-spot diskusích.
- Breakout místnosti pro dvě fáze. Při Pivotal Events fázi rozdělte skupinu do 2–3 breakout místností po 4 lidech. Každá skupina si v Miru pracuje na jednom segmentu časové osy. Po 20 minutách se vše vrátí zpět do hlavní místnosti a synchronizuje. Bez breakoutů online workshop kolabuje na jednoho aktivního a pět pasivních pozorovatelů.
- Přestávky každých 60 minut. Online unavuje rychleji než offline. Vložte 10minutové přestávky a nezkracujte je.
- Asynchronní příprava. Pošlete účastníkům 24 hodin předem otevřený Miro board s úvodním textem a požádejte je, aby před workshopem nalepili 5–10 eventů, které je napadnou. Workshop pak nezačíná u prázdné stěny.
04.04.6 Kdy Big Picture nedělat
- Zralý produkt s ustáleným modelem. Když tým pracuje v jedné doméně tři roky a má aktuální Context Map, nový Big Picture typicky neodhalí nic nového. Víc přinese Process Modelling nad konkrétním bolavým BC.
- Tým není ochotný diskutovat. Big Picture stojí na otevřené debatě. Pokud je v týmu strach z konfrontace nebo silně hierarchická kultura, musí nejdřív padnout tato bariéra. Jinak workshop produkuje falešný konsenzus.
- Doménoví experti jsou v různých časových pásmech bez přesahu. Big Picture musí proběhnout najednou. Když se nenajde 3–4hodinové okno, kdy jsou všichni hlavní hráči online, náhradou je série Domain Storytelling sezení 1:1 se sloučenými výstupy.
04.05 Process Modelling – jeden BC, hlubší detail#
Po Big Picture máte 3–7 Bounded Contextů. Process Modelling si vždy bere jeden BC najednou a zhušťuje ho. Cílem je dostat se ke struktuře, která se v Symfony reálně přeloží do Command tříd, Handlerů a Eventů na message busu (podrobně v kapitole CQRS).
04.05.1 Co Process Modelling přidává oproti Big Picture
K eventům přibývají modré Commands, tedy záměry, které k nim vedou, a žlutí Actors, kteří je spouštějí. Lila stickies nesou Policies, reaktivní pravidla typu „kdykoliv X, udělej Y“. Šedá patří External Systems, třetím stranám. A zelené Read Models zachycují projekce, podle kterých se actor rozhoduje.
04.05.2 Postup (4–8 hodin per BC)
- Otevřete jen události a hot spoty z Big Picture, které spadají do cílového BC. Zbytek skryjte (jiný frame v Miru, papírová stěna jen pro tento BC).
- Pro každou událost zpětně doplňte: jaký command k ní vedl? a kdo ten command vyvolal? Vznikne sekvence
Actor → Command → Event. - Pro každou událost dopředně doplňte: co se v reakci stane? Lila (světle fialové) policy stickies. „Kdykoliv
OrderPlaced, pošli potvrzovací mail“. - Identifikujte commands, které volají externí systémy nebo na ně reagují (šedé). „Po
PaymentRequestedvolám Stripe, čekám naStripePaymentSucceeded“. - Pro každý command identifikujte, jaký read model actor potřebuje vidět, aby command spustil. „Cashier potvrdí objednávku, když vidí, že platba prošla – read model
OrderDetailmusí obsahovatpaymentStatus“. - Aktualizujte hot spoty. Některé z Big Picture se na této úrovni vyřeší, jiné se rozpadnou na podrobnější (např. „Co když Stripe vrátí 500?“).
04.05.3 Příklad – Ordering BC e-shopu
Sekvence pro hlavní scénář:
1Customer (actor)2 → PlaceOrder (command)3 → OrderPlaced (event)4 → "Reserve stock" (policy)5 → ReserveStock (command, jiný BC: Warehouse)6 → "Send confirmation email" (policy)7 → SendGrid (external system)8 → "Initiate payment" (policy)9 → ChargeCard (command, jiný BC: Payment)10 → Stripe (external system)11 → PaymentReceived (event)
Sekvence ještě není kód, slouží jako mapa pro implementaci. Ale je z ní okamžitě vidět, že budete potřebovat:
- Application Service
PlaceOrderHandlerv Ordering BC. - Process Manager, který koordinuje
OrderPlaced → ReserveStock → ChargeCardpřes BC hranice (podrobně v kapitole Ságy a process managery). - Adaptér k Stripe (anti-corruption layer).
- Read model
OrderDetailViewpro UI.
04.05.4 Co máte na konci Process Modellingu
- Pro každý BC: detailní mapu commands, events, policies, externals, read models.
- Seznam kandidátů na Application Services (1 command typicky = 1 service / 1 handler).
- Seznam kandidátů na ságy / process managery (každý policy přes hranici BC).
- Seznam externích systémů, pro každý plánovaný ACL.
- Aktualizovaný seznam hot spotů, vyřešené i nové.
04.06 Software Design – pro každý BC zvlášť#
Software Design je nejtaktičtější formát Event Stormingu a první, který se přibližuje kódu. Cílem je pro každý Bounded Context identifikovat agregáty, jejich invariantní pravidla a způsob, jakým commands modifikují stav agregátu.
04.06.1 Co Software Design přidává
- Constraints, v této knize agregáty (velké žluté lepky) – konzistenční hranice. Každý command má jeden agregát, který ho obsluhuje.
- Invariants – pravidla, která agregát musí dodržet. Píšou se jako bullet pointy na sticky agregátu.
- Pre-conditions – co musí být splněno, aby command směl projít.
04.06.2 Postup (2–6 hodin per BC)
- Vezměte mapu z předchozí úrovně a pro každý command položte velkou žlutou sticky agregátu, který ho obsluhuje. Stejný agregát pro více commandů je v pořádku; znamená to jen, že třída bude mít víc metod.
- Pod každý agregát vypište jeho invarianty. „Order: nemůže být confirmed bez aspoň jedné položky“, „Order: po cancelled už nelze confirm“, „Order: součet item.quantity * item.price = total“.
- Pro každý command vyznačte pre-conditions: „
ConfirmOrdervyžaduje, abyOrderbyl ve stavuDrafta měl alespoň jeden item“. - Označte hot spoty, které vám chybí pro úplnou specifikaci agregátu („Co když má položka nulovou cenu? Jde o legitimní freebie nebo chybu?“).
04.06.3 Mapping z workshopu do Symfony
Workshop: Customer → PlaceOrder → Order Aggregate → OrderPlaced
Symfony / PHP draft (toto je první draft, ne finální kód):
1<?php2 3// Application/Command/PlaceOrderCommand.php4namespace App\Ordering\Application\Command;5 6use App\Ordering\Domain\ValueObject\CustomerId;7 8final readonly class PlaceOrderCommand9{10 public function __construct(11 public CustomerId $customerId,12 /** @var list<OrderItemDto> */13 public array $items,14 ) {}15}16 17// Domain/Order.php18namespace App\Ordering\Domain\Model;19 20use App\Ordering\Domain\Event\OrderCancelled;21use App\Ordering\Domain\Event\OrderConfirmed;22use App\Ordering\Domain\Event\OrderPlaced;23use App\Ordering\Domain\Exception\EmptyOrderException;24use App\Ordering\Domain\Exception\InvalidOrderStateTransitionException;25use App\Ordering\Domain\ValueObject\CustomerId;26use App\Ordering\Domain\ValueObject\OrderId;27use App\Ordering\Domain\ValueObject\OrderStatus;28use App\Ordering\Domain\ValueObject\ProductId;29use App\SharedKernel\Domain\AggregateRoot;30use App\SharedKernel\Domain\Money;31 32final class Order extends AggregateRoot33{34 /** @var list<OrderItem> */35 private array $items = [];36 37 private function __construct(38 public readonly OrderId $id,39 public readonly CustomerId $customerId,40 private OrderStatus $status,41 ) {}42 43 public static function place(OrderId $id, CustomerId $customerId): self44 {45 $order = new self($id, $customerId, OrderStatus::Draft);46 $order->record(new OrderPlaced($id, $customerId));47 48 return $order;49 }50 51 public function addItem(ProductId $productId, int $quantity, Money $unitPrice): void52 {53 if ($this->status !== OrderStatus::Draft) {54 throw new InvalidOrderStateTransitionException('Cannot add items to a non-draft order');55 }56 57 $this->items[] = new OrderItem($productId, $quantity, $unitPrice);58 }59 60 public function confirm(): void61 {62 // Invariant z workshopu: confirm jen ze stavu Draft63 if ($this->status !== OrderStatus::Draft) {64 throw new InvalidOrderStateTransitionException('Cannot confirm a non-draft order');65 }66 67 // Invariant z workshopu: objednávka musí mít aspoň jednu položku68 if ($this->items === []) {69 throw EmptyOrderException::cannotConfirm();70 }71 72 $this->status = OrderStatus::Confirmed;73 $this->record(new OrderConfirmed($this->id, $this->customerId, new \DateTimeImmutable()));74 }75 76 public function cancel(string $reason): void77 {78 // Invariant z workshopu: zrušit lze draft i potvrzenou objednávku79 if ($this->status !== OrderStatus::Draft && $this->status !== OrderStatus::Confirmed) {80 throw new InvalidOrderStateTransitionException('Cannot cancel a shipped order');81 }82 83 $this->status = OrderStatus::Cancelled;84 $this->record(new OrderCancelled($this->id, $this->customerId, $reason, new \DateTimeImmutable()));85 }86 87 public function totalAmount(): Money88 {89 if ($this->items === []) {90 throw EmptyOrderException::cannotBePlaced();91 }92 93 $total = Money::zero($this->items[0]->unitPrice->currency);94 95 foreach ($this->items as $item) {96 $total = $total->add($item->unitPrice->multiply($item->quantity));97 }98 99 return $total;100 }101}102 103// Application/Handler/PlaceOrderHandler.php104namespace App\Ordering\Application\Handler;105 106use App\Ordering\Application\Command\PlaceOrderCommand;107use App\Ordering\Domain\Model\Order;108use App\Ordering\Domain\Repository\OrderRepository;109use App\Ordering\Domain\ValueObject\OrderId;110use Symfony\Component\Messenger\Attribute\AsMessageHandler;111 112#[AsMessageHandler]113final readonly class PlaceOrderHandler114{115 public function __construct(116 private OrderRepository $orders,117 ) {}118 119 public function __invoke(PlaceOrderCommand $cmd): OrderId120 {121 // Identitu přiděluje aplikace, ne databáze - viz Základní koncepty.122 $order = Order::place(OrderId::generate(), $cmd->customerId);123 124 foreach ($cmd->items as $item) {125 $order->addItem($item->productId, $item->quantity, $item->unitPrice);126 }127 128 $this->orders->save($order);129 130 return $order->id;131 }132}
Každý prvek z workshopu má v kódu protějšek. Command sticky → PlaceOrderCommand. Constraint (agregát) → třída Order. Invariant z bullet pointu → throw v doménové metodě. Event sticky → OrderPlaced zaznamenaný přes record().
Překlad ale není mechanický. Tři rozhodnutí padají mimo místnost a na stěně pro ně není barva:
- Kdy se eventy publikují. Ukázka je jen zaznamenává do agregátu. Kdo je pošle na sběrnici a jak se to sladí s commitem databázové transakce, řeší Outbox Pattern. Dispatch hned za
save()je dual-write a rozbije se při první výjimce mezi zápisem a odesláním. - Jestli command něco vrací.
__invoke()zde vracíOrderId. Volající tu hodnotu dostane jen přesHandledStampneboHandleTraita pouze u synchronně zpracovaných zpráv; na asynchronním transportu běží handler ve worker procesu a návratová hodnota se k odesílateli nedostane. - Na jakou sběrnici to jde. Symfony má
MessageBusInterface. Oddělená command a event sběrnice je až věc konfiguraceframework.messenger.busesa aliasů, podrobně v kapitole CQRS.
04.07 Domain Storytelling – alternativa pro malé týmy#
Domain Storytelling představili Stefan Hofer a Henning Schwentner v knize stejného jména (Addison-Wesley, 2021). Stejně jako Event Storming řeší extrakci doménových znalostí, ale jinou cestou: místo časové osy událostí kreslíte příběh o práci doménového experta ve standardizované piktogramové notaci.
04.07.1 Notace
- Actor – postavička panáčka. Kdo v doméně něco dělá. „Customer“, „Cashier“, „Warehouse worker“. Může to být i jiný systém nebo celá organizace. Každý actor se v jednom příběhu kreslí jen jednou.
- Work Object – piktogram věci, se kterou actor pracuje. Dokument (objednávka), peníze, e-mail, balík, zboží. Kreslí se znovu u každé aktivity, i když jde o tutéž věc, protože se během příběhu mění její stav nebo médium.
- Activity – šipka se slovesem, opatřená pořadovým číslem. Číslo patří aktivitě, ne jednotlivé šipce: krok, ve kterém actor předává work object dalšímu actorovi, se kreslí dvěma šipkami, ale nese jedno číslo.
- Annotation – textová bublina s poznámkou: varianta, volitelný krok, možná chyba, doménový pojem.
- Group – rámeček kolem skupiny aktivit. Ohraničuje opakovaný úsek, lokalitu, organizační hranici nebo subdoménu.
Věta příběhu má pevnou gramatiku: kdo (actor) dělá co (activity) s čím (work object) s kým (jiný actor). Jeden příběh se drží v rozmezí pěti až patnácti kroků. Delší se rozpadne na dva.
04.07.2 Scope – jaký příběh vlastně kreslíte
Domain story bez určeného scope dopadne tak, že si polovina místnosti myslí, že popisuje dnešek, a druhá polovina, že návrh. Hofer se Schwentnerem proto každý příběh zařazují ve třech osách:
- Granularita. Hrubý příběh (coarse-grained) dává přehled o celém procesu. Jemný (fine-grained) rozepisuje jeden úsek do detailu, ve kterém se dá programovat.
- Čas. AS-IS zachycuje, jak práce probíhá dnes. TO-BE, jak má probíhat po změně.
- Čistota domény. Pure příběh popisuje doménu bez softwaru, digitalized včetně systémů, které v ní figurují. Jeden obrázek je buď jedno, nebo druhé; míchat obojí nelze.
Typická cesta projektu vede třemi příběhy. Jemný AS-IS pure ukáže, jak lidé pracují dnes. Hrubý AS-IS pure z toho udělá mapu. Jemný TO-BE digitalized popíše cílový stav i se softwarem. Trojici os se vyplatí napsat do rohu plátna dřív, než padne první šipka. Jinak se o ni skupina pohádá v půlce příběhu.
04.07.3 Konkrétní příklad – proces objednávky v e-shopu
Story „Customer places an order“ v Domain Storytelling notaci, čtená v pořadí čísel:
- Customer →(1) browses → Catalog
- Customer →(2) adds product to → Cart
- Customer →(3) submits → Order → Order System
- Order System →(4) requests payment from → Payment Gateway (annotation: „Stripe; async webhook“)
- Payment Gateway →(5) confirms payment to → Order System
- Order System →(6) sends → Confirmation Email → Customer
- Order System →(7) creates → Shipment Order → Warehouse
Sedm vět, sedm čísel. Krok 3 se kreslí dvěma šipkami (od actora k work objectu a od work objectu k druhému actorovi), pořadové číslo ale nese celá aktivita, ne šipka. Kresba je úmyslně jednoduchá – ručně nakreslené piktogramy nebo nástroj egon.io (open source, v prohlížeči). Příběh je čitelný shora dolů ve sledu čísel a každá aktivita má slovesné jméno.
04.07.4 Domain Storytelling vs. Event Storming – kdy zvolit co
| Kritérium | Event Storming | Domain Storytelling |
|---|---|---|
| Velikost skupiny | 15–30 pro Big Picture, 4–8 pro Process Modelling | Malá: vypravěč, posluchači, moderátor s modelářem |
| Doba trvání | 2–8 h | Jedno kratší sezení na příběh |
| Šíře záběru | Celý systém / podstatná část | Jeden konkrétní proces |
| Hloubka záběru | Mělčí, ale široký | Hluboká, úzká |
| Hlavní výstup | Bounded Contexty + eventy | Sekvence kroků s actor a work object |
| Dovednost facilitátora | Vyšší (mnoho lidí, chaos) | Nižší (lineární proces) |
| Doporučený nástroj | Stěna + Post-It nebo Miro | egon.io, papír, Miro |
| Kdy zvolit | Nový BC, migrace, strategický přehled | Hluboká diskuse o jednom procesu, malý tým, omezený čas |
Hofer a Schwentner v knize zdůrazňují, že obě techniky se nekonkurují, ale doplňují. Event Storming ukáže, jaké procesy v doméně existují (širokoúhlý objektiv). Domain Storytelling v každém z nich pak odkryje detail (teleobjektiv). Doporučují kombinovat: Big Picture pro strategický přehled, Domain Storytelling pro jednotlivé hlavní procesy a Process Modelling se Software Designem pro implementaci.
04.07.5 Praktický egon.io walkthrough
egon.io je open-source webová aplikace (Angular nad diagram-js), která Domain Storytelling notaci plně implementuje. Pro tým, který nechce kupovat Miro licence nebo tahat papír, je to vhodný nástroj. Postup pro první sezení:
- Otevřete egon.io v prohlížeči – nevyžaduje registraci. Vlevo nahoře je toolbar s ikonkami: actor (panáček), work object (obdélník), activity (šipka).
- Začněte s actorem. Přetáhněte ikonu „person“ na plátno a pojmenujte ji rolí, ne osobou:
Customer, nePetr Novák. Jméno se v exportu objeví u každé aktivity, takže na jeho volbě záleží. - Přidejte work object. Druhý nejčastější tvar – věc, se kterou actor pracuje. V e-shopu typicky
Cart,Order,Invoice,ShipmentLabel. - Spojte je activity. Klik na actora, drag na work object; egon.io vytvoří očíslovanou šipku. Slovesné jméno (browses, submits, confirms) se píše do labelu šipky.
- Buďte struční. Jeden Domain Storytelling diagram by měl mít jeden lineární příběh s 5–15 aktivitami. Když jich máte 30, rozdělte ho na dva diagramy.
- Export do SVG. Menu vpravo nahoře → Download → SVG. Soubor pojmenujte
<datum>-<story-name>.svga uložte dodocs/discovery/<datum>/storytelling/. SVG je textový formát, ve kterém git přehledně zobrazuje rozdíly a v PR review vidíte změny.
Egon.io ukládá příběh ve vlastním textovém formátu .egn (vedle exportu .egn.svg). Soubor patří do repa vedle SVG. Příběh tak lze verzovat, po změně znovu otevřít v egon.io a SVG přegenerovat.
04.08 Anti-vzory workshopů#
Workshop bez přípravy a pevného vedení je horší než žádný. Vytvoří zdání shody, která neexistuje, a tým podle něj implementuje chybný model. Brandolini vede na eventstorming.com katalog sedmnácti pojmenovaných patternů a anti-patternů; kde se s ním následující vzory kryjí, je kanonické jméno uvedeno v závorce. Zde je seznam nejčastějších a jejich řešení.
04.09 Co Event Storming neumí#
Předchozí sekce je o tom, jak workshop pokazí lidé. Následuje seznam toho, co technika neumí ani ve chvíli, kdy ji vedete správně.
Happy path vytlačí zbytek. Časová osa se staví jako příběh a příběhy se vyprávějí od začátku do úspěšného konce. Storna, částečné refundy, ruční zásahy podpory a timeouty externích systémů se na stěnu dostanou jen tehdy, když se na ně někdo cíleně zeptá. Obrana stojí jednu otázku, položenou po dokončení osy u každé pivotní události: „co se stane, když tohle selže?“.
Nefunkční požadavky nemají kam sednout. Latence, dostupnost, retenční lhůty, GDPR, objem dat, cena provozu. Žádná barva pro ně v notaci není a workshop je systematicky přehlíží. Pokud na nich stojí architektura, patří do samostatného sezení; Event Storming je nenahradí.
Mapa žije jen tak dlouho, dokud ji někdo udržuje. Stěna je artefakt jednoho dne. Bez převodu do repa a do kódu z ní za tři měsíce zbude fotka, na kterou se nikdo nedívá. Sekce 04.10 proto není administrativní příloha workshopu, ale podmínka toho, aby po něm něco zbylo.
Výsledek závisí na facilitátorovi víc, než je zdrávo. Tatáž skupina se stejnou doménou vyprodukuje se dvěma facilitátory dvě různé mapy. Technika sama žádnou korekci neobsahuje, a proto se doporučuje mapu po pár týdnech znovu otevřít s odstupem, nejlépe s někým, kdo u prvního workshopu nebyl.
Poslední limit je nejtišší. Konsenzus dvaceti lidí, ze kterých patnáct sedí v jednom oddělení, popisuje pohled toho oddělení, ne doménu. Hot spoty tu díru odhalí jen zčásti: ptají se na to, co skupina ví, že neví.
Nic z toho není důvod workshop nedělat. Je to důvod nečekat, že z něj vypadne hotová specifikace.
04.10 Po workshopu – co s výstupem#
Workshop bez follow-upu je promarněná investice. Zde je seznam 4 konkrétních artefaktů, které musí jít do repa do 24 hodin po skončení workshopu.
04.10.1 Foto / Miro link
Širokoúhlé foto stěny v originálu, detailní fotky po sekcích, Miro export PNG i link. Uložit do:
1docs/discovery/2026-04-29-big-picture/2├── 00-wide-angle.jpg3├── 01-customer-area.jpg4├── 02-payment-area.jpg5├── 03-shipment-area.jpg6├── 99-miro-export.png7└── README.md
README.md obsahuje datum, účastníky, BC a link na živý Miro board.
04.10.2 Aktualizovaná Context Map
Z fialových BC stickies aktualizujte Context Map v docs/context-map.png. Pokud ji ještě nemáte, vytvořte ji teď. Pro každý BC zkontrolujte, který tým ho vlastní a do které kategorie (core / supporting / generic) spadá.
04.10.3 Seznam doménových eventů
Plain-text soubor s jedním eventem na řádek. Slouží jako reference pro budoucí PR. Když vývojář přidává nový event, ověří v něm, zda už nějaký podobný neexistuje.
1# docs/discovery/2026-04-29-big-picture/events.md2 3## Ordering BC4- OrderPlaced5- OrderConfirmed6- OrderCancelled7- OrderItemAdded8- OrderItemRemoved9 10## Payment BC11- PaymentRequested12- PaymentReceived13- PaymentFailed14- PaymentRefunded15 16## Shipment BC17- ShipmentCreated18- ShipmentDispatched19- ShipmentDelivered20- ShipmentReturned
04.10.4 Hot Spots → tickety
Každý hot spot z workshopu = jeden ticket v issue trackeru, ve formátu „Discovery question“ nebo „Domain question“, s odkazem na fotku/Miro. Ticket dostane doménový expert, ne vývojář – odpověď leží v doméně, ne v kódu.
1Title: [Discovery] Co když platba selže po vytvoření zásilky?2Labels: discovery, ordering-bc3Assignee: @business-expert-name4Description:5Hot spot z Big Picture workshopu 2026-04-29 (foto: docs/discovery/2026-04-29-big-picture/02-payment-area.jpg).6Tým si není jist, zda se zásilka vrací zpět, nebo se účet zákazníka jen označí jako neuhrazený.7Potřebujeme jednoznačné rozhodnutí před implementací Process Manager v Ordering BC.
04.10.5 Doporučená struktura repa po prvním workshopu
Aby výstup workshopu nezapadl ve Slacku, založte v Symfony projektu rovnou tuto adresářovou strukturu. Každý soubor má jasný účel a nikdo nemusí hádat, kam co patří:
1my-symfony-app/2├── docs/3│ ├── discovery/4│ │ └── 2026-04-29-big-picture/5│ │ ├── 00-wide-angle.jpg6│ │ ├── 01-customer-area.jpg7│ │ ├── 02-payment-area.jpg8│ │ ├── 03-shipment-area.jpg9│ │ ├── 99-miro-export.png10│ │ ├── events.md ← seznam doménových eventů (text)11│ │ ├── hot-spots.md ← otázky k vyřešení12│ │ └── README.md ← účastníci, datum, link na Miro13│ ├── context-map.png ← aktualizovaná z workshopu14│ ├── context-map.md ← textový popis vztahů mezi BC15│ └── ubiquitous-language.md ← rostoucí slovník pojmů16├── src/17│ ├── Ordering/ ← jeden BC z workshopu = jeden namespace18│ ├── Payment/19│ └── Shipment/20└── ...
Adresář docs/discovery/ je append-only: staré workshopy nemažete, jen přidáváte nové s novým datem. Tým tak má historii, jak se mapa domény vyvíjela. Re-storming, tedy opakovaný workshop nad toutéž doménou (sekce 04.11), pak porovná docs/discovery/2026-04-29-big-picture/events.md s docs/discovery/2026-10-15-re-storming/events.md.
Adresáře src/Ordering, src/Payment, src/Shipment zrcadlí tři z pěti fialových stickies z workshopu, ty Bounded Contexty, které dostaly vlastní kód; jejich vnitřní členění podle vrstev popisuje struktura podle subdomén. Když nový vývojář otevře projekt, vidí strukturu odpovídající tomu, co viděl na fotce ze workshopu. Tato vazba mezi artefaktem v repu a artefaktem ze stěny chrání jazyk workshopu před tím, aby se po půl roce vytratil z kódu.
04.10.6 První PR po workshopu
První pull request po workshopu by měl být malý a explicitně značený jako follow-up, ne velký commit s implementací první feature. Doporučená velikost:
- Vytvoření
docs/discovery/<datum>/se všemi výstupy workshopu. - Aktualizace
docs/context-map.mdadocs/ubiquitous-language.md. - Založení prázdných namespace adresářů (
src/<BC>/Domain/) s krátkýmREADME.mdv každém: kdy vznikl, z jakého workshopu, co obsahuje. - Tickety pro hot spoty (případně přes script, který je vytvoří hromadně).
Žádný kód doménové logiky. Tento PR má jediný úkol: uložit společnou paměť workshopu do repa, než ji všichni zapomenou. Implementace prvního agregátu přijde v dalším PR, který už staví na Software Designu.
04.11 Pravidelné re-stormingy#
Doména se vyvíjí. Pivotní událost, která dnes platí (OrderPlaced), může za rok ztratit význam, protože podnikání přešlo na model subscription a ústředním eventem se stane SubscriptionRenewed. Když tým neudělá nový workshop, kód a doména se rozejdou – a nikdo si toho hned nevšimne, protože jednotlivé PR vypadají rozumně.
04.11.1 Doporučená frekvence
- Pravidelně: 1× za 6 měsíců nebo 1× za rok velký Big Picture re-storming pro celý systém. Rozhoduje stáří produktu: startup může re-stormovat čtvrtletně, zralý produkt jednou ročně.
- Po velkém produktovém rozhodnutí: nový tržní segment, nový obchodní model, akvizice. Re-storming proběhne před implementací, ne po ní.
- Při akutních problémech: tým má pocit, že kód „nedává smysl“ nebo že feature requesty se opakovaně modelují špatně. Pak je čas znovu vytáhnout stickies.
04.11.2 Diff jako priorita refaktoringu
Po re-stormingu porovnejte novou mapu se starou, uloženou v docs/discovery/<starý-datum>/. Místa, kde se mapa změnila nejvíc, jsou kandidáti na refaktoring. Tam doména kódu reálně „utekla“ dopředu. Naopak místa, kde se mapa změnila málo, jsou stabilní a kód v nich je pravděpodobně v pořádku.
Re-storming typicky dělá menší skupina (3–5 lidí z původního workshopu) a trvá kratší dobu, protože hodně mapy se zachová.
04.12 Most z workshopu do testů#
Software Design EventStorming přirozeně ústí v test-driven development. Každý invariant napsaný na sticky agregátu je jeden test case. Totéž platí pro hot spot, který se během workshopu vyřešil. Tým, který z workshopu odejde a nezačne psát testy podle invariantů, ztrácí polovinu jeho hodnoty.
04.12.1 Mapping invariantů na PHPUnit testy
Sticky agregátu z workshopu:
1Order Aggregate2- Inv-1: nemůže být confirmed bez aspoň jedné položky3- Inv-2: po cancelled už nelze confirm4- Inv-3: součet item.quantity * item.price = total5- Inv-4: confirm vyžaduje, aby payment byl Settled (hot spot Order-9)
Přímý překlad do testů:
1<?php2 3declare(strict_types=1);4 5namespace App\Tests\Ordering;6 7use App\Ordering\Domain\Exception\EmptyOrderException;8use App\Ordering\Domain\Exception\InvalidOrderStateTransitionException;9use App\Ordering\Domain\Model\Order;10use App\Ordering\Domain\ValueObject\CustomerId;11use App\Ordering\Domain\ValueObject\OrderId;12use App\Ordering\Domain\ValueObject\ProductId;13use App\SharedKernel\Domain\Currency;14use App\SharedKernel\Domain\Money;15use PHPUnit\Framework\Attributes\Test;16use PHPUnit\Framework\TestCase;17 18final class OrderTest extends TestCase19{20 // Inv-1 (workshop 2026-04-29)21 #[Test]22 public function confirm_throws_when_order_has_no_items(): void23 {24 $order = Order::place(OrderId::generate(), CustomerId::generate());25 26 $this->expectException(EmptyOrderException::class);27 $order->confirm();28 }29 30 // Inv-2 (workshop 2026-04-29)31 #[Test]32 public function cannot_confirm_after_cancellation(): void33 {34 $order = $this->orderWithOneItem();35 $order->cancel('customer request');36 37 $this->expectException(InvalidOrderStateTransitionException::class);38 $order->confirm();39 }40 41 // Inv-3 (workshop 2026-04-29)42 #[Test]43 public function total_equals_sum_of_line_subtotals(): void44 {45 $order = Order::place(OrderId::generate(), CustomerId::generate());46 $order->addItem(ProductId::generate(), 2, new Money(100, Currency::CZK));47 $order->addItem(ProductId::generate(), 1, new Money(50, Currency::CZK));48 49 self::assertSame(250, $order->totalAmount()->amountInCents);50 }51 52 private function orderWithOneItem(): Order53 {54 $order = Order::place(OrderId::generate(), CustomerId::generate());55 $order->addItem(ProductId::generate(), 1, new Money(100, Currency::CZK));56 57 return $order;58 }59}
Komentáře Inv-1 (workshop 2026-04-29) nejsou kosmetika – ukazují na původ pravidla. Když test selže za půl roku a nový vývojář chce zjistit, proč pravidlo existuje, doloví ho přes git blame nebo podle data workshopu.
04.12.2 Doménové eventy jako testy
Z Process Modellingu máte sekvenci Command → Event → Policy → Command. Tato sekvence je acceptance test:
1<?php2 3declare(strict_types=1);4 5namespace App\Tests\Ordering;6 7use App\Ordering\Application\Command\OrderItemDto;8use App\Ordering\Application\Command\PlaceOrderCommand;9use App\Ordering\Domain\Event\OrderPlaced;10use App\Ordering\Domain\ValueObject\CustomerId;11use App\Ordering\Domain\ValueObject\ProductId;12use App\Payment\Application\Command\ChargeCardCommand;13use App\SharedKernel\Domain\Currency;14use App\SharedKernel\Domain\Money;15use PHPUnit\Framework\Attributes\Test;16use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;17use Symfony\Component\Messenger\MessageBusInterface;18 19final class PlaceOrderHandlerTest extends KernelTestCase20{21 // Workshop scenario "Customer places an order" (2026-04-29)22 #[Test]23 public function place_order_emits_OrderPlaced_and_triggers_payment(): void24 {25 $bus = self::getContainer()->get(MessageBusInterface::class);26 $events = $this->collectEvents();27 28 $bus->dispatch(new PlaceOrderCommand(29 CustomerId::generate(),30 [new OrderItemDto(ProductId::generate(), 1, new Money(100, Currency::CZK))],31 ));32 33 self::assertCount(1, $events->ofType(OrderPlaced::class));34 // Policy z workshopu: OrderPlaced ⇒ ChargeCard35 self::assertCount(1, $events->commands(ChargeCardCommand::class));36 }37}
Toto má dva přínosy. První: testy jsou čitelné pro doménové experty. Pojmenování přesně odpovídá workshopu, takže nevývojář si test může přečíst a potvrdit, že vyjadřuje to, co měl na mysli. Druhý: testy jsou ochrana před regresí. Když někdo za rok refaktoruje a omylem porušuje invariant z workshopu, test ho chytí.
Podrobně viz kapitolu Testování v DDD: testovací strategie, doménové testy, integrační testy se Symfony Messenger.
04.13 Shrnutí#
Event Storming a Domain Storytelling jsou dvě konkrétní, prověřené techniky, jak před první řádkou kódu dostat doménu na společný papír. Obě stojí na stejném předpokladu: doménové znalosti nelze přečíst – musí se v dialogu objevit.
- Event Storming ve třech formátech (Big Picture / Process Modelling / Software Design) je nástroj pro širokoúhlé mapování domény. Big Picture objevuje Bounded Contexty a pivotní události. Process Modelling zhušťuje jeden BC do sekvencí Command-Event-Policy. Software Design z nich dodá agregáty s invarianty.
- Domain Storytelling je úzkoúhlý teleobjektiv pro hloubkovou diskusi nad jedním procesem v malé skupině. Notace actor-work object-activity se čte bez zaškolení a hodí se pro kontexty, kde Event Storming je „příliš velký“.
- Workshop začíná u doménového experta, ne u datového modelu. Eventy se píšou v minulém čase, agregáty se objevují až nakonec.
- Workshop bez follow-upu je promarněný. Foto, eventy, hot spoty a Context Map musí jít do repa do 24 hodin a do kódu do 1–2 sprintů.
- Re-storming je pravidelná činnost. Doména se vyvíjí; mapa zastará. 1× za 6–12 měsíců nebo po každém velkém produktovém rozhodnutí.
Po prvním Event Stormingu typicky následuje implementace prvního Bounded Contextu; viz kapitoly o základních konceptech DDD, CQRS, Event Sourcingu a ságách. Pokud migrujete z legacy CRUD systému, pokračujte kapitolou Migrace z CRUD na DDD.
Časté otázky
Kolik lidí by mělo být na Event Storming workshopu?
Primární zdroje uvádějí pro Big Picture 15–30 lidí, typicky 25–30. Velká skupina není chyba: u dostatečně dlouhé stěny se sama rozpadne na hloučky, které pracují paralelně, a facilitátor je průběžně stahuje k celku. Menší sestava kolem deseti lidí (2–4 doménoví experti, 3–5 vývojářů, PM, facilitátor) se uřídí snadněji, je to ale kompromis: část pohledů na doménu v místnosti chybí. Pro Process Modelling a Software Design stačí 4–8 lidí; tam jde o detail jednoho BC. Detailní rozpis v sekci 04.04.
Dá se Event Storming dělat online?
Záleží na formátu a autor techniky je v tom vyhraněný. Brandolini v textu Remote EventStorming (2020) považuje Software Design online za dobře proveditelný, Process Modelling za podmínečně proveditelný (půlden, 5–15 lidí, každá třetí session naživo) a k Big Picture píše doslova „Don't even try“. Online mizí paralelní konverzace u části stěny, řeč těla i celodenní ponoření. Když jinou možnost nemáte, dělejte online Big Picture jako vědomý kompromis: breakout místnosti pro paralelní diskuse, kratší bloky, přestávky každou hodinu. Postup je v sekci 04.04.5.
Jak vést hot spoty během workshopu?
Pravidlo zní: nediskutuje se, jen se zaznamenává. Když během workshopu zazní otázka, kterou nikdo neumí hned zodpovědět, facilitátor ji okamžitě napíše na růžovou sticky a nalepí přesně tam, kde otázka vznikla, a workshop pokračuje dál. Pokus o vyřešení hot spotu hned vždy konzumuje 15–30 minut a typicky se nedořeší, protože odpověď leží mimo místnost. Po workshopu se každý hot spot stane ticketem přiřazeným doménovému expertovi, ne vývojáři.
Kdo platí workshop: produkt, nebo vývoj?
Nejlépe oba společně. Workshop je investice do společné Ubiquitous Language a slovníku, který používají obě strany. Pokud ho zaplatí jen jedna, druhá strana ho nevezme vážně. Pokud přesto platí jen jeden, pak vývoj: bez workshopu vyrobí špatný model a bude ho refaktorovat tři sprinty. To stojí mnohonásobně víc než 4 hodiny doménových expertů.
Co když doménoví experti používají hovorovou češtinu a slang („chronický neplatič nás zase odbil“)?
Workshop dělejte v jazyce, který experti používají v reálné práci – typicky v češtině s vlastním slangem. Slang se neopravuje; je Ubiquitous Language. Když expert říká „chronický neplatič“, napište to na sticky tak, jak to řekl. V kódu pak modelujte koncept s tímto jménem (např. ChronicLatePayer); synonymum používané v týmu doplňte jako PHPDoc komentář. Ztratit jazyk = ztratit slovník = za rok zase nikdo neví, o čem mluvíme.
Když máme jen sólo vývojáře a PM, dá se Event Storming dělat ve dvou?
Ne, Event Storming ve dvou ztrácí smysl; stojí na konfrontaci více pohledů. Místo toho použijte Domain Storytelling, který malou skupinu snese. PM hraje doménového experta, vývojář kreslí story, debatujete krok za krokem. Za jedno sezení dostanete použitelný výstup pro jeden konkrétní proces. Jen si předem ujasněte scope příběhu podle sekce 04.07.2. Až přibude třetí člen týmu nebo se uvolní více doménových expertů, přejděte k Big Picture Event Stormingu.
04.14 Další četba#
- Alberto Brandolini – Introducing EventStorming (Leanpub). Kniha přímo od autora techniky s popisem všech tří formátů, příklady i anti-patterny. Vychází průběžně od roku 2013 a dokončená není: k datu psaní uvádí Leanpub 70 % obsahu, poslední aktualizaci ze srpna 2021 a glosář zhruba ze dvou pětin.
- eventstorming.com – oficiální web techniky, kde Brandolini publikuje šablony, fotografie z workshopů a aktuální postupy.
- Stefan Hofer & Henning Schwentner – Domain Storytelling: A Collaborative, Visual, and Agile Way to Build Domain-Driven Software (Addison-Wesley, 2021). Komplexní kniha o Domain Storytellingu s notací, příklady a integrací s DDD.
- egon.io – open-source webový nástroj pro Domain Storytelling. Drag-and-drop editor, export do SVG.
- Vaughn Vernon – Domain-Driven Design Distilled (Addison-Wesley, 2016), kapitola 7 obsahuje stručný úvod do Event Stormingu jako součásti DDD strategie.
- Eric Evans – Domain-Driven Design: Tackling Complexity in the Heart of Software (Addison-Wesley, 2003). Kniha, ze které DDD vychází; Ubiquitous Language a Bounded Context jsou základem všech workshopových technik.
- ddd-crew – EventStorming Glossary & Cheat Sheet – nejúplnější veřejná legenda notace včetně formátů, z dílny ddd-crew, licence CC BY-SA 4.0.
- Alberto Brandolini – Remote EventStorming – stanovisko autora k online workshopům, odstupňované podle formátu.
- Vlad Khononov – Learning Domain-Driven Design (O'Reilly, 2021), kapitola 12 shrnuje Event Storming v deseti krocích z pohledu praktika, který techniku nasazuje u zákazníků.
- Evelyn van Kelle, Gien Verschatse, Kenny Baas-Schwegler – Collaborative Software Design (Manning, 2024). O facilitační vrstvě, kterou Event Storming předpokládá, ale neučí: ranking v místnosti, kognitivní zkreslení, práce s odporem.
- Nick Tune, Jean-Georges Perrin – Architecture Modernization (Manning, 2024) – Big Picture EventStorming jako jeden ze čtyř nástrojů modernizace, vedle Wardley Mappingu a Team Topologies.
- Event Modeling – sesterská technika Adama Dymitruka (2018) s pouze dopřednou časovou osou a UI vrstvou. Pro návrh event-sourced systému bližší nástroj než Software Design EventStorming, viz kapitola Event Sourcing.
- Oficiální Miro šablony Brandoliniho na Miroverse: Process Modelling a Software Design.