skip to content

How does the Law of Demeter generalise beyond single objects — to modules, packages, and service boundaries — and what are its known limitations at that scale?

level: principalimportance: nice to knowfreq 18%

answer

  1. Don't depend on your dependency's dependencies
  2. Aggregate root / facade / gateway / ACL = LoD scaled up
  3. Module allowedDependencies = class-form LoD mechanised
  4. Chatty vs chunky: hidden hop = round trip
  5. Only structural coupling; connascence covers timing/order/identity

basics

~20 s

The same idea scales up: a module or service should depend only on its direct neighbours, not reach through them into their dependencies. Facades, aggregate roots, and anti-corruption layers are Law of Demeter applied at architectural scale.

solid answer

~50 s

At scale, "don't talk to strangers" becomes **don't depend on your dependency's dependencies**. Manifestations: - **DDD aggregates** — outside code may only reference the aggregate root, never internal entities. That is LoD as an invariant-protection rule. - **Facade / API gateway / BFF** — one deliberate front door instead of clients traversing a subsystem's internals. - **Anti-corruption layer** — stops a foreign model's structure leaking into yours. - **Package/module dependency rules** (e.g. enforced by ArchUnit-style checks or module systems) — the class-level Law of Demeter mechanised. Limitations: chattiness (each hidden hop can become a network round trip, so architectural LoD can *cost* latency where object-level LoD is free); facades ossify into god interfaces; strict layering produces pass-through layers that add no value; and transitive dependencies are often *semantic*, not structural, so hiding a call doesn't remove the coupling — it just makes it invisible. LoD limits *knowledge of structure*; it says nothing about behavioural or temporal coupling.

go deeper

for a junior

Say the same idea applies to modules: a module should use its direct dependencies, not reach through them; facades exist for this.

for a middle

Give the concrete architectural instances — facade, aggregate root, API gateway — and note that enforced package dependency rules are the class-level form of the same rule.

for a senior

Add the trade-offs: chattiness across networks, facades growing into god interfaces, pass-through layers, and the need to confine unavoidable traversal to read models and mappers.

for a principal

Lead with the limitation that LoD addresses only structural coupling, bring in connascence for the dimensions it misses, distinguish hiding a transitive dependency from removing it, and resolve the chatty/chunky tension by placing composition behind the boundary rather than in the client.

## Scaling the principle up The object-level rule — *only message immediate collaborators* — has a direct analogue at every larger granularity, because the underlying idea is granularity-independent: **don't encode knowledge of another unit's internal composition.** ### Where it shows up architecturally **1. DDD aggregates.** An aggregate is a cluster of objects with one **root**; external code may hold a reference to the root only, and must reach internals through it. This is the Law of Demeter promoted to an invariant-protection rule: if outsiders could grab `order.lines()` and mutate a line, the root could no longer guarantee "total must equal the sum of lines". **2. Facade pattern.** A single entry point over a subsystem so clients don't navigate its parts. Textbook LoD at module scale. **3. API gateway / Backend-for-Frontend.** Clients call one endpoint instead of orchestrating five services; they never learn the internal service topology. Same principle, network edition. **4. Anti-corruption layer (DDD).** Translates a foreign bounded context's model into yours so its *structure* does not leak inward — LoD applied across an ownership boundary, which is the highest-value place to apply it. **5. Enforced module/package dependency rules.** Declaring which packages a module may depend on (module systems, ArchUnit-style tests, Spring Modulith `allowedDependencies`) is the **class-level** Law of Demeter mechanised: the set of types a unit may name is declared, and anything else is a stranger. **6. Law of Demeter for data.** Don't let a caller depend on the *shape* of a payload three levels deep; expose a flat, intent-shaped contract. Wire schemas that mirror internal structure are train wrecks with a serializer attached. ### The limitations — this is what separates a principal answer **a) Chattiness / latency inversion.** In-process delegation is free. Across a network it is not: each hidden hop can be a round trip. Architecturally you often want the *opposite* of LoD's naive shape — one coarse-grained call that returns a composed result ("chunky, not chatty"), which is precisely a caller reaching for a *composed* view. The resolution is that LoD forbids *the client knowing the traversal*, not the *server performing* it — so composition belongs behind the boundary (gateway, read model), not in the client. **b) Facades become god interfaces.** Every new client need adds a method. Over years the facade is a 300-method interface that couples everyone to everything — the Middle Man smell at architectural scale, with the added problem that it becomes a change bottleneck and a deployment coupling point. **c) Pass-through layers.** Strict layer-by-layer delegation yields controllers that call services that call services that call repositories, each adding nothing. The delegation cost that is annoying inside a class is expensive across layers, because each layer has its own DTOs, tests, and mappers. **d) LoD only addresses *structural* coupling.** Two services can share zero types and still be lethally coupled — on ordering, on a shared database, on an implicit state machine, on message semantics. **Connascence** gives a richer vocabulary here (name, type, meaning, position, algorithm, timing, execution order, identity); LoD roughly targets connascence of *position/structure* and *name*, and is silent about *timing*, *execution order*, and *identity* — often the couplings that actually cause outages. **e) Hiding vs removing.** Wrapping a transitive dependency behind a method makes it invisible, not absent. If service A's response *semantically* depends on service C's data, you still have an availability and change dependency on C. The org chart, not the call graph, is often the truer picture (Conway's Law). **f) Traversal has to live somewhere.** Query/reporting, serialization, and ETL genuinely need whole-graph access. The mature answer is to confine it — dedicated read models, mappers, a CQRS query side — rather than pretend it doesn't exist. ### The synthesis to state "Law of Demeter is a specific, useful, *narrow* tool: it limits how much a unit knows about another unit's internal composition. Scaled up it produces aggregates, facades, gateways, anti-corruption layers, and enforced module boundaries. But at scale it can be actively wrong if applied naively — hiding hops across a network costs latency, and it addresses only one of many coupling dimensions. I use it as a detector for structural leakage, and reach for connascence and explicit contracts to reason about the rest."

  • Doesn't applying the Law of Demeter across service boundaries make systems chattier and slower?
    It can, if you translate 'hide the hop' into 'add a network hop'. The correct reading is that the *client* must not know the traversal — the composition should happen behind the boundary in a gateway, BFF, or read model, so the client makes one chunky call and still learns nothing about the topology.
  • What does connascence add that the Law of Demeter does not?
    Connascence classifies coupling by kind (name, type, meaning, position, algorithm, timing, execution order, identity) and by strength, locality, and degree. LoD mainly targets structural/positional knowledge; connascence lets you reason about timing, execution-order, and identity coupling, which LoD is entirely silent about and which frequently cause production failures.
  • How does a DDD aggregate root relate to the Law of Demeter?
    It is the same rule turned into an invariant guarantee: external code may only reference the root, so it cannot navigate to internal entities and mutate them behind the root's back. LoD explains why the design is decoupled; the aggregate explains why it is also correct.

An embassy. You don't wander into a foreign country's ministries to get a visa; you go to the one building that speaks for the whole state. That's the facade — and its known failure is the embassy becoming an overloaded, permanently backlogged bottleneck.

context