Events, By the Book - the North Star
The platform's event architecture, judged against Brandolini's Introducing EventStorming, Evans' Domain-Driven Design, and Dilger's Understanding Eventsourcing - all read in full against the as-built system (164 registered types, verified 2026-08-29). This page is the standing north star for how the event model evolves; the interventions below are discussed in the design review and adopted through workshops with domain experts.
Scorecard: 7 right · 16 partial · 2 wrong · 6 missing across 31 extracted principles. Headline: the mechanics are unusually strong - several exceed what the books ask. The method is inverted: events were discovered by reading code, and every "missing" verdict traces to that inversion.
Provenance, stated honestly
The initial service and API shapes were deliberately taken from Socotra - a mature insurance-platform model (the locator scheme and policy/quote/claims surfaces conform to it). In Evans' vocabulary that is a considered Conformist relationship with a knowledge-rich upstream: a stable skeleton, with our own domain language growing on top as we learn. The event layer is where that learning lives, and the spec is iteratively refinable precisely because consumers depend on facts, not internals.
Where we are right
| Principle | Source | Why |
|---|---|---|
| The registry is a real Published Language | Evans Ch. 14 pp. 374-377 | Schema, versioning, producers/consumers, docs and goldens per type - Open Host Service done properly |
| Test-protected boundaries, beyond the book | Evans pp. 356-358 | Goldens in CI + e2e gate + runtime verdicts: drift is a red verdict on a live walk, not an incident |
| Behavior-first, no shared database | Brandolini p. 66 | Facts are the only inter-service contract; per-consumer projections translate at the boundary |
| Not event-sourcing is orthodox | Evans Ch. 6 | Mutable rows + published facts is the book's shape; argue any change from a concrete need, never purity |
| Choreography, no orchestrator | Dilger p. 504 | "Simply act on the facts in the system. That's it." |
| Buy-generic distillation in practice | Evans Ch. 15 | Novu, Temporal, Keycloak, Frappe - effort goes to the differentiating core |
| The rename machinery the books beg for | Brandolini p. 186 | schemaVersion + priorVersions + tested goldens, built before the naming debt came due |
Where we are wrong
- The discovery inversion (missing) - zero workshops at any altitude; events from
grep. Brandolini: "I just don't trust the official version" (p. 85); reverse narrative typically uncovers 30-40% more system (p. 97). Every other finding is a symptom of this one. - The internal model is the interchange medium (wrong; Evans p. 375) - event-carried state freezes the complete internal row, pinning producers' shapes and making every consumer a conformist. Dilger lands the same blow from privacy: fat events scatter PII ("the simplest data to handle is the data that doesn't exist", p. 554). Fix: slim state to the consumer-verified field set - computable from the registry.
- Commands unmodelled (missing) - only outcomes are published; intent survives nowhere but the frontend's activityName. Name the triggering operation per type; validate at invariant-heavy boundaries. No command bus needed.
- No context map, noun test never run (missing; Evans p. 345) - what do policy, coverage, member mean per service? Enrollment fuses quoting and policy-admin; identity's 49 onboarding types are a context in hiding. First cut is nearly free from registry edges.
- All 164 events implicitly public (partial; Dilger Ch. 5) - no integration/internal/audit classification; everything carries public-contract weight and feeds the warehouse.
- Choreography's reliability half missing outside billing/eligibility (partial) - consumers can bury failures with lag at zero; no work-owed recomputation. Dilger's processor-TODO-list is the self-healing answer, and the books' consensus highest-leverage fix.
- Read models by aggregation, not projection (partial; Dilger p. 261) - PAS REST-aggregates, timeline pull-aggregates, though every entity already arrives pre-calculated. The PAS field audit was Dilger's information-completeness check (p. 60), done manually, unnamed, unrepeatable.
- Strategic layer undocumented (partial) - no pivotal-event phase structure (computable: rank by consumer count), no core-domain vision page, no named aggregates/invariant owners.
- GDPR forget-me (missing; Dilger Ch. 41) - the live warehouse-erasure gap has a book playbook: minimalism, crypto-shredding ("delete the encryption key", p. 558), and a
party.data_purgedpurge-propagation event. - Upcast notes are prose, not code (partial; Dilger Ch. 28) - ship tested upcasters with the registry so consumers only ever see the latest version.
The plan
| # | Move | Cost |
|---|---|---|
| P1 | Big Picture storm on quote-and-buy, run blind, then diff wall vs registry - divergences are the findings; Socotra-derived language that loses to expert language becomes renames | half a day |
| P2 | Reverse-narrative walks from policy.activated and claim.paid | 2h each |
| P3 | Noun test then context map from registry edges; settle enrollment split + identity boundary on paper | 2 sessions |
| P4 | Registry fields: audience, command, policy names per consumer edge | days, mechanical |
| P5 | State minimalism per consumer-verified fields (identity and party types first) | incremental |
| P6 | Forget-me pipeline: party.data_purged + warehouse purge + crypto-shred | the real project |
| P7 | Reliability floor: durable inbox standard + work-owed processor + alerting on red verdicts | pattern exists ×2 |
| P8 | Executable upcasters, then rename quote.updated, element.added | small |
The rule going forward
New event types are storm-validated with domain experts before registration; emit-site sweeps become a drift detector, never a discovery mechanism. The registry stays the living model - Brandolini's paper roll dies ("we seldom look back to the model", p. 133); ours is enforced in CI, e2e and at runtime, so it cannot drift silently. What changes is the intake.
