Published January 29, 2022
by Doug Klugh

Facilitating Independent Deployability

Design components to enable a change to one component without the need to redeploy any of the others.  These principles provide guidance on which classes are best grouped together within a component and which ones should be separated to facilitate independent deployability.  Component partitioning will likely change as the quality model matures and expectations shift from adaptability toward reuse.

Principles of Component Cohesionlaunch
Release Reuse Equivalency Principlelaunch

Design components to be large enough to justify the cost of managing the release cycle.  These management activities include assigning release numbers, maintaining release notes with features and known issues, keeping track of dates, committing to grandfather old versions, etc.  Others cannot reuse your components without them.  Considering the time and effort these activities require, it is unlikely you will be able to manage a large number of components.  Better to plan a small number of strategic components that conform to the other Principles of Component Cohesion.

Common Closure Principlelaunch

Design components by grouping classes together that change for the same reason, while separating those that change for different reasons.  This will help minimize the number of components that change when requirements change.  Since the classes within a component all have the same Single Responsibility, they all serve the same actor.  Therefore, these classes are closed to all but that one responsibility and to the needs of every other actor.  So when requirements change, we will know which components are affected and which are not.

Common Reuse Principlelaunch

Design components by ensuring that if one class in the component is used, they’re all used.  This will prevent dependent components from having to be needlessly retested and redeployed because a class, that it does not use, was changed within that component.  This aligns with the Law of Demeter by promoting interactions only with closely related classes and by limiting knowledge of other parts of the system.  This also helps to deodorize the smell of Immobility.

Finding Balance

These three principles of component cohesion are not cooperative.  They each influence the design of a component in different ways that often oppose each other.  As shown below in figure 1, each principle resides at a vertex of the triangle.  And each edge denotes a condition that the opposing principle is trying to avoid.  The closer a component is to a vertex, the more it conforms to the corresponding principle, and the less it is influenced by the condition on the opposing edge.

Figure 1 - Component Tension Diagram

For example, a component that sits at the top near the Release Reuse Equivalency Principle (RREP) will be highly reusable, often with numerous classes.  Located far from the other two principles, this is likely to promote tight coupling and low cohesion.

A component that is located close to the Common Closure Principle (CCP) will only be affected by one responsibility, but may not have enough classes in it to be reusable.  It will be highly cohesive, but will also be tightly coupled to other components.

A component that is located near the Common Reuse Principle (CRP) will not have unwanted dependencies, but it will also not be cohesive nor very reusable.

The trick is to find the best position within this triangle to promote the qualities you want most.  You cannot satisfy all these principles at the same time — you must find a balance.

In the early stages of a project, teams tend to focus more on the Common Closure Principle (CCP) and the Common Reuse Principle (CRP) to enable a high degree of adaptability.  But as the project matures, components tend to shift more toward the Release Reuse Equivalency Principle (RREP) to facilitate a greater degree of reuse.

False Cohesion

There are many ways to group classes together that would seem to create good cohesion, but really do not.  For example, you should not group together a class that queries or aggregates data with a class that uses that queried data.  Or classes that calculate premiums for different types of insurance policies.  Those will all change for different reasons.  And never bundle whole inheritance hierarchies into a single component.

Dependencies need to be inverted across inheritance relationships, especially when crossing component boundaries.  This is accomplished by ensuring that the flow of control opposes the source code dependency.  Does the Dependency Inversion Principle sound familiar?  So when employing Polymorphic Dispatch, those classes should be encapsulated within different components.

Take time and care when partitioning components.  Do not group classes together simply because they feel right.  You should have explicit reasons for bundling classes together that you can explain with specific engineering principles.  The Principles of Component Cohesion can guide your decision on which classes you really should group together.

What is a Component?

A component is a collection of classes encapsulated within an independently deployable library, such as a jar, gem, or DLL.  These libraries are independently deployable when a change to one does not cause others to be recompiled or redeployed.  The key to developing independently deployable components is to manage dependencies.  For example, changes to an application do not require that framework components be recompiled or redeployed.  Applications depend on frameworks.  Frameworks do not depend on applications.  If developed correctly, you should be able to swap out components during a normal business day, with all users hammering away at your system, with zero downtime.

« Classes are a necessary but insufficient vehicle for decomposition. »

- Grady Booch

Modular Design

Imagine building a large software system out of many small and simple classes.  If you only follow the Single Responsibility Principle, this is exactly what you will end up with.  So, we need a way to group classes together into larger modules.  Components provide a way to encapsulate related classes into larger chunks.  The trick is determining which classes should be grouped together within a component — which is resolved by the Principles of Component Cohesion.  Then the next challenge is managing the dependencies between components — which is solved by the Principles of Component Coupling.

« It's like building a sandcastle from individual grains of sand. »

- Bob Martin