Skip to main content
Clean Architecture 70 XP · 7 min

Component Cohesion: The Common Reuse Principle

Don't force users of a component to depend on things they don't need — depending on a component means depending on everything it contains.

Showing
Ad (728×90)

Why this matters

The Common Reuse Principle (CRP) tells us which classes not to put together in a component: don't force users of a component to depend on things they don't need. If a component contains class A and class B, a user who only needs class A must still depend on class B. When class B changes — even if the user never calls it — that user must revalidate and potentially redeploy their software.

CRP is the component-level equivalent of ISP. Both say the same thing at different scales: don't couple consumers to things they don't use. The practical discipline is to ensure that every class in a component is inseparable from the others — that users of the component genuinely need all of the classes it contains. When a class is "almost always used" alongside certain other classes, that's a CRP signal to bundle them; when a class is "sometimes useful" alongside an unrelated class, that's a CRP signal to separate them.

The problem

A containers package that includes GraphTraversal forces all consumers — even those using only List — to install heavy graph dependencies and retest when graph code changes.

Bad

# containers/ package bundles basic collections with a heavy graph algorithm
#   list_impl.py        — simple linked list
#   map_impl.py         — hash map
#   queue_impl.py       — FIFO queue
#   graph_traversal.py  — BFS/DFS, requires networkx (heavy dep!)

# A service that only needs List must install networkx.
# When graph_traversal.py changes, that service must re-validate
# its code — even though it never touches graph traversal.
// @company/containers bundles simple collections with heavy graph utilities
// packages/containers/src/
//   LinkedList.ts       — basic linked list
//   HashMap.ts          — hash map
//   Queue.ts            — FIFO queue
//   GraphTraversal.ts   — BFS/DFS (pulls in "graphlib" — heavy peer dep)

// A service using only LinkedList must list graphlib as a peer dependency
// and re-run tests every time GraphTraversal is modified.

The solution

GraphTraversal lives in its own package. Services that need only basic collections depend on containers — no graphlib, no graph churn.

Good

# containers/             — basic collections only
#   list_impl.py
#   map_impl.py
#   queue_impl.py

# graph_algorithms/       — separate package, separate release cadence
#   graph_traversal.py    # requires networkx — only installed by graph users

# Services needing only List depend on "containers" — no networkx, no graph churn.
# Services needing BFS also depend on "graph_algorithms" — explicit, intentional.
// @company/containers      — LinkedList, HashMap, Queue only
// @company/graph-algorithms — GraphTraversal, requires graphlib

// A service using LinkedList:
//   import { LinkedList } from "@company/containers";  // no graphlib

// A service needing BFS:
//   import { bfs } from "@company/graph-algorithms";   // explicit opt-in

Key takeaway

CRP says: don't put classes in a component if users of the component don't need all the classes. Depending on a component means depending on everything it contains — so everything in a component should be inseparable.

Done with this lesson?

Mark it complete to earn XP and track your progress.