Skip to main content

Chapter 16: Independence

"A good architecture must support... the independent developability of the system's components."

๐ŸŽ“ For New Learnersโ€‹

Three Kinds of Independenceโ€‹

A well-structured system enables three types of independence:

  1. Use-case independence โ€” adding a new use case doesn't require modifying existing ones
  2. Development independence โ€” different teams can work on different use cases simultaneously without stepping on each other
  3. Deployment independence โ€” components can be deployed independently, without deploying the entire system

Use-Case Decouplingโ€‹

Each use case should be a separate, isolatable slice through the system. A use case should touch only the layers it needs to:

Use Case: Place Order
โ†’ Controller (web adapter)
โ†’ PlaceOrderUseCase (application layer)
โ†’ Order (domain entity)
โ†’ OrderRepository (persistence)

Use Case: Cancel Order
โ†’ Different controller
โ†’ CancelOrderUseCase (separate class)
โ†’ Same Order entity
โ†’ Same OrderRepository

These two use cases share the domain entity and repository interface but are otherwise independent. A bug in CancelOrderUseCase cannot affect PlaceOrderUseCase.

Operational Decoupling (Scaling)โ€‹

Some use cases need to run at different scales:

  • Processing orders: high volume, must scale
  • Generating PDF invoices: low volume, can be slow
  • Administrative reports: rare, resource-intensive

If these are decoupled, you can scale order processing independently of invoice generation. If they're coupled in one monolithic process, you must scale everything together.

The Decoupling Mode Dilemmaโ€‹

Martin presents a key insight: the right decoupling mode depends on factors you often don't know at design time:

ModeDeployment UnitCommunication
Source levelSingle processIn-memory calls
Deployment levelSeparate JARs, same processIn-memory calls
Service levelSeparate processesNetwork calls

Starting with source-level decoupling (clean boundaries in a monolith) lets you migrate to service-level decoupling later โ€” without rewriting business logic.


๐Ÿ”ฌ Senior Deep Diveโ€‹

The Fallacy of "Start with Microservices"โ€‹

The popular advice "build microservices from day one" violates the principle of deferring decisions. Martin argues:

  • Microservice boundaries require understanding the domain's natural seams
  • Natural seams only become apparent after working with the domain for months
  • Premature service boundaries are hard to change โ€” network calls don't refactor as easily as method calls
  • A well-structured monolith can be extracted into microservices later; a poorly structured microservice system cannot be consolidated without a rewrite

The Modular Monolith is an underrated intermediate:

  • Single deployable unit (operational simplicity)
  • Strict module boundaries enforced by Maven modules
  • Use cases independent at the code level
  • Can be split into services exactly where the seams are proven

Decoupling in Spring: The Progressionโ€‹

Stage 1: Source-level decoupling (monolith)

// All in one Spring Boot application
// Boundaries enforced by package/module structure
// Communication: direct method calls
orderService.place(command);

Stage 2: Deployment-level decoupling (modular monolith)

// Separate Maven modules, single deployable JAR
// domain-jar, application-jar, web-jar โ†’ assembled into app.jar
// Communication: still method calls

Stage 3: Service-level decoupling (microservices)

// Separate Spring Boot applications
// Communication: REST or messaging
orderServiceClient.place(command); // HTTP call to order-service
// OR
eventPublisher.publish(new PlaceOrderCommand(command)); // async message

The business logic in each stage is identical. Only the communication mechanism and deployment unit change.

Duplication: Real vs. Accidentalโ€‹

Martin warns against conflating two types of duplication:

Accidental duplication: two pieces of code that look similar but change for different reasons. They should NOT be combined โ€” they will diverge.

Real duplication: two pieces of code that are truly the same concept. They SHOULD be extracted and shared.

Prematurely deduplicating accidental duplication is a common coupling mistake. Two use cases that have similar request/response structures might look like they share a DTO โ€” but if they evolve independently, coupling them via a shared DTO causes unnecessary friction later.


Summaryโ€‹

ConceptKey Point
Three independence typesUse-case, development, deployment
Decoupling modesSource โ†’ deployment โ†’ service level
Monolith firstDefer service extraction until seams are understood
Modular monolithClean module boundaries + single deployment = best of both worlds
Accidental vs. real duplicationDon't deduplicate things that merely look similar but evolve separately