Event Storming a Domain Storytelling
Event Storming Alberta Brandoliniho a Domain Storytelling Stefana Hofera a Henninga Schwentnera. Dvě workshopové techniky, jak vývojáře a doménové experty dovést ke společnému modelu dřív, než vznikne první řádek kódu.
Obsah kapitoly
Kapitola ve videokurzu
Celý videokurzPřehrávač YouTube se načte až po kliknutí.
DDD nezačíná u kódu, ale v místnosti, kde proti sobě sedí lidé, kteří píší kód, a lidé, kteří doménu provozují. Event Storming Alberta Brandoliniho (2013) a Domain Storytelling Stefana Hofera a Henninga Schwentnera (2016) jsou dvě techniky, jak v takové místnosti strávit několik hodin až celý den a odejít s něčím, co se dá zítra otevřít v IDE. Obě vytahují na světlo nevyslovené doménové znalosti, každá jinou cestou, a liší se tím, kdy se hodí a jak se vedou.
04.01 Proč workshop, proč ne čtení dokumentace#
Vývojářský tým, který má zahájit nový projekt nebo přepsat existující, obvykle reaguje „dejte nám specifikaci a my to naprogramujeme“. Specifikace ale typicky neexistuje v použitelné podobě. Jsou tu wiki stránky staré tři roky, e-mailová vlákna, issue tracker s 1 800 tickety a čtyři lidé, kteří „to vědí“. Žádný z těch zdrojů není autoritativní: každý zachycuje doménu z jiného úhlu a v jiné době a často si navzájem odporují.
To je v pořádku. Doména žije v hlavách doménových expertů jako znalostní síť a jako knihu ji přečíst nejde. 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. Na workshopu se takové rozpory ukážou hned a řeší se společně. Wiki je nikdy neukáže, protože vždy zachytí pohled toho, kdo ji psal.
Eric Evans v Domain-Driven Design (2003) staví Ubiquitous Language na mluvené řeči: model se tříbí tím, že o něm tým mluví nahlas, a dokumenty řeč a kód jen doplňují. Brandolini, Hofer a Schwentner k tomu přidávají praktickou metodu: konkrétní notaci, harmonogram a role v místnosti.
04.02 Event Storming – co to je a co umí#
Event Storming zavedl italský konzultant Alberto Brandolini v roce 2013. Účastníci lepí na dlouhou stěnu (nebo Miro/Mural board) oranžové sticky notes s doménovými událostmi v minulém čase. Postupně z nich vzniká časová osa toho, co se v doméně děje. S rostoucí osou přibývají další barvy: modrá pro Commands, žlutá pro Actors, růžová pro Hot Spots.
Technika vznikla z praktické potřeby. 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 cestu shrnuje sám: z náhrady za diagram se stala učební pomůcka a nakonec platforma pro společné 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ší názvy Process Level a Design Level.
- Big Picture Event Storming – 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 15–30 lidí, typicky 25–30; 2–4 hodiny je zkrácená varianta této knihy pro menší doménu.
- Process Modelling Event Storming (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 Event Storming (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í návrh API. Trvání 2–6 h, typicky na jeden 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ěnuje mu až poslední kapitolu, za agregáty a doménovými událostmi. Na začátek práce ho staví tato 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. Uvnitř jednoho workshopu má každá barva jediný význam. Kdo začne improvizovat, ztratí 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ů. Legenda proto patří před workshopem na stěnu, ať se skupina nedohaduje o významu barvy místo o doméně.
Dvě položky tabulky potřebují komentář. Zelená má 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í. Velká žlutá lepka se v glosáři ddd-crew jmenuje Constraint, ne Aggregate. Důvod je jazykový: 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 udělá (stejně tak při migraci z existujícího CRUD systému, viz kapitola o migraci). Cílem není dokonalý model, ale společná mapa toho, co se v doméně děje, a 3–7 identifikovaných Bounded Contextů.
04.04.1 Příprava (-1 týden)
Bez přípravy workshop nefunguje:
- 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 dále potřebuje okno, které jde otevřít; skupina dvaceti lidí vydýchá vzduch dřív, než se čeká. Online varianta potřebuje frame 12 000 × 4 000 px v Miru nebo Muralu.
- Úč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 jedné papírové role. Velká skupina se neřeší ubíráním lidí; u stěny se 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é propisky, jejich text není z 2 m čitelný).
- Catering. Káva, voda, ovoce, oběd. Workshop unavuje a bez jídla energie padá 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. Každý dostane zhruba 15 oranžových stickies a píše události, které ho napadnou. Lepí kamkoliv bez pořadí. Chaos je záměrný: lidé si mají vzpomenout na všechno, ne hned strukturovat. 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,PaymentSucceeded,ShipmentDispatched,OrderClosed. Každý se označí svislou čarou přes celou výšku stěny; čáry rozdělí časovou osu 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 jich bývá 8–15. Jsou nejcennějším výstupem 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 závisí na tom, zda hranici poznáte, když na ni narazíte. Na stěně nejspolehlivěji fungují čtyři heuristiky:
- 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. Tři definice jednoho slova jsou silné vodítko, že na stěně leží 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. Kde si firma předává práci (prodej → sklad → účtárna), mění se obvykle slovník i pravidla. Slepě je ale přebírat nejde; organizační schéma bývá historické, ne doménové.
- Vlastnictví dat. Otázka „kdo smí tohle pole změnit?“ má uvnitř jednoho kontextu jedinou odpověď. Mění-li cenu produktu 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 sama nerozhoduje. Hledají se místa, kde se jich protne víc najednou – lingvistický šev na hranici oddělení, kde se navíc mění vlastník 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 událostmi.
- 3–7 identifikovaných pivotal eventů.
- 3–7 vyznačených Bounded Contextů.
- 8–15 hot spotů jako budoucí tickety.
- Foto / Miro export.
Co na konci není a být nemá: 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 formátu. Brandolini to rozepsal v textu Remote EventStorming (březen 2020). Software Design online snese nejvíc: malý rozsah, 90 minut, technické publikum. Process Modelling funguje podmíněně: půlden, 5–15 lidí, tým 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í. Radí také remote sezení vůbec nenazývat EventStormingem, aby si tým se jménem techniky nespojil špatnou zkušenost.
Online Big Picture se přesto dělá tam, kde doménoví experti sedí ve třech městech a alternativou není offline workshop, ale žádný. Následující postup je vědomý kompromis se známou cenou:
- Frame 12 000 × 4 000 px. Týmy velikost plátna často podcení. Big Picture na 50+ eventů potřebuje hodně horizontálního prostoru, jinak se stickies různých účastníků začnou překrývat. V Miru založte nový board a prvnímu framu nastavte tyto rozměry v parametru 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 diskuse nad hot spoty.
- Breakout místnosti. Ve fázi Pivotal Events rozdělte skupinu do 2–3 breakout místností po 4 lidech. Každá skupina pracuje v Miru na jednom úseku časové osy. Po 20 minutách se skupiny vrátí do hlavní místnosti a sladí výsledky. Bez breakoutů se online workshop zúží na jednoho aktivního účastníka 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ě. Má-li tým strach z konfrontace nebo silně hierarchickou kulturu, musí tahle bariéra padnout nejdřív. Jinak workshop vyrobí 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 bere vždy jeden BC a zhušťuje ho do struktury, která se v Symfony přeloží na Command třídy, Handlery a Eventy 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 na jeden 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 → PaymentSucceeded (event)
Sekvence ještě není kód, je to mapa pro implementaci. Už z ní ale je vidět, co bude potřeba:
- Application Service
PlaceOrderHandlerv Ordering BC. - Process Manager, který koordinuje
OrderPlaced → ReserveStock → ChargeCardpřes hranice BC (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, externích systémů a read modelů.
- 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 blíží kódu. Pro každý Bounded Context hledá agregáty, jejich invariantní pravidla a způsob, jakým commands mění 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 odrážky 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 na jeden 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? Je to záměrný dárek, nebo chyba?“).
04.06.3 Převod z workshopu do Symfony
Workshop: Customer → PlaceOrder → Order Aggregate → OrderPlaced
Náčrt v Symfony / PHP (první verze, ne finální kód):
1<?php2 3// Application/Command/PlaceOrder.php4namespace App\Ordering\Application\Command;5 6use App\Ordering\Domain\ValueObject\CustomerId;7 8final readonly class PlaceOrder9{10 public function __construct(11 public CustomerId $customerId,12 /** @var list<OrderItemDto> */13 public array $items,14 ) {}15}16 17// Domain/Model/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 InvalidOrderStateTransitionException::notAllowedInState(55 'přidání položky',56 $this->status->value,57 );58 }59 60 $this->items[] = new OrderItem($productId, $quantity, $unitPrice);61 }62 63 public function confirm(): void64 {65 // Invariant z workshopu: confirm jen ze stavu Draft66 if ($this->status !== OrderStatus::Draft) {67 throw InvalidOrderStateTransitionException::cannotTransition(68 $this->status->value,69 OrderStatus::Confirmed->value,70 );71 }72 73 // Invariant z workshopu: objednávka musí mít aspoň jednu položku74 if ($this->items === []) {75 throw EmptyOrderException::cannotConfirm();76 }77 78 $this->status = OrderStatus::Confirmed;79 $this->record(new OrderConfirmed($this->id, $this->customerId, new \DateTimeImmutable()));80 }81 82 // Náčrt z workshopu bez zámku ságy; kanonický přechod viz Návrh agregátu.83 public function cancel(string $reason, \DateTimeImmutable $when): void84 {85 // Invariant z workshopu: odeslanou ani doručenou objednávku zrušit nejde86 if (in_array($this->status, [OrderStatus::Shipped, OrderStatus::Delivered], true)) {87 throw InvalidOrderStateTransitionException::cannotTransition(88 $this->status->value,89 OrderStatus::Cancelled->value,90 );91 }92 93 $this->status = OrderStatus::Cancelled;94 $this->record(new OrderCancelled($this->id, $this->customerId, $reason, $when));95 }96 97 public function totalAmount(): Money98 {99 if ($this->items === []) {100 throw EmptyOrderException::cannotBePlaced();101 }102 103 $total = Money::zero($this->items[0]->unitPrice->currency);104 105 foreach ($this->items as $item) {106 $total = $total->add($item->unitPrice->multiply($item->quantity));107 }108 109 return $total;110 }111}112 113// Application/Handler/PlaceOrderHandler.php114namespace App\Ordering\Application\Handler;115 116use App\Ordering\Application\Command\PlaceOrder;117use App\Ordering\Domain\Model\Order;118use App\Ordering\Domain\Repository\OrderRepository;119use App\Ordering\Domain\ValueObject\OrderId;120use Symfony\Component\Messenger\Attribute\AsMessageHandler;121 122#[AsMessageHandler(bus: 'command.bus')]123final readonly class PlaceOrderHandler124{125 public function __construct(126 private OrderRepository $orders,127 ) {}128 129 public function __invoke(PlaceOrder $cmd): OrderId130 {131 // Identitu přiděluje aplikace, ne databáze - viz Základní koncepty.132 $order = Order::place(OrderId::generate(), $cmd->customerId);133 134 foreach ($cmd->items as $item) {135 $order->addItem($item->productId, $item->quantity, $item->unitPrice);136 }137 138 $this->orders->save($order);139 140 return $order->id;141 }142}
Každý prvek z workshopu má v kódu protějšek. Command sticky → PlaceOrder. Constraint (agregát) → třída Order. Invariant z odrážky → 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ě barva není:
- Kdy se události publikují. Ukázka je jen zaznamenává do agregátu. Synchronní dispatch na
event.busuvnitř transakce command busu je v pořádku, rollback vrátí i zápisy posluchačů. Co opouští proces (broker, e-mail, cizí služba), jde přes Outbox Pattern. Přímé odeslání ven hned zasave()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á jediné rozhraní
MessageBusInterface; oddělená command a event sběrnice je věc konfiguraceframework.messenger.busesa aliasů, podrobně v kapitole CQRS.
04.07 Domain Storytelling – alternativa pro malé týmy#
Domain Storytelling popsali Stefan Hofer a Henning Schwentner v knize stejného jména (Addison-Wesley, 2021). Technika je starší. Tímto jménem ji autoři označují od roku 2016, kořeny ale sahají do 90. let na Univerzitu Hamburk, kde se jí říkalo Kooperationsbild a později exemplarische Geschäftsprozessmodellierung. Doménové znalosti získává jinou cestou než Event Storming: místo časové osy událostí se kreslí příběh o práci doménového experta ve standardizované piktogramové notaci.
04.07.1 Notace
- Actor – panáček. 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). Jako praktické vodítko se jeden příběh drží v rozmezí pěti až patnácti kroků. Delší se dělí na dva.
04.07.2 Rozsah – jaký příběh vlastně kreslíte
U domain story bez určeného rozsahu 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
Příběh „Customer places an order“ v notaci Domain Storytellingu, č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. Kroky 3, 6 a 7 se kreslí dvěma šipkami (od actora k work objectu a od work objectu k druhému actorovi), číslo ale nese celá aktivita. Kresba je úmyslně jednoduchá: ručně nakreslené piktogramy nebo nástroj egon.io (open source, v prohlížeči).
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ělký, ale široký | Hluboký, ale úzký |
| Hlavní výstup | Bounded Contexty + eventy | Sekvence kroků s actory a work objecty |
| 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 se obě techniky doplňují. Event Storming ukáže, jaké procesy v doméně existují (širokoúhlý objektiv), Domain Storytelling v každém z nich odkryje detail (teleobjektiv). Autoři doporučují kombinaci: Big Picture pro strategický přehled, Domain Storytelling pro jednotlivé hlavní procesy a Process Modelling se Software Designem pro implementaci.
04.07.5 egon.io v praxi krok za krokem
egon.io je open-source webová aplikace (Angular nad diagram-js), která implementuje celou notaci Domain Storytellingu. Hodí se týmu, který nechce kupovat licence Miro ani tahat papír. Postup pro první sezení:
- Otevřete egon.io v prohlížeči – nevyžaduje registraci. Vlevo nahoře je panel nástrojů s ikonami: 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 aktivitou. Klikněte na actora a táhněte na work object; egon.io vytvoří očíslovanou šipku. Slovesné jméno (browses, submits, confirms) se píše do popisku šipky.
- Buďte struční. Jeden diagram má nést jeden lineární příběh s 5–15 aktivitami. Když jich máte 30, rozdělte ho na dva.
- 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, takže git ukáže rozdíly a změny jsou vidět v PR review.
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 jde 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 vzorů a anti-vzorů; kde se s ním následující anti-vzory kryjí, je uvedeno kanonické jméno.
04.09 Co Event Storming neumí#
Anti-vzory výše způsobují lidé. Následující limity má technika sama, i když se vede 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. Notace pro ně nemá barvu a workshop je systematicky přehlíží. Stojí-li na nich architektura, patří do samostatného sezení; Event Storming je nenahradí.
Mapa žije, jen 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 nad stejnou doménou vytvoří se dvěma facilitátory dvě různé mapy. Technika sama žádnou korekci nemá. Mapu je proto dobré po pár týdnech otevřít znovu, nejlépe s někým, kdo u prvního workshopu nebyl.
Poslední limit je nejméně nápadný. Konsenzus dvaceti lidí, z nichž 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, o čem skupina ví, že to neví.
Nic z toho není důvod workshop vynechat, jen nečekat od něj hotovou specifikaci.
04.10 Po workshopu – co s výstupem#
Workshop bez follow-upu je promarněná investice. Do 24 hodin po jeho skončení musí do repa jít čtyři konkrétní artefakty.
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-shipping-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 událostí
Textový soubor s jedním eventem na řádek slouží jako reference pro budoucí PR. Vývojář, který přidává nový event, v něm ověří, zda už podobný neexistuje.
1# docs/discovery/2026-04-29-big-picture/events.md2 3## Ordering BC4- OrderPlaced5- OrderConfirmed6- OrderCancelled7- OrderItemAdded8- OrderItemRemoved9 10## Payment BC11- PaymentRequested12- PaymentSucceeded13- PaymentFailed14- PaymentRefunded15 16## Shipping 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í, nebo se účet zákazníka jen označí jako neuhrazený.7Potřebujeme jednoznačné rozhodnutí před implementací Process Manageru 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 nehádá, 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-shipping-area.jpg9│ │ ├── 99-miro-export.png10│ │ ├── events.md ← seznam doménových událostí (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│ └── Shipping/20└── ...
Adresář docs/discovery/ je append-only: staré workshopy se nemažou, přibývají jen 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/Shipping 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. Nový vývojář po otevření projektu vidí strukturu, která odpovídá fotce ze workshopu. Vazba mezi artefaktem v repu a artefaktem ze stěny brání tomu, aby se jazyk workshopu po půl roce z kódu vytratil.
04.10.6 První PR po workshopu
První pull request po workshopu má být malý a výslovně označený jako follow-up, ne velký commit s implementací první funkce. Obsahuje:
- 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 skript, který je vytvoří hromadně).
Žádný kód doménové logiky. PR má jediný úkol: uložit společnou paměť workshopu do repa, než ji všichni zapomenou. První agregát 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, když firma přejde na předplatné a ústředním eventem se stane SubscriptionRenewed. Bez nového workshopu se kód a doména 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 se požadavky na nové funkce 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. Kde se mapa změnila málo, je doména stabilní a kód 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 velká část mapy zůstává.
04.12 Most z workshopu do testů#
Software Design Event Storming přirozeně ústí v test-driven development. Každý invariant na sticky agregátu je jeden testovací případ, stejně jako každý hot spot, který se během workshopu vyřešil. Tým, který po workshopu nezačne psát testy podle invariantů, přichází o velkou část jeho hodnoty.
04.12.1 Převod 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 platba prošla (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', new \DateTimeImmutable());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 za půl roku selže, nový vývojář dohledá důvod pravidla přes git blame nebo podle data workshopu.
04.12.2 Doménové události jako testy
Sekvence Command → Event → Policy → Command z Process Modellingu je rovnou akceptační test:
1<?php2 3declare(strict_types=1);4 5namespace App\Tests\Ordering;6 7use App\Ordering\Application\Command\OrderItemDto;8use App\Ordering\Application\Command\PlaceOrder;9use App\Ordering\Domain\Event\OrderPlaced;10use App\Ordering\Domain\ValueObject\CustomerId;11use App\Ordering\Domain\ValueObject\ProductId;12use App\Payment\Application\Command\ChargeCard;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 // collectEvents(): testovací spy nad sběrnicí, který zaznamenává27 // odeslané eventy a commandy; jeho implementace je zde vynechána.28 $events = $this->collectEvents();29 30 $bus->dispatch(new PlaceOrder(31 CustomerId::generate(),32 [new OrderItemDto(ProductId::generate(), 1, new Money(100, Currency::CZK))],33 ));34 35 self::assertCount(1, $events->ofType(OrderPlaced::class));36 // Policy z workshopu: OrderPlaced ⇒ ChargeCard37 self::assertCount(1, $events->commands(ChargeCard::class));38 }39}
Přínosy jsou dva. Testy jsou čitelné pro doménové experty: pojmenování odpovídá workshopu, takže i nevývojář si test přečte a potvrdí, že vyjadřuje to, co měl na mysli. A testy chrání před regresí. Kdo za rok při refaktoringu omylem poruší invariant z workshopu, na test narazí.
Testovací strategii, doménové testy a integrační testy se Symfony Messengerem rozebírá kapitola Testování DDD.
04.13 Shrnutí#
Event Storming a Domain Storytelling jsou dvě prověřené techniky, jak před první řádkou kódu dostat doménu na společný papír. Obě vycházejí ze stejného předpokladu: doménové znalosti nejde přečíst, musí se objevit v dialogu.
- Event Storming ve třech formátech (Big Picture / Process Modelling / Software Design) mapuje doménu do šířky. Big Picture objevuje Bounded Contexty a pivotní události. Process Modelling zhušťuje jeden BC do sekvencí Command-Event-Policy. Software Design z nich vyvodí agregáty s invarianty.
- Domain Storytelling jde do hloubky jednoho procesu v malé skupině. Notace actor-work object-activity se čte bez zaškolení a hodí se tam, kde je Event Storming „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. Pokus vyřešit hot spot na místě zabere typicky 15–30 minut a obvykle skončí bez výsledku, 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ého Ubiquitous Language, který používají obě strany. Když ho zaplatí jen jedna, druhá ho nevezme vážně. Musí-li přesto platit jen jedna strana, 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ář. Jazyk „učesaný“ do spisovné podoby se ztratí a za rok zase nikdo neví, o čem se mluví.
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í příběh, debatujete krok za krokem. Za jedno sezení dostanete použitelný výstup pro jeden konkrétní proces. Jen si předem ujasněte rozsah 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 Event Storming 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 Event Storming, viz kapitola Event Sourcing.
- Oficiální Miro šablony Brandoliniho na Miroverse: Process Modelling a Software Design.