Designing orthogonal software systems
Principle · Chapter 6
The idea in brief
Section titled “The idea in brief”- 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.
Loose coupling
Section titled “Loose coupling”- 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)”| Type | What it is | Verdict |
|---|---|---|
| Content (pathological) | One module directly references another’s internal/private data | Never acceptable; refactor to add abstraction |
| Common (global) | Modules share global data (e.g. a global variable) | Highly undesirable; prefer constants over mutable globals |
| External | Modules share an external data format, interface, protocol, tool, or device | Often unavoidable; limit how many modules depend on it |
| Control | One module passes a flag that drives another’s internal logic | Moderate; make it explicit and test the two together |
| Stamp (data-structure) | Modules share a composite data structure, some fields unused | Fairly low; acceptable |
| Data | Modules share primitive values via parameters/return values, all used | Low; the common, acceptable interaction type |
| Message | One module calls another’s method with no parameters — coupling only on the method name | Lowest coupling |
| No coupling | No direct communication at all | Ideal 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.
High cohesion
Section titled “High cohesion”- 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)”| Type | Grouped because… | Level |
|---|---|---|
| Coincidental | Arbitrary grouping, no real relationship (e.g. a Utils/Helpers dumping ground) | Worst — refactor |
| Logical | Loosely related by general category (e.g. all I/O functions together) | Low |
| Temporal | Executed at the same time (e.g. all startup/shutdown/error handling) | Low |
| Procedural | Always run in a particular sequence (e.g. order-payment steps) | Moderate/acceptable |
| Communicational | Operate on the same input/output data (e.g. shopping-basket calcs) | Moderate/acceptable |
| Sequential | Output of one part feeds the input of the next (e.g. format → validate) | Moderate |
| Functional | United for one single, well-defined purpose | Highest — 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.
Why an architect cares
Section titled “Why an architect cares”- Loose coupling + high cohesion directly improve maintainability, reusability, and testability.
- These goals are operationalized by the SOLID principles (SRP → cohesion, DIP → coupling) and by the complexity-minimizing principles in minimizing complexity.
Citations
Section titled “Citations”- Software Architect’s Handbook (Packt, 2018), Ch.6 “Designing orthogonal software systems”, pp. 395-417.