public class AccountProjection { private long balance = 0; public void apply(DepositedEvent e) { balance += e.amount; } public void apply(WithdrawnEvent e) { balance -= e.amount; } public static AccountProjection rebuild(List<Event> events) { AccountProjection p = new AccountProjection(); for (Event e : events) p.dispatch(e); return p; }}
Optimization
Detail
Snapshots
Fold every N events; replay tail only
Materialized views
Persist projections for queries
Catch-up subscriptions
Subscribe from last position
4. Understanding CQRS Pattern
Side
Model
Command
Validates, mutates, emits events
Query
Read-optimized projections
Sync
Events propagate from write → read
Pros
Independent scaling and schema
Cons
Eventual consistency between sides
5. Implementing Event Bus
Implementation
Detail
In-process
Spring ApplicationEventPublisher, MediatR
Distributed
Kafka, RabbitMQ, NATS, EventBridge
Schema registry
Confluent, Apicurio for evolution
CloudEvents spec
Standardized envelope
6. Implementing Event Schema Evolution
Strategy
Detail
Additive only
New optional fields safe
Versioned event types
OrderCreatedV2
Upcaster
Transforms old events on read
Compatibility mode
Schema registry enforces BACKWARD
7. Handling Event Ordering and Causality
Guarantee
Mechanism
Per-aggregate order
Partition by aggregate id
Causal across aggregates
Vector clock / causal id
Total order
Single partition (limited throughput)
Out-of-order tolerance
Watermarks, late event handling
8. Implementing Event Deduplication
Method
Detail
Event id set
Track processed ids in DB / Redis
Idempotent handlers
Apply naturally idempotent
Producer dedup id
Kafka enable.idempotence=true
Window-based dedup
Fixed time window (Flink)
9. Understanding Domain Events vs Integration Events
Domain Events
Internal to bounded context
Rich domain language
Synchronous in-process
Coupled to internal model
Integration Events
Cross bounded context / service
Stable contract; versioned
Asynchronous over broker
Decoupled from internals
10. Implementing Outbox Pattern
Transactional Outbox
In single DB transaction: write entity + insert into outbox table
Background relay polls outbox or reads CDC log (Debezium)
Publish to broker (Kafka, RabbitMQ)
Mark outbox row as published / delete
Consumers dedup using event id
Note: Outbox guarantees no event lost when DB commits, eliminating dual-write inconsistency.