Notes on component coupling
In this post, I write about the three principles regarding component coupling that I have learned from reading the book “Clean Architecture : A Craftsman’s Guide to Software Structure and Design” by Robert Martin. The three principles are: Acyclic Dependencies Principle, Stable Dependencies Principle and Stable Abstractions Principle.
Acyclic Dependencies Principle (ADP)
ADP comes from the Telecommunication industry. The purpose is to solve the “morning after syndrome”. The “morning after syndrome” refers to the situation in which a developer goes to work in the morning to find his codes no longer working because someone has changed something which his codes depend on.
ADP strives to have no cycle in the dependencies between the components. Each component can be developed individually and be given a release number. As new versions of a component come out, developers of other components that depend on the component can have the options to use the new version or keep using the current one. This allow developers to have the time to prepare to integrate with changes to the component which their codes depend on. In this way, the team can avoid the “morning after syndrome”. For this to work, the dependency structure between the components cannot have any cycle.
Stable Dependencies Principle (SDP)
Component A is stable if it has other components that depend on it. The degree of stability is proportional to the number of dependents. The more dependents A have, the more stable A is.
Changing a stable component can have an impact on its dependents. As such, we don’t expect stable components to be difficult to change, and we don’t expect them to change often. A stable and difficult to change component in turn causes other components that depend on it to be difficult to change, and we don’t want this. For the same reason, we don’t want a volatile, unstable component from depending on another volatile component. In other words, if component A depends on component B, then B should be more stable than A. This is the idea of SDP.
Measuring stability of a component
Fan-in = Number of classes outside the component which depends on the component.
Fan-out = Number of classes outside the component which the component depends on.
Instability = Fan-out / (Fan-in + Fan-out).
Example: Component A has three classes from component B that depends on it (Fan-in = 3). Component A also depends on two classes from component C (Fan-out = 2). Therefore, the instability value for component A is 2/(3+2) = 2/5.
The value of instability has a range of 0 to 1. 0 means maximal stability, whereas 1 means maximal instability.
Maximal instability happens when a component does not have any classes that depend on it (Fan-in = 0), and the component depends on classes from other components (Fan-out > 0). Such component can have ample reasons to change since it does not have any incoming dependencies.
On the contrary, maximal stability happens when a component has classes that depend on it (Fan-in > 0), and the component does not depend on any classes from outside components (Fan-out = 0).
The instability metric should decrease in the direction of incoming dependency. For example, consider a component A that is maximal unstable (i = 1), which means it has no other component depends on it (fan-in = 0), but it depends on other components (fan-out > 1). Conforming to SDP means that the instability metric of the other components should be less than 1. In other words, the other components should be more stable than A.
In short, SDP suggests us to design components such that the unstable components depend on the stable components.
Stable Abstractions Principle (SAP)
In a software system, we may have certain high level policies which we don’t expect to change often. Those policies can be business rules, validations, architecture patterns etc … At the same time, we don’t want to make it difficult to change the high level policies. How can we keep the high level policies stable yet extendable? One way is to place the policies in stable components consisting of abstract classes and interfaces. In fact, SAP suggests stable components which have dependent components should be abstract. The more abstract a stable component is, the less impact any changes to it may have on its dependents. In contrast, unstable components which have no dependent components should consist of concrete classes.
To measure the abstractness of a component, we can divide the number of abstract classes and interfaces in the component by the total number of classes in the component.
A = Na / Nc. A = abstractness, Na = number of abstracts and interfaces in the component. Nc = total number of classes in the component. Value of A is from 0 to 1.
A stable component should have more abstract and interfaces then concrete classes. If the component is stable yet not overly abstract, then it would be difficult to change the classes in the component without affecting its dependents.
In reality, not all components can or need to follow SAP. For example, a database schema is a layer upon which other layers depend on, which means the schema is stable; yet, the schema is generally concrete and difficult to change. Another example is the String component, which is unstable as it has no incoming dependencies (I = 1); however, it is nonvolatile. In general, we do not have to worry much about nonvolatile components as they do not change often. But we should avoid volatile components that are overly concrete (A value is low) as it would be difficult to change those components.
In the book, the author provides a graph and shows the zones where the components may be in. At one extreme is the zone of pain, at the other extreme is the zone of uselessness. In the zone of pain are the components that are very stable (I = 0), yet very concrete (A = 0). These components have others depend on them, but they are difficult to change because of the concreteness. At another extreme is the zone of uselessness. In this zone are the components that are highly unstable (I = 1) and highly abstract (A = 1). These are the components that have no dependents and also no implementations. Ideally, they should not exist in a codebase. The author calls the middle the main sequence which is the ideal place for the components. A component that sit in or near the main sequence is abstract enough for other components to depend on, and concrete enough to the extent it depends on other components.
Together, the three principles provide solid guidelines for designing loosely coupled components. To comply with ADP, dependencies between components should have no cycle. To comply with SDP, dependencies should go in the direction of decreasing instability. That means, a dependent component should be less stable than the components it depends on. To comply with SAP, the stable parts of a component which others depend on should be abstract, and the volatile parts of the component which depend on other components should be concrete.
Originally published at https://www.taithienbo.com on February 27, 2022.