[Book Study] Clean Architecture — Part 3

Zong Yuan
10 min readOct 28, 2021

This article covers the component section in the book.

Components

Components are the units of deployment such as jar files, DLL files or executables. Components can be aggregated or linked together.

Dependencies managed by maven also considered as components here.

So how should we define a good components design?
Well-designed components always retain the ability to be independently deployable and developable.

The book explained the important of plugin architecture by telling the history, but I will skip them here.

Nowadays the component plugin architecture become a common design in the software architecture, which reduce the compilation time (by incremental compile), reduce the deployment efforts and also enhance the software flexibility.

Imagine you have a big software (like OS), every time full compilation might take until half hour, with incremental compilation (or only compile the changed components part), the compile time will be greatly reduce.

Component Cohesion

This chapter explains how to allocate code/functions in same components.

Cohesion: State of sticking together
Component Cohesion: State of sticking stuffs together become a component

  • REP: The Reuse/Release Equivalence Principle
  • CCP: The Common Closure Principle
  • CRP: The Common Reuse Principle

The Reuse/Release Equivalence Principle

The granule of reuse is the granule of release

Reusable software components should be trackable through a release process and release number, so:

  1. Able to ensure reused components are compatible with each other by release number
  2. Software developers able to know new releases’ changes and timeline

From software architecture point of view, this principle means that the classes and modules that belong to a cohesive group should be formed into a component, and cannot simply consist of a random classes or modules.

All classes and modules in same component should share the same purpose and also able to release together.

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.

In short, this is the SRP version for the component design.

A component should have only one reason(actor) to change.

This principle suggests put those classes with same types of changes in future into same component, thus it can help to minimize the deployment workloads and also restrict the changes to a minimal number of components.

Imagine there are multiple teams handling different components, a violation of CCP not only cause CR cost higher due to more teams have to be included in a CR, and also raise the potential risks for conflicts when working on the changes.

Gather together those things that change at the same times and for the same reasons. Separate those things that change at different times or for different reasons.

The Common Reuse Principle

Don’t force users of a component to depend on things they don’t need.

This is the ISP version for the component design.

A good example for this principle is how Spring separated their components. For example, there are Spring core, Spring boot, Spring Security and so on. Spring allowed developers to depend only the required components, for other components, developer can always ignore and won’t be affected by those components’ changes.

Don’t depend on things you don’t need.

The Tension Diagram For Component Cohesion

REP and CCP belong to inclusive principles: make components larger. While CRP belongs to exclusive principle: make components smaller.

The relationship between REP, CCP and CRP as below.

You can’t have a perfect design that match 3 principles perfect, that’s why the software architecture is all about balance or trade-off, or we can say a good architect always finds the most suitable balance in this tension triangle at the moment, which the balance will change over time.

For me, software architecture don’t have a perfect solution or standard answer, it is all about trade-off. What is most important and what we can give up to achieve our goals.
Software architecture also have to be flexible, since your concerns or requirements are changing over the time.

Component Coupling

Last chapter explains what should be put into same component and what shouldn’t. And this chapter is explaining the relationship between the components.

  • ADP: The Acyclic Dependencies Principle
  • SDP: The Stable Dependencies Principle
  • SAP: The Stable Abstraction Principle

The Acyclic Dependencies Principle

Allow no cycles in the component dependency graph.

Cycles in dependencies not only happened in inter-components, it might also occurred in dependencies injection that provided by different frameworks. A lot frameworks such as Spring detect dependencies cycles and throw errors. However, they still provide ways to achieve the dependency injections which should be think twice before use them.

Imagine there are two components, A and B. Both of them depend on each other as diagram below.

Component A and Component B depend on each other.

Let’s say this two components maintained by two different teams. Team A maintains component A while team B maintains component B. Now if component A has a new releases that required components that depended on component A to make some changes to use the new release. When component B makes the changes, the other changes on component B will be released at the same time. Imagine if the changes on component B also requires component A to make some changes, things become complicated.

The example above shows that why cycles in dependencies will cause issue, if there is no cycles in the dependencies, like only component B depends on component A, with the help of release number, component B can always decide when to update their dependencies on component A to the newer release number. Component B even can choose to not use the newer release of component A.

Sometimes, a new change request might causing the cycle of components. The book provides two mechanisms to break the cycle.

  1. Apply DIP. Let’s say component B depends on component A, now component A needs a class from component B. We can create an interface in component A, then let class in component B inherit the interface.
  2. Create a new component and move the class(es) required into the component. Then make those components that need those class(es) depend on the new component.

The example also shows that as the software grows, the component dependency structure might change too.

Top-Down Design

Component dependency design are focus on software buildability and maintainability. This is why they aren’t designed at the beginning of project.

Below is the component design timeline of software development that provided by the book.

Component design timeline

There is one prioritized concern with dependency design, which is the isolation of volatility. When start software development, we should always protect the stable high-value components from volatile components. Such as GUI modification shouldn’t have any impact on software core business logic.

Since software growth is mostly unpredictable, and we can’t predict what dependencies or components will be used in future, if spend too much efforts on component design at earlier stage, the efforts will be wasted if the growth is not matched with design. However, I think we should always stick with basic SOLID principles which will ease our jobs when adjusting our code to the component design in future.

The Stable Dependencies Principle

Depend in the direction of stability.

To reduce unneeded changes on stable components from volatile components (Volatility isolation).

Some components are expected to be volatile, that’s why it is important to reduce other components depended on these volatile components.

Stability

The stability of components here is defined as how many works or we should say how many components will be affected if we make changes on the components. If the component’s stability is high, it mean there are lots of components depended on the component, and the efforts to make the changes will be higher.

A component that depended by other components but doesn’t depend on any component is independent, which is a stable component.

Component A is independent and stable

A component that depends on other components but isn’t depended by any component is dependent, which is a unstable component.

Component A is dependent and unstable

Stability Metrics

  • Fan-in: Incoming dependencies, number of classes outside this component that depend on classes within the component.
  • Fan-out: Outgoing dependencies, number of classes inside this component that depend on classes outside the component.
  • I: Instability, range [0,1]. I=0 indicates a maximally stable component. I=1 indicates a maximally unstable component.

Calculate the stability of component A from example below. The Fan-in=3, Fan-out=1 and the I=1/4.

The fan-in and fan-out can calculate by counting import statements in Java or similar statements in other language.

The SDP says that component should depends only on components with smaller I metric.
I metrics should decrease in the direction of dependency.

For example showed below, I metrics of component A should be larger than I metrics of component B.

I of component A should > I of component B

How if there is some stable components that need a class from unstable components?
The book provides a fix, which is using the DIP. We can create an interface for the class and put it in a new component, then make the unstable component implement this interface, and force both components (stable & unstable) depend on this new component. Since a component that only contains interfaces or abstract classes considered as stable component (this kind of component called abstract component), the SDP violation now is avoided.

The Stable Abstractions Principle

A component should be abstract as it is stable.

High-level policy should be placed into stable component (I=0) to avoid other components’ changes affect these policies.

For example, you don’t want a change on apply promotion GUI affects the promotion calculation policy.

However, placing all high-level policy into stable components will make the high-level policy becomes inflexible, any changes on those policy will become difficult. Luckily the OCP provides the solution idea for this issue, we can use abstract classes to enhance the flexibility.

Stable component should be abstract so that its stability does not prevent it from being extended. Unstable component should be concrete since its instability allows the concrete code within it to be easily changed.

The SAP and the SDP combined amount to the DIP for components. However, for DIP, a class can only be abstract or not abstract, but for SAP and SDP combination, a component can be partially abstract and partially stable.

Dependencies run in the direction of abstraction

Abstraction Measurement

  • Nc: The number of classes in the component
  • Na: The number of abstract classes and interfaces in the component
  • A: Abstractness, range [0,1]. A=0 implies that the component has no abstract classes at all. A=1 implies that the component contains nothing but abstract classes.

The Main Sequence

Below is the graph with A on the vertical axis and I on the horizontal axis.

The I/A Graph

There are 3 important sections in the graph

  1. The Main Sequence
    The ideal positions are all components sit at either (0,1) or (1,0). However, this is not practical. What we can do is trying to let our components fall nearby the line connected from (0,1) to (1,), which called “The Main Sequence” in the book. It is a optimal line for balance between degrees of abstraction and stability.
  2. The Zone Of Pain
    This is the zone where components are highly stable and concrete. Such components are not able to extended since they are not abstract, at the same time, they are hard to change due to their stability. Maintaining or making changes on components falling in this zone will be painful.
    An exception component that fall in this zone without issue is the concrete utility library. This kind of library is considered as nonvolatile due to it seldom make any changes. String class in Java is an example of them.
  3. The Zone of Uselessness
    The components in here are abstract and no dependents. Normally are leftover abstract classes that no one ever implemented.

Distance from The Main Sequence

Distance metric

We can use the metric above to calculate our components’ distance from the main sequence.

  • D: Distance, range [0,1]. D=0 indicates the component is on the Main Sequence. D=1 indicate the value is fall on farthest section from the Main Sequence.

We can use statistical analysis of D on every software release to detect component design issues. We also can find out which component has to be reexamined according to the analysis.

Conclusion

This component section provide a good metric to measure if the component design is a good pattern or not. However, a metric is not a god, please use it wisely and adjust your decision according your needs.

import in Java are so common until I never really put them into consideration when design my code structure. Personally I might still need some practice or real case study before fully understand the concepts explained in the book.

--

--