Capability·Driven
FAQs

Common questions.

Two questions come up every time: how does this compare with the architectures I already know, and how does it hold up at scale? Here are both, in full.

Question 01

How does it compare with other topologies?

The home-page diagram shows the two endpoints of a spectrum. Here is the whole spectrum: how each arrangement performs, and — since it tracks closely — what each means for how you organise the code.

01

Point-to-point mesh

Services call each other directly.

Pros

  • Fast to start — call the service you need, no infrastructure to stand up.
  • Lowest latency per hop — a direct synchronous call, no broker in the middle.
  • Simple to reason about for a single interaction.
  • repo Polyrepo-friendly — each service owns its repo and ships on its own.

Cons

  • Coupling grows with the square of the services — every new one multiplies connections.
  • Wide blast radius — one service's change, outage, or slow response cascades to its callers.
  • No single place to see or govern who calls whom.
  • Hard to evolve a contract without breaking direct callers.
  • repo Shared DTOs and client SDKs leak into the build, recreating the coupling in code.
  • repo Breaking changes force lockstep releases across every consumer repo.
02

Layered n-tier, shared database

Client → API → services → one shared database.

Pros

  • Familiar and well understood — easy onboarding.
  • One database means strong consistency and easy cross-entity joins.
  • Clear top-down request flow.
  • repo Simple to navigate — usually one monorepo with everything in one place.

Cons

  • The shared database is the coupling — any team can read or write any table, so nobody truly owns the data.
  • Schema changes become org-wide events; the database is a change bottleneck.
  • Scales vertically far more easily than horizontally.
  • Failure of the shared store takes everything down.
  • repo The migrations folder is a contention point — every team commits to the same place.
  • repo A shared entity/ORM library recompiles and redeploys everyone on a single change.
03

Hub-and-spoke / ESB

Everything integrates through a central broker.

Pros

  • One place to route, transform, and monitor integrations.
  • Endpoints are decoupled from each other — they only know the hub.
  • A central point for governance, auth, and observability.
  • repo Spoke repos stay decoupled from one another; each evolves as long as the hub absorbs the change.

Cons

  • The hub is a single point of failure and a scaling chokepoint.
  • It accumulates business logic until it becomes its own monolith.
  • The central team becomes a delivery bottleneck for every integration.
  • repo Integration logic accretes in the hub repo — the central team owns every change to it.
04

Event-driven bus

Producers publish; consumers subscribe.

Pros

  • Producers and consumers are decoupled in time and identity — add a consumer without touching the producer.
  • Naturally resilient — a consumer can be down and catch up later.
  • Scales horizontally; a good fit for reactive, async workloads.
  • repo Naturally polyrepo — no build-time dependency between producer and consumer.
  • repo Independent CI/CD — each side ships on its own cadence.

Cons

  • Eventual consistency — there is no global "now".
  • Harder to debug — the flows are implicit across subscriptions.
  • Demands discipline: schema versioning, idempotency, dead-letter handling.
  • Not ideal where a caller genuinely needs a synchronous answer.
  • repo The shared event schema (registry or package) needs governance and backward-compatible evolution.
  • repo Consumers don't fail fast at build time on a bad schema change — you catch it at runtime unless contract tests guard the registry.
05

Capability-driven

Each capability owns its data; curated REST in, events out.

Pros

  • Ownership is explicit — each capability owns its data; no shared database, no reaching in.
  • Bounded blast radius — a change lands behind a versioned contract, and peers are insulated.
  • Two clear, governable paths (REST for channels, events between capabilities) instead of a mesh.
  • Independent scaling and deployment; each capability is sized to fit one agent's context window.
  • repo Repos are genuinely independent — no shared databases, entities, or codebases across capabilities.
  • repo The only shared thing is the contract — OpenAPI and AsyncAPI specs as versioned packages, never implementation.

Cons

  • More upfront design — you must find the right boundaries before building.
  • Data is duplicated across capabilities, with eventual consistency between them.
  • More moving parts than a monolith — it needs the bus, the contracts, and event discipline in place.
  • Overkill for a small, single-team app where a shared database would just work.
  • repo It takes discipline to keep contracts versioned and boundaries enforced.
  • repo A monorepo variant needs CI boundary checks, or the "no shared implementation" rule erodes.

As you move down the list you trade immediacy and simplicity for decoupling and independent evolution. Point-to-point and shared-database are cheapest on day one and most expensive at scale; capability-driven inverts that — more design cost upfront, far lower cost of change later. And repository independence tracks runtime decoupling: the looser the coupling, the smaller the shared surface, until the only thing shared is a versioned contract and every repo — and the team behind it — can move on its own clock.

Question 02

How does it handle scaling?

The constraints the model imposes on day one — own your data, talk through events, share no database — are the same ones global scale forces on every architecture eventually. Most systems reach them painfully after they go multi-region. A capability-driven system starts there.

Scales cleanly

  • Independent scaling — each capability owns its data and is decoupled, so a hot capability scales out without dragging its peers. Independent scalability is one of the four promotion-test criteria, so boundaries fall where demand actually varies.
  • Data residency by design — because each capability owns its store and shares nothing, pinning data to a region is a per-capability deployment choice, not a system-wide migration.
  • Read locality — capabilities already keep their own copies built from events, so regional read models serve journeys from the nearest region, with no cross-region synchronous reads.
  • Failure isolation — no shared database or codebase means a regional or single-capability outage is bulkheaded; consumers fall back to last-known state from the event log.
  • No global-write bottleneck — per-capability ownership avoids the single database that cannot span regions; each capability picks its own consistency and replication posture.
  • Distributed teams — versioned OpenAPI and AsyncAPI contracts let teams across regions evolve on their own clock.

The hard parts

  • The event bus becomes critical infrastructure — cross-region replication, ordering, idempotency, and backpressure are now the central engineering problem. The model concentrates the difficulty here on purpose.
  • Eventual consistency across regions — there is no global "now"; cross-capability, cross-region workflows need sagas and compensating actions, not distributed transactions.
  • Duplication cost — regional read models multiply storage and add reconciliation work.
  • Ordering and observability — causal consistency is genuinely hard, and tracing a journey across async, cross-region hops is harder than following a synchronous call chain.

The bottom line. The model doesn't make global scale free — it relocates the difficulty to one well-understood place, the event bus and its schemas, instead of spreading it across a shared database and a call mesh. At global scale, that's the trade you want.