Skip to main content

Chapter 14: Component Coupling

"The dependency structure between components must be a directed acyclic graph (DAG). There can be no cycles."

๐ŸŽ“ For New Learnersโ€‹

The Three Coupling Principlesโ€‹

While cohesion answers "what goes inside a component," coupling principles answer "how should components depend on each other":

PrincipleRule
ADP โ€” Acyclic DependenciesNo cycles in the component dependency graph
SDP โ€” Stable DependenciesDepend in the direction of stability
SAP โ€” Stable AbstractionsStable components should be abstract

ADP: The Acyclic Dependencies Principleโ€‹

Cycles in the dependency graph are catastrophic:

ComponentA โ†’ ComponentB โ†’ ComponentC โ†’ ComponentA (cycle!)

To change any component in the cycle, you must consider changes to all components in the cycle. You cannot test A without B and C. You cannot release A without releasing B and C. The cycle makes the group of components a single monolithic block disguised as separate pieces.

Breaking cycles โ€” two solutions:

  1. DIP: introduce an interface that inverts the dependency. If ComponentC depends on ComponentA, create an interface in ComponentC that ComponentA implements. Now the runtime call goes from C โ†’ A, but the source code dependency goes from A โ†’ C. Cycle broken.

  2. New component: extract the shared thing that creates the cycle into a brand new component. Both A and C depend on the new component. No cycle.

SDP: The Stable Dependencies Principleโ€‹

Stability doesn't mean "doesn't change." It means: hard to change because many things depend on it.

  • A component with many fan-in (things depending on it) is stable โ€” changing it breaks many things
  • A component with many fan-out (it depending on many things) is unstable โ€” it changes frequently because its dependencies change

SDP says: depend in the direction of stability. Volatile components (high fan-out, few dependents) should depend on stable components (high fan-in, few dependencies). Never the reverse.

Stability metric: I = fan-out / (fan-in + fan-out)

  • I = 0: maximally stable (nothing depends on anything external; many things depend on it)
  • I = 1: maximally unstable (depends on many things; nothing depends on it)

SDP: dependencies should point from high-I components to low-I components.

SAP: The Stable Abstractions Principleโ€‹

A maximally stable component that is also maximally concrete is maximally rigid โ€” it cannot be extended without modification (OCP violation).

The solution: stable components should be abstract. Stability + abstraction = flexible stability.

  • Concrete + Unstable: fine โ€” volatile details that change freely
  • Abstract + Stable: ideal โ€” stable policies expressed as interfaces that can be extended
  • Concrete + Stable: problematic โ€” rigid, hard to extend (the "zone of pain")
  • Abstract + Unstable: useless โ€” interfaces nobody depends on (the "zone of uselessness")

๐Ÿ”ฌ Senior Deep Diveโ€‹

Cycle Detection in Maven Projectsโ€‹

Maven's dependency-check plugin and ArchUnit can detect cycles:

// ArchUnit: fail the build if cycles exist between packages
@Test
void noCyclesBetweenPackages() {
JavaClasses classes = new ClassFileImporter()
.importPackages("com.example");
slices().matching("com.example.(*)..").should().beFreeOfCycles()
.check(classes);
}

In a multi-module Maven project, cycles between modules cause Maven build failures โ€” Maven enforces DAG structure at the module level, which is one reason multi-module projects improve architecture.

SDP and the Clean Architecture Layersโ€‹

The Clean Architecture's concentric rings are ordered by stability:

LayerStabilityInstability Metric (I)
Entities (domain)Most stable~0
Use CasesStableLow
Interface AdaptersUnstableMedium
Frameworks & DriversMost unstable~1

Dependencies point inward (toward stability). The domain layer has the highest fan-in (everything depends on it) and nearly zero fan-out (it depends on nothing external). This is SDP + SAP realized as concentric rings.

SAP Metrics and the Main Sequenceโ€‹

Martin introduces the "Main Sequence" โ€” the ideal line on a graph of Abstractness (A) vs. Instability (I):

A
1 โ”‚ โ— Zone of Uselessness
โ”‚ (abstract but nobody depends on it)
โ”‚
โ”‚ โ•ฒ Main Sequence
โ”‚ โ•ฒ (ideal)
โ”‚ โ•ฒ
โ”‚ โ•ฒ
0 โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ— Zone of Pain
0 1 I
(concrete and maximally stable = rigid)

Components should plot near the main sequence. Distance from the main sequence is the metric of architectural health:

D = |A + I - 1|

D near 0 is ideal. High D means either "zone of pain" (stable + concrete) or "zone of uselessness" (abstract + unstable).

Applying These Metrics in Spring Projectsโ€‹

A typical Spring application's component metrics:

Domain model (pure Java)
fan-in: high (used by everything)
fan-out: near zero
Abstractness: high (interfaces, value objects)
โ†’ I โ‰ˆ 0, A โ‰ˆ 0.7 โ†’ near main sequence โœ“

Spring Boot starter classes (@SpringBootApplication)
fan-in: near zero (nothing depends on the main class)
fan-out: very high (depends on all other components)
Abstractness: near zero (concrete configuration)
โ†’ I โ‰ˆ 1, A โ‰ˆ 0 โ†’ near main sequence โœ“

Spring @Service "God service" class
fan-in: high (many controllers depend on it)
fan-out: high (depends on many repos, clients, utils)
Abstractness: zero (concrete class)
โ†’ I โ‰ˆ 0.5, A = 0 โ†’ Zone of Pain โœ—

The God Service is architecturally dangerous: it's concrete (hard to extend) and moderately stable (hard to change without breaking callers), but not abstract enough to be extended cleanly. Breaking it up โ€” making it depend on interfaces, extracting smaller use cases โ€” moves it toward the main sequence.


Summaryโ€‹

PrincipleCore RuleViolation Consequence
ADPNo cyclesCycle = monolith in disguise; cannot release independently
SDPDepend toward stabilityStable component depending on volatile one = unexpected breaks
SAPStable components should be abstractStable + concrete = Zone of Pain (rigid, can't extend)
Main SequenceBalance stability and abstractionUse D metric to identify architectural problem areas