DEV Community

Cover image for Software Component Design Principles
Brandon Gautama
Brandon Gautama

Posted on

Software Component Design Principles

In my last post, I wrote about SOLID design principles that guides us on how to arrange/create modules from functions and data. Combining these modules together, we get a deployable component. In this article, we will look at some component cohesion principles and component coupling principles.

What is a Component?
Components are units of deployment. They are the smallest entities that can be deployed as part of a system. In Java, they are jar files.

A. Component Cohesion Principles.

Let's take a look at the 3 principles of component cohesion, which gives us guidance on which classes belong in which components.

A1. REP: The Reuse/Release Equivalence Principle
The granule of reuse is the granule of release.

Dependency management tools have become very important due to the vast number of reusable components and component libraries that have been created. Whenever we write a software, it is very likely that we depend on multiple external dependencies.

The Reuse/Release Equivalence Principle states that people who want to reuse software components cannot, and will not, do so unless those components are tracked through a release process and are given release numbers. This is to ensure that all the reused components are compatible with each other and for software developers to know what each new releases will bring.

From a software design and architecture point of view, this principle means that the classes and modules that are formed into a component must belong to a cohesive group and they should be releasable together.

Why is this important? Reusability - Group classes into a component for reusability.

A2. CCP: The Common Closure Principle
Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons.

This is the Single Responsibility Principle restated for components. The CCP gathers together all the classes that are likely to change for the same reasons into the same component. This minimizes the workload related to releasing, revalidating, and redeploying the software.

For most applications, maintainability is more important than reusability. If the code in an application must change, you would rather make those changes in just one component, rather than making changes across many components, so that we only have to redeploy one component.

Why is this important? Developability/Maintainability - Group classes into a component so changes will be done in just one component, thus easier to release.

A3. CRP: The Common Reuse Principle
Don’t force users of a component to depend on things they don’t need.

The CRP states that classes and modules that tend to be reused together belong in the same component. For example, a component contains both the container class and its associated iterators as they are tightly coupled to each other.

The CRP also tells us which classes not to keep together in a component. Every time a dependency component is changed, our component will need to be recompiled, revalidated and redeployed. This is true even if our component doesn’t care about the change made in the used components.

The CRP is the generic version of the ISP. The ISP advises us not to depend on classes that have methods we don’t use. The CRP advises us not to depend on components that have classes we don’t use.

Why is this important? Separate classes into different components to avoid unnecessary releases for our upstream dependencies (since they may not need every change in the component)

Tension between the 3 Component Cohesion Principles:

The three cohesion principles tend to fight with each other. The REP and CCP are inclusive principles (they tend to make components larger), while the CRP is an exclusive principle (driving components to be smaller). It is the tension between these principles that architects seek to resolve.

Balancing these forces with the needs of the application is always dynamic. That is, the partitioning that is appropriate today might not be appropriate next year.

Generally, projects tend to sacrifice reuse (developability is more important than reuse). As the project matures, and other projects begin to draw from it, the project will change focus towards reuse. This means that the component structure of a project can vary with time and maturity.

B. Component Coupling Principles.

Now that we've looked at how to arrange classes into component, let us take a look at how these components should be related to each other, also known as Component Coupling Principles. There are three of them:

B1. ADP: The Acyclic Dependencies Principle
Allow no cycles in the component dependency graph.

To release a component, it must be compatible with its dependencies. It is common to create a new component that depends on a bunch of other dependency components. Those dependency components then depend on more components. Every time any of those dependency component change, there is a risk that it might break the dependent component. This problem is aggravated if there is a cycle in the dependency graph. Releasing a component is much more difficult since it must not break its dependents and those dependents happen to be its dependencies too. Additionally, what is the correct build order? There is no correct answer.

Let's say you have a cycle in the dependency graph, how to fix it? You can apply the Dependency Inversion Principle by inserting interface between those components so they depend on the interface instead.

B2. SDP: The Stable Dependencies Principle
Depend in the direction of stability.

A component is stable if it is hard to change while a component is flexible if it can be changed easily.

What is hard to change? If a change requires multiple dependent components to recompile.

This principle states that we must ensure that the modules that are intended to be easy to change are not depended on by modules that are harder to change. In other words, to keep a component flexible, it should not be depended on by a lot of modules.

For example, DoorDash and Grubhub calls TheRestaurant service. TheRestaurant is stable because it is being depended on by multiple dependents. Any change to TheRestaurant has to ensure it does not break both DoorDash and Grubhub. So TheRestaurant is stable and not flexible. But we want to keep TheRestaurant flexible so that its changeable.

TheRestaurant depended on by multiple dependents

We can fix this by employing the DIP, by creating an interface that DoorDash/Grubhub can call. TheRestaurant itself will implement the interface. These interfaces contains nothing but an interface and this is very common because they are very stable and ideal targets for less stable components to depend on.

TheRestaurant becomes flexible now with the interface in between

B3. SAP: The Stable Abstractions Principle
A component should as abstract as it is stable.

Some software in the system should not change very often. They are high-level architecture and policy decisions. We don’t want these business and architectural decisions to be volatile. But if they are stable, then the source code that represents those policies will be difficult to change and make the overall architecture inflexible. This can be fix using OCP by creating Abstract Classes.

A stable component should be abstract so that its stability does not prevent it from being extended. On the other hand, an unstable component should be concrete since its instability allows the concrete code within it to be easily changed.

Thus, if a component is to be stable, it should consist of interfaces and abstract classes so that it can be extended. Stable components that are extensible are flexible and do not overly constrain the architecture.

Combining SDP and SAP, we can conclude that dependencies run in the direction of abstraction.

Conclusion
In this article, we have explored Component Cohesion Principles and Component Coupling Principles. Component Cohesion Principles gives us guidance on which classes belong in which components, while Component Coupling Principles gives us guidance on how to arrange these components together.

References: The content of this article is referencing concepts/ideas from the book Clean Architecture by uncle Bob (Robert C. Martin). It is a really great book that I recommend to further if you're interested about software architecture.

Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series). Buy it here: https://amzn.to/3SJguhf

The 3 Programming Paradigms
https://dev.to/brandongautama/the-3-programming-paradigms-34pe

The SOLID Design Principles
https://dev.to/brandongautama/the-solid-design-principles-319f

Top comments (0)