Part 1 · 1 chapters · ~8 min

Hexagonal and Clean Architecture in Real Code

Ports and adapters, the dependency rule, a domain free of frameworks, application services as use cases, driving and driven adapters, testing with fakes, and when the indirection is not worth it.

3

A use case with ports

code
// domain/payout.ts: no imports from express, pg or SDKs
export class Payout {
  private constructor(readonly id: string, readonly accountId: string, readonly amount: Money, private state: PayoutState) {}
  static request(id: string, acct: Account, amount: Money): Payout {
    if (amount.gt(acct.available)) throw new InsufficientFunds(acct.id, amount);
    return new Payout(id, acct.id, amount, 'reserved');
  }
}
// application/ports.ts
export interface PayoutRepo { save(p: Payout, tx: Tx): Promise<void>; }
export interface AccountRepo { lockById(id: string, tx: Tx): Promise<Account>; }
// application/create-payout.ts
export class CreatePayout {
  constructor(private uow: UnitOfWork, private accounts: AccountRepo, private payouts: PayoutRepo, private outbox: Outbox) {}
  run(cmd: { id: string; accountId: string; amount: Money }) {
    return this.uow.run(async tx => {
      const acct = await this.accounts.lockById(cmd.accountId, tx);
      const p = Payout.request(cmd.id, acct, cmd.amount);
      await this.payouts.save(p, tx);
      await this.outbox.add({ type: 'payout.requested', id: p.id }, tx);   // same transaction
      return p;
    });
  }
}
// test: new CreatePayout(fakeUow, inMemoryAccounts, inMemoryPayouts, inMemoryOutbox): runs in microseconds
PORTS AND ADAPTERS
the domain in the middle, technology at the edges
HTTP controlleradapter inapplication serviceuse case: CreatePayoutPostgresPayoutRepoadapter outqueue consumeradapter indomainPayout, Money, rulesPaystackRailadapter out
swipe the figure sideways, or tap expand for full screen
1/5
domain
The domain holds entities and rules (a payout cannot exceed the available balance) with no imports from HTTP, SQL or SDKs.
pure rules, no frameworksfast unit tests