Skip to content

Designing orthogonal software systems

Principle · Chapter 6

  • Borrowing from geometry (two perpendicular vectors are independent), an orthogonal system is one whose modules are independent of each other.
  • Ideal: changing one module should not force changes in another.
  • Payoff over a system’s lifetime: higher productivity, lower risk of introducing defects when changing code, better maintainability and extensibility.
  • Higher upfront design cost, but worth it for long-lived systems.
  • Achieved through two related goals: loose coupling and high cohesion. The two correlate — loose coupling goes with high cohesion, tight coupling with low cohesion.
  • Coupling = the degree to which one module depends on another; how closely connected they are.
  • Tight (high/strong) coupling raises complexity, lowers maintainability, and makes change riskier because edits ripple across dependent modules.
  • Tight coupling also slows development and testing, and reduces reusability (you must drag dependencies along).
  • Loose coupling enables parallel development — different developers work on different parts independently.
  • Goal for the architect: minimize coupling; for coupling that must exist, use the loosest type necessary.

Types of coupling (tightest/worst → loosest/best)

Section titled “Types of coupling (tightest/worst → loosest/best)”
TypeWhat it isVerdict
Content (pathological)One module directly references another’s internal/private dataNever acceptable; refactor to add abstraction
Common (global)Modules share global data (e.g. a global variable)Highly undesirable; prefer constants over mutable globals
ExternalModules share an external data format, interface, protocol, tool, or deviceOften unavoidable; limit how many modules depend on it
ControlOne module passes a flag that drives another’s internal logicModerate; make it explicit and test the two together
Stamp (data-structure)Modules share a composite data structure, some fields unusedFairly low; acceptable
DataModules share primitive values via parameters/return values, all usedLow; the common, acceptable interaction type
MessageOne module calls another’s method with no parameters — coupling only on the method nameLowest coupling
No couplingNo direct communication at allIdeal for independent implementation/testing/maintenance
  • Note: two modules can be coupled in more than one way; the coupling type is the worst (tightest) present.

The Law of Demeter (principle of least knowledge)

Section titled “The Law of Demeter (principle of least knowledge)”
  • A design principle supporting loose coupling: “only talk to your friends.”
  • A method should only call methods on: the same object, objects passed into it, objects it created, its direct component objects, or accessible globals.
  • A module should know as little as possible about other modules. Complements information hiding.
  • Cohesion = the degree to which the elements inside a module belong together; a qualitative measure of a module’s consistency of purpose.
  • Highly cohesive modules have a single, well-defined purpose — better design, easier to maintain and reuse.
  • Low cohesion (a grab-bag of unrelated functions) is harder to understand, more change-prone, more defect-prone, and less reusable.

Types of cohesion (lowest/worst → highest/best)

Section titled “Types of cohesion (lowest/worst → highest/best)”
TypeGrouped because…Level
CoincidentalArbitrary grouping, no real relationship (e.g. a Utils/Helpers dumping ground)Worst — refactor
LogicalLoosely related by general category (e.g. all I/O functions together)Low
TemporalExecuted at the same time (e.g. all startup/shutdown/error handling)Low
ProceduralAlways run in a particular sequence (e.g. order-payment steps)Moderate/acceptable
CommunicationalOperate on the same input/output data (e.g. shopping-basket calcs)Moderate/acceptable
SequentialOutput of one part feeds the input of the next (e.g. format → validate)Moderate
FunctionalUnited for one single, well-defined purposeHighest — ideal
  • Aim for functional cohesion: each module does one thing. Move auxiliary elements that don’t serve the module’s purpose into a more appropriate module.
  • Software Architect’s Handbook (Packt, 2018), Ch.6 “Designing orthogonal software systems”, pp. 395-417.