Design Patterns Concepts

Last updated on
4 min read

Table of Contents

Distributed Design Patterns

Saga Pattern

  • In distributed systems, a single business request often requires coordination across multiple microservices.
  • For example, for a single request like ordering a product, there are multiple steps involved. Such as payment processing, inventory updates, shipping etc. The distributed nature makes achieving ACID compliance difficult since all the individual services will have databases of their own and operate independently.
  • This design pattern helps in managing transaction updates across multiple services, by breaking it down into small local transactional updates called saga steps. Once a step is completed the next step in the sequence is triggered. If any of the steps in the sequence fail, compensating updates are done to undo the changes made by previous steps.
  • The pattern ensures eventual consistency rather than strict atomicity across services.
  • Types
    • Choreography
      • In this approach when a request is made, the first service in the sequence is triggered. Once it is completed it publishes an event. Other services listening for this event, perform their respective transaction and publishes an event. This chain of sequence continues until the saga is complete. If a service fails, a compensating event is published to trigger compensating transactions in previous services.
      • Example: Service A performs its task and emits an event. Service B listens to the event, does work, emits another event. Service C repeats the same thing. This repeats until all the steps in the sequence are done.
      • Pros - Good for simple workflows.
      • Cons - Hard to manage and debug issues. Tracing is difficult
    • Orchestration
      • In this approach an orchestrator coordinates the saga steps. The orchestrator tells each step when to execute its local transaction. If any local transaction fails, the orchestrator then invokes compensating transactions to roll back previous steps.
      • Example: Orchestrator sends a command to Service A. On successful completion, orchestrator sends a command to Service B. And this process continues until all steps are done. If any of the command fails, then a a compensating command is triggered to reverse the previous operations.
      • Pros - Easy to manage, monitor and trace.
      • Cons - Cons – Centralized control introduces a potential single point of failure.

CQRS (Command Query Responsibility Segregation) Pattern

  • It separates the read and write operations of a system into different models:
    • Command: Operations that change state (create, update, delete).
    • Query: Operations that read data (fetch, list).
  • The write model (command side) is responsible for handling requests that modify data, while the read model (query side) is optimized for retrieving data.
  • Benefits:
    • Allows independent scaling of read and write workloads.
    • Enables use of different data stores or schemas for reads and writes. For example, the write side may use a normalized relational database optimized for transactional integrity, while the read side can use a denormalized NoSQL database or cache for fast query performance. This flexibility allows each side to be tailored for its specific workload, improving scalability and responsiveness.
    • Facilitates event sourcing, where state changes are stored as a sequence of events.
  • Drawbacks:
    • Increased complexity due to maintaining separate models and synchronization.
    • Eventual consistency between read and write sides.
  • Typical use cases:
    • Systems with high read/write load imbalance.
    • Applications requiring audit trails or event sourcing.
    • Complex domains where read and write logic differ significantly.
  • Example:
    • In an e-commerce system, order creation (command) and order history retrieval (query) can be handled by separate models and databases.

Event Sourcing Pattern

  • Event Sourcing is a pattern where the state of an application is determined by a sequence of events rather than just the current state.
  • Instead of storing just the current state, all changes to application state are stored as a sequence of events in the order they occurred.
  • The current state can be recreated by replaying these events from the beginning.
  • Core concepts:
    • Events: Immutable records of something that happened in the system.
    • Event Store: Database that stores the complete sequence of events.
    • Event Stream: Sequence of events for a particular entity or aggregate.
    • Replay: Process of reconstructing state by processing historical events.
  • Benefits:
    • Complete audit trail and history of all changes.
    • Ability to reconstruct past states of the system.
    • Easy to implement temporal queries (what was the state at time X?).
    • Natural fit for event-driven architectures.
    • Helps in debugging by providing detailed history.
  • Challenges:
    • Event schema evolution (handling changes to event structure over time).
    • Performance considerations for systems with long event histories.
    • Learning curve and complexity in implementation.
    • Eventually consistent read models.
  • Common Use Cases:
    • Financial systems requiring audit trails.
    • Systems needing point-in-time reconstruction.
    • Applications with complex domain logic where history is important.
  • Example:
    • Banking system storing all transactions as events rather than just account balances. Each deposit, withdrawal, or transfer is stored as an event. Current balance is calculated by replaying all events.

References