Skip to main content

Module 3: Event-Driven Architecture: Case Studies

These case studies focus on event semantics, delivery guarantees, ordering, sagas, and the operational cost of asynchronous systems.


Case Study 1: Transactional Outbox For Order Events

Scenario: Checkout writes an order row and publishes OrderCreated to a broker. The database commit succeeds, the broker publish fails, and fulfillment never hears about the order.

Source anchor: Chris Richardson's Transactional Outbox, which addresses updating a database and sending messages without a distributed transaction.

Module concepts: outbox, dual-write failure, relay, idempotent consumer, at-least-once delivery.

Wrong Approach

Write to the database and broker in the same function and hope both succeed.

Better Approach

Use the database as the atomic boundary:

Transaction:
insert order
insert outbox event
commit

Relay:
reads unsent outbox rows
publishes to broker
marks sent or relies on dedupe

Tradeoff Table

ChoiceGainCost
direct publishsimplelost events on partial failure
outboxatomic DB/event recordrelay and dedupe complexity
2PCatomic across resourcesblocking/ops complexity
change data capturelow app couplingCDC infrastructure

Required Artifact

Design an outbox table, relay retry policy, dedupe key, and consumer idempotency rule.


Case Study 2: Kafka Exactly-Once Is Not End-To-End Exactly Once

Scenario: A payments event processor uses Kafka idempotent producers and transactions. The team assumes duplicate charges are impossible.

Source anchor: Apache Kafka producer configuration docs, including transactional.id and idempotence behavior. See Apache Kafka producer configs.

Module concepts: idempotent producer, transactional producer, exactly-once semantics, external side effects.

Wrong Approach

"Kafka exactly-once means our whole business workflow is exactly-once."

Kafka can provide guarantees inside Kafka's read-process-write boundary. External APIs and databases still need idempotency.

Better Approach

Draw the end-to-end boundary:

Kafka input topic
-> processor
-> Kafka output topic: transactional
-> payment provider API: needs idempotency key
-> database write: needs unique operation key

Tradeoff Table

ChoiceGainCost
at-least-once + idempotent consumerrobust and explicitdedupe storage
Kafka transactionsstronger stream processingconfig/operational complexity
external idempotency keysprotects side effectsAPI/storage contract
assume exactly onceeasy storyduplicate business effects

Required Artifact

Write an end-to-end processing contract that identifies every duplicate boundary.


Case Study 3: Event Notification vs Event-Carried State

Scenario: CustomerUpdated contains only customer_id. Every consumer calls Customer Service to fetch details, creating a thundering herd during batch updates.

Source anchor: Martin Fowler's Event-Driven article distinguishes event notification from event-carried state transfer.

Module concepts: event notification, event-carried state, coupling, consumer autonomy.

Wrong Approach

Make every event tiny without considering consumer needs.

Better Approach

Choose payload shape by consumer contract:

Notification:
customer_id only, consumers fetch current state

Carried state:
customer_id, email, plan, status, version

Tradeoff Table

ChoiceGainCost
notificationsmall eventsproducer service read dependency
carried stateconsumer autonomyduplicated data and schema evolution
full snapshoteasy projectionlarge events
delta eventcompactharder replay

Required Artifact

Write an event schema ADR comparing notification, delta, and carried-state payloads.


Case Study 4: Saga For Trip Booking

Scenario: A trip booking workflow reserves flight, hotel, and payment. Hotel reservation fails after flight succeeds. The system cannot use one ACID transaction across providers.

Source anchor: Microsoft Azure's Compensating Transaction pattern describes undo operations, idempotency, and resumable workflows.

Module concepts: saga, compensation, orchestration, choreography, idempotency.

Wrong Approach

Pretend a distributed workflow is a local transaction.

Better Approach

Use an explicit saga:

ReserveFlight -> ReserveHotel -> ChargeCard

If hotel fails:
CancelFlight
ReleasePaymentHold

Every step and compensation gets an idempotency key and persisted state.

Tradeoff Table

ChoiceGainCost
2PCatomicitypoor provider fit
saga orchestrationvisible workflowcoordinator state
choreographyservice autonomyharder global reasoning
manual reviewhandles rare ambiguityops burden

Required Artifact

Draw a saga state machine with compensations, retry rules, timeout behavior, and manual review states.


Case Study 5: Partition Ordering In A Stream

Scenario: Inventory events are partitioned by event ID. Two updates for the same SKU land in different partitions and are processed out of order.

Source anchor: Kafka's documentation explains producers, partitions, and key-based partitioning behavior through configuration and client docs. See Apache Kafka documentation.

Module concepts: partition key, ordering guarantee, consumer group, hot partitions.

Wrong Approach

"Kafka preserves order" without saying within what scope.

Better Approach

Partition by the entity whose order matters:

Need order per SKU:
partition key = sku

Need order per customer:
partition key = customer_id

Need global order:
one partition or external sequencer, with scale cost

Tradeoff Table

ChoiceGainCost
key by event IDeven distributionno entity order
key by SKUper-SKU orderhot SKU risk
one partitiontotal orderpoor throughput
repartition streamfixes downstream shapeextra pipeline

Required Artifact

Write a partition-key decision note: order scope, skew risk, partition count, rebalance behavior, and consumer idempotency.


Source Map

SourceUse it for
Transactional Outboxatomic DB update plus event publication
Apache Kafka producer configsidempotence and transactional producer settings
Martin Fowler: Event-Drivenevent notification vs carried state
Azure Compensating Transactionsaga compensation and idempotency
Apache Kafka documentationpartitions, ordering, producers, consumers

Completion Standard

  • At least three artifacts are completed.
  • At least one artifact includes outbox schema and relay behavior.
  • At least one artifact identifies an ordering scope.
  • At least one artifact includes saga compensation.