Skip to content

// SOLID & Principles · 09

Law of Demeter

order.Customer.Address.Cityleads toTalk only to neighbours

Talk to your direct collaborators, not to their insides.

How it fits together

Law of Demeter structureBeforeAfterShippingServiceShip(order)order.Customer.Address.CountryOrderCustomerLinesCustomerAddressAddressCountryCityShippingServiceShip(order)order.ShipsTo(country)OrderShipsTo(country)Customer (private)CustomerAddress (private)AddressCountry1 order2 .Customer3 .Address
uses / callswraps / holds· numbered steps replay in order
  • ShippingService uses / calls Order (1 order)
  • Order uses / calls Customer (2 .Customer)
  • Customer uses / calls Address (3 .Address)
  • ShippingService uses / calls Order
  • Order wraps / holds Customer
  • Customer wraps / holds Address

Spot it

  • Long chains like order.Customer.Address.City
  • Tests build deep object graphs to run
  • A model change breaks distant callers

It costs

Wrapper methods multiply; applied blindly, owners grow bloated pass-through APIs.

In code

public sealed record Address(string Country, string City);​public sealed class Customer(Address address){    public bool IsIn(string country) => address.Country == country;}​public sealed class Order(Customer customer){    public bool ShipsTo(string country) => customer.IsIn(country);}​public sealed class ShippingService{    public bool Ship(Order order, string country) => order.ShipsTo(country);}
Go deeperThe full explanation and what to ask for · 1 min readExpand

A method should only call methods on its own object, its parameters, objects it creates and its direct fields. Each extra hop couples the caller to another object's structure.

It is not "count the dots". Query pipelines, fluent builders and plain data access return values or the same object, so chaining them reaches into nothing. The concern is navigating through objects that own behaviour.

Fix it with tell, don't ask: move the behaviour to the object that holds the data. If that turns a class into a dumping ground, the boundary itself is wrong.

Ask for it

  • principle of least knowledge
  • tell, don't ask
  • train wreck
  • encapsulation
  • coupling

Find method chains in the Application layer that reach through domain objects, like order.Customer.Address.Country. Replace them with intent-revealing methods on the nearest owner, e.g. order.ShipsTo(country), and leave fluent builders and query pipelines as they are.