Designs event-driven systems the way that survives production. Triggers when building, reviewing, or fixing event-driven architectures, Kafka/topic design, event modeling, schema registries, ordering, idempotency, DLQs, sagas, or transactional outboxes.
./install.sh event-driven-designname: event_driven_design description: Designs event-driven systems the way that survives production. Triggers when building, reviewing, or fixing event-driven architectures, Kafka/topic design, event modeling, schema registries, ordering, idempotency, DLQs, sagas, or transactional outboxes.
Turn "make it async" into a concrete, safe design: well-modeled events, a schema registry with explicit compatibility rules, ordering via partition keys, idempotent consumers, and DLQs that actually catch poison messages. Covers Kafka, RabbitMQ, SQS/SNS, EventBridge, Pub/Sub, and local event buses.
Run a lightweight event-storm: list what happens in the domain, one fact per line, named [Subject][PastTenseVerb] (OrderPlaced, PaymentProcessed). Separate facts (past tense) from commands (imperative: PlaceOrder) and policies ("whenever X, then Y"). Events are immutable facts; all state transitions get an event.
Run bin/edd-lint.py on your event list to catch naming violations before they fossilize.
Every event carries the same envelope: event_id (uuid, the dedup key), event_type (orders.placed.v1), event_time (UTC ISO-8601), source, subject (the entity the event is about), plus metadata: correlation_id, causation_id, trace_id. Version the event type with a suffix.
Register Avro / Protobuf / JSON Schema per subject (topic-value naming), with an explicit compatibility mode per subject. See references/schema-evolution.md for the change matrix and the upgrade-order rule. Breaking schema changes must fail at registration time, never at runtime.
Ordering exists only within a Kafka partition, a Kinesis shard, or a FIFO message group. Design the partition key so related events share one: order_id, user_id, tenant_id. More partitions = more parallelism; max consumer parallelism = partition count. If ordering matters cross-key, it does not exist: make consumers tolerate out-of-order arrival (partial records, reconcile later).
At-least-once delivery is the default; duplicates WILL arrive. Dedup on event_id in a store with a uniqueness constraint: seen before means skip, else process and record. Producer side: enable.idempotence=true, acks=all. End-to-end exactly-once needs transactions: use it only where the cost is justified (payments), not everywhere.
Transactional outbox: write the event to an outbox table in the same DB transaction as the business state; a relay publishes to the broker. Never dual-write. Every subscription gets a DLQ with bounded retries (exponential backoff + jitter), alerting on DLQ depth, and a documented replay path. Commit consumer offsets only after durable processing.
Simple flows (3-4 steps, multiple independent consumers): choreography. Complex multi-step sagas with coordinated rollback: orchestration. Common hybrid: orchestrate inside a bounded context, choreograph between them.
When asked to design or review an event-driven system, return:
publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.