DEV Community

Imamori Daichi
Imamori Daichi

Posted on

Introduction to Clean Architecture for TypeScript: PART3

Refs

Component Principles

This section describes the component-level design principles.
As mentioned earlier, SOLID is a design principle for connections between modules.
In Clean Architecture, a component is a set of modules, which is the unit of deployment.
In other words, a component is a collection of modules designed based on SOLID.
The component principles described in this section guide how to design components.
Some of the principles of the component are similar to SOLID.
This suggests that the main ideas of these principles can be applied recursively regardless of the size of the component.

What is a component?

Components are the units of deployment.
 They are the smallest entities that can be deployed as part of a system.
 In Java, they are jar files. In Ruby, they are gem files. In .Net, they are DLLs.
 In compiled languages, they are aggregations of binary files.
 In interpreted languages, they are aggregations of source files.
 In all languages, they are the granule of deployment.
(Robert C, Martin. "Components". Clean Architecture. Pearson Education.)

In TypeScript, for example, each package managed by npm is a deployment unit and a component.
Web Components, as the name implies, are also components.
What is difficult to determine is what is a component in an application development.
If you divide the compilation granularity appropriately into jar files as in a Java application, they become components.
On the other hand, TypeScript applications are usually transpiled as a whole.
In other words, an application is a single deployment unit.
Of course, we may break it up into chunks to optimize delivery, but that is more to reduce the load on the client side rather than to reduce the granularity of build and deployment.
With this background, it will be difficult to adopt the "component = deployment unit" approach as is.
On the other hand, this doesn't mean that the component principle is useless for designing React applications.
For example, in developing an application, processes that are highly reusable may be separated as libraries.
When this happens, it means that the library is recognized as a separate component from the application.
Have you ever made a library without being sure, because "it's highly reusable" or "it looks better as a library"?
By knowing these principles, you will be able to determine how components should be made independent from the application.

Reuse/Release Equivalence Principle (REP)

The granule of reuse is the granule of release.
(Robert C, Martin. "Component Cohesion". Clean Architecture. Pearson Education.)

The Reuse/Release Equivalence Principle (REP), as the name implies, is the principle that granule of reuse and granule of release should be equivalent.
As an example, consider the reuse of a component A. Suppose that the desired function cannot be reused without another component B.
In this case, the unit of reuse and the unit of release are different.This is an example of "unit of reuse < unit of release".
On the other hand, if "unit of reuse > unit of release", then a single release unit contains multiple granule of reuse.
In this case, if you modify only one reuse unit, you will also need to release it.
From the above, by making the unit of reuse and the unit of release equivalent, the cohesion of components can be improved.

Common Closure Principle (CCP)

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.
(Robert C, Martin. "Component Cohesion". Clean Architecture. Pearson Education.)

ccp

The Common Core Principle of Closure (CCP) is a component version of the Single Responsibility Principle (SRP).
What the two principles have in common is that one element should not be changed for more than one reason.
In the case of SRP, an element is a class, and in the case of CCP, it is a component.

CCP is also closely related to the Open-Closed Principle (OCP).
The "closed" in OCP meant closed to modification.
CCP will change this wording to "combine similar types of changes into one component" to achieve the same goal.
We aim to achieve the same goal.

When you want to cut out a library from your application, you need to pay attention to when that library is going to be modified.
For example, if you want to cut out common UI components to be used in multiple applications, be careful that these UI components are really changed for different reasons than the application.
You also need to pay attention to whether that each element of cut out UI component will be modified for the same reason.
This is because elements that change for different reasons need to be separated into different components.

Common Reuse Principle (CRP)

Don't force users of a component to depend on things they don't need.
(Robert C, Martin. "Component Cohesion". Clean Architecture. Pearson Education.)

The Common Reuse Principle (CRP) is a principle for determining which elements should be combined into components in the same way as CCP.
While CPP focuses on the reason for the change, CRP focuses on the user's usage.

It is also a generalization of Interface Separation Principle (ISP) to components.
What CRP and ISP have in common is that users should not rely on what users do not use.

CRP can help you determine not only which elements should be included in a component, but also which elements should not be included.
By assuming the user who will use the component, we can ensure that the component does not contain elements that will not be used by that user.

In general, I think there are multiple possible use cases for a component.
For example, there are libraries for date manipulation such as Moment.js.
This library is divided into two components: Moment and Moment Timezone.
This is because there are two use cases where you want to perform date manipulations: one where you want to take timezone into account, and one where you do not.
If we follow the CRP approach, we should avoid forcing users who do not need timezone handling to rely on the timezone module.
In other words, assuming that the use case of not using timezone is not negligible, this kind of component division in Moment.js satisfies the CRP.

Acyclic Dependencies Principle (ADP)

Allow no cycles in the component dependency graph.
(Robert C, Martin. "Component Coupling". Clean Architecture. Pearson Education.)

Acyclic Dependency Principle (ADP), as the name implies, calls for dependencies to be acyclic.
In TypeScript, it can be detected by eslint rule import/no-cycle.
It is easy to imagine that the cycle of dependency can lead to many unexpected problems.
For example, if class A and class B depend on each other, it is easy to detect and imagine the possible problems.
In an extreme case, if class A and class B instantiate each other in the constructor, the process will loop.
A little more confusing is the case of indirect circular dependence. In this case, the problem is that a modification of one side indirectly affects the other.
Suppose that class C and class D are indirectly circularly dependent on each other. Then, changes to class C will have some effect on class D.
Then class D will be modified to accommodate the changes in class C.
In this case, class C also depends on class D, so class C is also affected by the modification of class D.
This is even though the modifications to class D were intended to address changes in class C.
Thus, the cyclic of dependencies means that the effects on change are cyclic.
For products with larger applications and more developers, these issues can be hard to ignore.

In order to solve the circular dependency, we need to rethink the division of components.
Specifically, we will cut out the elements that class C and class D have in common and make them into new components.

Stable Dependencies Principle (SDP)

Depend in the direction of stability.
(Robert C, Martin. "Component Coupling". Clean Architecture. Pearson Education.)

Stable Dependency Principle (SDP) defines the relationship between dependency and stability.
As explained in SOLID's Dependency Inversion Principle (DIP), code stability and dependency direction are closely related.
SDP gives the details of the relation.

Stability

So how is the stability level determined?
In Clean Architecture, stability is defined as "something that cannot be easily moved".
A component that is "hard to change" is said to be a stable component.
In other words, it is dependent on many components.
This is because if you change a component that is dependent on many other components, it will affect many other components and they will need to be adjusted.
The number of dependencies from other components determines the stability of that component.
If you want a component to be stable, design it to be dependent on other components.
On the other hand, components that are expected to change a lot need to be easy to modify, and thus need to be unstable.
To make a component unstable, make it as independent from other components as possible.

In "Clean Architecture", a quantitative measure of stability is presented.
If you are interested in this topic, please refer to "Clean Architecture".

Stable Abstractions Principle (SAP)

A component should be as abstract as it is stable.
(Robert C, Martin. "Component Coupling". Clean Architecture. Pearson Education.)

Stable Abstractions Principle (SAP) specifies the relationship between stability and abstraction.
In the previous section, we explained that stability can be specified by dependencies.
And SAP says that the level of abstraction must be determined by the level of stability.
So why does a highly stable component also need to be highly abstract?
As an example, let's look at the business domain.
Your business domain is the core element of your business, and you want it to be stable.
Therefore, it should be designed to be dependent on other components to ensure a high degree of stability.
But then, the high stability of the business domain makes it difficult to fix.
This makes the business inflexible, and the architecture becomes a bottleneck for the business.
So, to make it easier to make changes while maintaining a high level of stability, we use a high level of abstraction.
Design the business logic to a high level of abstraction through interfaces and abstract classes, and make other components depend on these.
This way, modifying the business logic within that level of abstraction will not affect other components.

SDP + SAP = DIP

Stable Dependency Principle (SDP) says, "Depend on the less stable to the more stable".
Stable Abstractions Principle (SAP) says, "If something is less stable, make it less abstract. If it is more stable, make it more abstract".
Putting these two principles together, we can say, "Depend on the lower level of abstraction to the higher level".
This is exactly what Dependency Inversion Principle (DIP) is.
Of course, DIP is a module-level principle, while SDP and SAP are component-level principles, so strictly speaking, they cannot be put together.
However, through the explanations I have given so far, I think the relationship between these three principles has become clearer.

NEXT: Introduction to Clean Architecture for TypeScript: PART4.

Ads

This article is an excerpt from the first chapter of the book "React Clean Architecture". Some wording has been changed. You can buy the kindle version of this book from amazon.

React Clean Architecture

Latest comments (0)