Published July 5, 2021
by Doug Klugh
Design software systems in a manner that promotes independence between software modules. Establish architectural boundaries to enforce independence — isolating the frequency, cost, and risk associated with changes to software modules. This will minimize the impact of changes that propagate through the system, insulating one part of the system from changes in another. Business rules should not have to be recompiled, rebuilt, and redeployed every time a change is made to the user interface, data persistence, or REST transactions. These low-level implementations should be Plug-ins to the business rules to facilitate independent deployability.
A software module that is stable and does not change often should be independent of modules that are unstable and do change often. If a stable module depends on an unstable module, then the stable module (that should not change often) would change every time the unstable module changed — which would be much more frequent than if the dependency was reversed. Employ the Stable Dependencies Principle to ensure that any given component depends on other components that are more stable than it is.
A software module that is hard to change should be independent of modules that are easy to change. This will keep us from transmitting the high cost of change to modules that are easy to change. If a module that is difficult and expensive to change depends on a module that is easy and cheap to change, then every change to the easy module would be just as expensive as changes to the hard module.
A software module that is risky to change should be independent of modules that are relatively safe to change. If a high-risk module depends on a safe module, then a change to the safe module would force a change to the high-risk module making the change to the safe module just as risky as a change to the high-risk module.
As dictated by the Single Responsibility Principle, use cases that serve different actors should be independent of each other. A change to one use case should have no effect on the other. Use cases that serve different actors have no relevant relationship; therefore, the architecture should not connect them.