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.