Implementing Event Handling
1. Designing Domain Events
| Aspect | Detail |
|---|---|
| Naming | Past tense: OrderPlaced, PaymentCaptured |
| Immutable | Use records |
| Self-contained | Include all data needed by consumers |
| Versioned | Schema evolution |
Example: Domain event record
public record OrderPlaced(
UUID orderId, UUID customerId, Money total, Instant occurredAt) {}
2. Implementing Event Publisher
Example: Spring ApplicationEventPublisher
@Service @RequiredArgsConstructor
public class OrderService {
private final ApplicationEventPublisher events;
@Transactional
public Order place(Cart c) {
var o = repo.save(Order.from(c));
events.publishEvent(new OrderPlaced(o.id(), c.customerId(), o.total(), Instant.now()));
return o;
}
}
3. Implementing Event Subscriber
Example: @EventListener
@Component
public class OrderEmailListener {
@TransactionalEventListener(phase = AFTER_COMMIT)
@Async
public void on(OrderPlaced e) { mail.sendConfirmation(e.orderId()); }
}
4. Implementing Event Bus
| Type | Use |
|---|---|
| In-process | Spring events, Guava EventBus |
| Distributed | Kafka, RabbitMQ, NATS |
| Cloud | SNS+SQS, EventBridge, Pub/Sub |
5. Handling Async Event Processing
| Concern | Detail |
|---|---|
| Decoupling | Publisher doesn't wait |
| Failure isolation | Subscriber failure doesn't break publisher |
| Order | Use partitioning if order matters |
| Backpressure | Bounded queues |
6. Implementing Event Sourcing
| Concept | Detail |
|---|---|
| Append-only log | Events are source of truth |
| Aggregate state | Replay events to rebuild |
| Snapshot | Periodic state checkpoint |
| Projections | Read models from events |
7. Implementing Event Replay
Example: Rebuild aggregate
public Order load(UUID id) {
var order = new Order();
eventStore.streamFor(id).forEach(order::apply);
return order;
}
8. Designing Event Schema
| Field | Detail |
|---|---|
| eventId | UUID, deduplication |
| eventType | FQN or topic name |
| version | Schema version |
| occurredAt | UTC timestamp |
| aggregateId | Source entity ID |
| data | Payload |
| metadata | Correlation, causation, actor |
9. Implementing Event Versioning
| Strategy | Detail |
|---|---|
| Additive only | New fields optional |
| Upcasters | Transform v1 → v2 on read |
| Schema registry | Avro/Protobuf with compat checks |
| Multiple versions | Publish both temporarily |
10. Handling Event Ordering
| Mechanism | Detail |
|---|---|
| Single partition per key | Kafka partition key = aggregateId |
| Sequence number | Per-aggregate monotonic |
| Vector clock | Distributed causal order |
| Re-order at consumer | Buffer and sort by seq |
11. Implementing Event Deduplication
| Approach | Detail |
|---|---|
| Inbox table | Persist eventId; skip duplicates |
| Idempotent handlers | Naturally safe to replay |
| Bloom filter | Memory-efficient probabilistic |
| Kafka exactly-once | Transactional producer + consumer |
12. Implementing Event Audit Trail
| Field | Detail |
|---|---|
| published_at | Producer timestamp |
| consumed_at | Per-consumer timestamp |
| handler_status | success / failed / dlq |
| retry_count | Attempts before success/DLQ |