Skip to content
Harjot Singh Rana
Back to blog
ArchitectureEvent-DrivenRedisGo9 min read

Event-Driven Architecture: Patterns for Real-Time Systems

March 5, 2024

Why event-driven

Event-driven architecture decouples producers from consumers. When a user places an order, the order service emits an event. The inventory service, notification service, and analytics service each consume that event independently. No direct coupling, no cascading failures.

The event bus

The event bus is the backbone of any event-driven system. We chose Redis Streams over Kafka for our use case because:

  • Lower operational complexity (we already ran Redis)
  • Sub-millisecond latency for most operations
  • Built-in consumer groups for load balancing
  • No JVM dependency

Event sourcing

Instead of storing the current state, store every event that led to that state. The current state is derived by replaying events. This gives you a complete audit trail and the ability to reconstruct state at any point in time.

CQRS (Command Query Responsibility Segregation)

Separate the write model from the read model. Commands go through the event-sourced write path. Queries read from optimized materialized views:

// Command side
type CreateOrderCommand struct {
    UserID    string
    Items     []OrderItem
    Total     Money
}

// Event
type OrderCreated struct {
    OrderID   string
    UserID    string
    Items     []OrderItem
    Total     Money
    CreatedAt time.Time
}

// Query side (materialized view)
type OrderSummary struct {
    OrderID    string
    Status     string
    ItemCount  int
    Total      float64
    CreatedAt  time.Time
}

Handling 2M+ events per minute

At this scale, every microsecond matters:

  • Batch writes to the event store
  • Use protobuf for serialization (smaller payloads, faster parsing)
  • Partition by event type for parallel processing
  • Implement backpressure at every consumer
  • Monitor consumer lag as a first-class alert

Lessons learned

  • Start with a simple event bus, not a complex event platform
  • Schema evolution is harder than you think — use schema registry from day one
  • Idempotent consumers are worth the extra effort
  • Dead letter queues are not optional
Built with Moonshift