Module A-12·27 min read

Aggregates, invariants, Ports & Adapters, and CQRS applied to banking ledgers — keeping domain logic clean under extreme architectural complexity.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

Module 11 — Enterprise Architecture for State-Heavy Systems: DDD & Clean Architecture

What this module covers: A blockchain indexer processing 50,000 events/second will accumulate complex business rules over time: transaction validity invariants, account balance constraints, settlement reconciliation logic, fraud detection rules. Without architectural discipline, these rules scatter across route handlers, database queries, and event listeners — untestable, fragile, and impossible to reason about under load. This module covers Domain-Driven Design applied to state-heavy systems, Clean Architecture for transport-agnostic domain logic, and CQRS for systems where the read model and write model need to evolve independently.


Why Architecture Matters More at Scale

At low throughput (< 1K events/sec), architectural shortcuts are invisible. Validation in route handlers, business logic in SQL queries, domain concepts scattered across layers — these are maintainability problems, not performance problems.

At high throughput, architectural shortcuts become performance problems:

javascript

Problems:

  • Untestable: you need a running HTTP server and database to test the business logic
  • Uncacheable: the business rules are coupled to the SQL and HTTP transport
  • Unreusable: the same logic cannot be reused by a gRPC endpoint or a Kafka consumer
  • Unrefactorable: changing the validation requires touching the route handler

Domain-Driven Design Core Concepts

DDD provides vocabulary and patterns for modeling complex business domains in code. For a payment system, the key concepts are:

Entities

Objects with identity that persists over time. An account is an entity — it has an ID, and its state changes.

typescript

Why this matters for high-throughput systems:

The invariants are in the domain object and enforced before any I/O happens. At 50K payments/second, you want to reject invalid payments with zero database roundtrips. The Account.debit() method checks balance without touching the database — if it throws, the payment is rejected before any SQL runs.

Value Objects

Immutable objects without identity. An Amount is a value object — two amounts of 100 USD are identical regardless of which "instance" they are.

typescript

Aggregates: The Consistency Boundary

An aggregate is a cluster of entities and value objects that must be changed together atomically. The aggregate root is the entry point — external code can only access the aggregate through the root.

An aggregate is the blast radius of one transaction — everything inside it goes up together or not at all; everything outside only ever hears about it afterward, through a message slipped under the door (a domain event). That last part matters: the standard DDD invariant is that a single transaction should modify exactly one aggregate. Two aggregates never get mutated in the same in-process call — the second one finds out via an event, on its own transaction, after the first has already committed.

The naive version of a Payment breaks this rule directly — it reaches into two separate account aggregates from inside a single method call:

typescript

The fix — one aggregate per transaction, coordinated by a domain event: Payment.process() (or, better, the application service driving it) modifies exactly one side — the sender's debit — as its own transaction, and emits MoneyDebited. A process manager (a saga) reacts to that event and issues the credit to the recipient as a separate transaction. If the credit fails, the process manager issues a compensating action (reversing the debit) rather than the whole operation silently rolling back two aggregates it never should have touched together in the first place.

typescript
typescript

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.