Photo by Markus Spiske on Unsplash

Clean Architecture -> PART V: Architecture

Muhammed Ali Aydın

--

Chapter 15: What is Architecture

Architecture can be defined as the shape given to a software system by the creators of that system. This, division of the system into components is the arrangement of the components and how they communicate with each other. The purpose of shaping; facilitating development, deployment, operation and maintenance costs. In addition, the main purpose of building a system on good architecture is to keep the programmer’s productivity to the maximum by minimizing the lifetime cost spent on the system.

1. Development

Decisions about architecture often vary according to different developer team structures. While a superstructure structure is unnecessary for a group of small programmers dealing with a small project, it is important to develop the architectural system with minimum effort and maximum productivity for groups of different teams working on larger projects.

2. Deployment

An effective software system must be deployable. As deployment costs increase, the benefit of the system decreases. Therefore, Software Architecture needs to allow the system to be easily deployable with a single action.

3. Operation

The effect of Architecture on operations is less than that of other features. This is because almost every operational challenge can be solved by using more hardware without major changes to architecture. However, this does not imply that architectures where the system’s operations are well organized are undesirable.

That is, architecture needs to create the infrastructure that will allow the system to use the case, feature, and required behavior.

4. Maintenance

Maintenance’s main cost is spelunking and risk. Spelunking is a cost that is needed to find the best strategy and best place to add a new feature to the existing software or to repair a deficiency.

These costs can be drastically reduced in Architecture which has been prepared with a comprehensive thinking. By correctly dividing the system into components and isolating these components with stable interfaces, new features can be added and the risk of problems caused by carelessness can be significantly reduced.

● A good architecture should not depend on a particular database, web server or framework. It should be considered that these decisions have not been made yet. In this way, these decisions can be postponed or changed at any time.

● Architecture should not be device dependent, but should be designed to be applicable to each different device.

Chapter 16: Independence

1. Use Cases

Use cases means that architecture supports what the system intends to do. If the system is a shopping card application, the architecture must also support shopping card use cases.

2. Decoupling Layers

The architecture of a system requires that the structure of the system support all use cases, but does not know these use cases. It only knows the basic requirements of the system. Therefore, the architecture uses SRP (Single Responsibility Principle) and CCP (Common Closure Principle) to distinguish them.

A good architecture separates the UI parts of a use case from the business rule. This makes the use cases visible and clear, allowing them to be replaced independently of each other.

While business rules can be very tightly connected with the application, they can also be in a general relationship. For example, validation of input fields in the application involves a tight connection with the application, but rather a tighter connection with a business rule domain that allows the calculation of interest on money in a bank account. These two different types of rules are written for different reasons and therefore are classified differently (change at different rates, and for different reasons). Therefore, considering these two different classified situations separately, it will allow to be changed independently of each other.

On the other hand, the database is not related to the business rule or UI. Therefore, it must be independent of other parts of the system.

As a result, the architecture must ensure that each of these parts is separated from each other from the other parts of the system so that each part can be replaced independently.

3. Decoupling Use Cases

Use cases may also need to be changed for different reasons. Use cases are the most natural reasons for dividing the system into different parts. If layers (UI, application-specific business rules, application-independent business rules, database) are assumed to be horizontal dividing lines, use cases can be considered as vertical lines that divide this system.

If the system is divided correctly, new use cases can easily be added to the system without the need to intervene with the old use cases.

4. Independent Develop-Ability

The better the layers and use cases can be separated, the better the architecture of the system supports feature, component, layer and other tools, regardless of how they are organized.

5. Duplication

Duplication is often described as a bad thing for a software. Therefore, the developer wants to avoid code duplication.

There are two different types of duplications; true duplication, false or accidental duplication. Two different pieces of duplicated code that appear to be identical and appear in different locations are not considered true duplicates if they are classified as different and written for different purposes.

In the case of use cases, one of the errors generally made during the vertical partitioning of the system is that similar screen structures, similar algorithms, or similar database queries and schemes that use schemas connected to each other. Before qualifying them as interdependent, it is necessary to ensure that they are truly duplication.

Similarly, when the system is horizontally divided into layers, the data structure of a database record is similar to that of a particular data structure. Instead of creating a view model whose elements look the same and whose elements are the same, the developer might consider switching the database record to the UI. This is called accidental duplication. It does not require much effort to create the same view patterns, and this also ensures that the layers are separated from each other in an appropriate manner.

6. Decoupling Modes

There are many ways to distinguish between layers and use cases. It can be done at source code level, binary code (deployment) level or execution unit (service) level.

Source Level: Check the dependencies between the source code modules to see if a change in one module requires replacement or recompilation of other modules.

Deployment Level: Dependencies between deployable units such as jar files, DLLs, shared libraries are checked to see if one module’s source code requires rebuilding or re-deploying others.

Service Level: By reducing dependencies to the level of data structures and communicating only through network packets as each execution unit does, source and binary changes can be made completely independent of the others.

It is difficult to know which of these ways is the best way for the project, in the early stages of the project. In fact, as the project begins to mature, the optimal mode can also vary.

Chapter 17: Boundaries: Drawing Lines

● Database is a tool where business rules are used indirectly. The Business rule does not have to know any details about the schema, query language, or database. All he needs to know is that there is a set of functions that make the corresponding changes in the data.

The database behind an interface

● Thanks to this modularity, which database is used (MySQL, Oracle, Couch, Datomic…) is ignored by business rule.

Chapter 18: Boundary Anatomy

1. Boundary Crossing

Boundary crossing is defined as the function on one end of the boundary calling the function on the other end and passing data to it. The secret to creating an appropriate boundary crossing lies in the good management of source code dependencies.

2. The Dreaded Monolith

The simplest and widely used in architectural boundaries do not have a solid, physical representation. They are usually based on the discipline of separation of functions and data in a single address range and using a single processor. This is called source-level decoupling mode. However, for deployment, this means that a single executable file is executed, which is called a monolith.

The simplest possible boundary crossing can be accomplished by calling a function from a low-level client to a high-level client.

If a high-level client needs to call a lower-level service, dynamic polymorphism is applied to reverse the dependency control flow.

3. Local Processes

Local processes are one of the most powerful physical achitectural boundaries. A local process can be called from the command line or using an equivalent system call. They usually communicate with each other, via sockets, or using a tool provided by the operating system, such as a mailbox-message box.

Each local process can be either a statically linked monolith or a combination of dynamically linked deployment components.

Chapter 19: Policy and Level

Software systems are statements of policy. In other words, a computer program is a detailed description of policies that converts the inputs it receives to output. In most important systems, policy can be broken down to be expressed in smaller policies. The division of these policies into smaller pieces and grouping them according to what has been changed is part of the development of software architecture. At the same time, policies changing at the same level and for the same reason are grouped as belonging to the same component.

Art of architecture says that the grouped components must be in a directional graph without loops. The components of this graph have the same level of policy. The directional edges in this graft represent the dependencies between the nodes and are used to connect components of different levels to each other.

In a good architecture, the direction of these dependencies depends on the level of the component to which they are connected. In any case, low-level components need to be designed to depend on high-level components.

1. Level

The concept of this level, whether high or low, is determined by proximity to input and output. The more distant a policy is from input and output, the higher the level. Those who manage input and output are considered the lowest-level policies of the system.

Chapter 20: Business Rules

Business rules are defined in the book as roughly the rules or procedures for earning or saving business money. To be defined in a superficial manner, these rules should ensure that they are earning or saving business money, whether or not implemented on a computer. These rules are called Critical Business Rules. These rules usually require data to work. This data is also called Critical Business Data. Regardless of whether the system is automated, these data and rules must exist.

Rules and data need to be linked. The objects that link them are called Entity.

1. Entity

Entities are objects that work on Critical Business Data and embody a group of business rules in the system. The interface of an Entity consists of functions where Critical Business Rule’s are implemented.

It does not need to be written in object-oriented language to create an Entity. Although it is usually understood as it should be a class; A separate or combined software module that will allow Critical Business Data to be linked to Critical Business Rule is sufficient to create Entity.

2. Use Cases

Use case is an explanation of how to use an automated system. A use case should specify in detail the input to be provided by the user, the output to be returned to the user, and the steps during which this output is generated. Use cases are also defined as application-specific business rule, in contrast to Critical Business Rule.

It is important, however, that a use case does not define how the system will look to the user and an entitity does not know about the use case that controls it. Entities are defined as high level, while use cases are defined as low level. Therefore, use cases are dependent on Entities.

3. Request and Response Models

Use cases expect an input data and use this data to generate output data. However, a well-designed use case object does not bear any trace of how this data will communicate with the user. In other words, HTML, SQL definitions are not used in use cases.

The use case class uses simple data structure requests for inputs and simple data structure responses for outputs. Again, these data structures are not dependent on anything.

People often think that data structures should contain references to Entitity objects. This makes sense because Entities and request / response models share a lot of data. However, this should be avoided. These two objects exist for different purposes. They will also violate CCP and SRP because of their dependence when they change at different times. This will result in a lot of stray data and a large number of conditions in the code.

Chapter 21: Screaming Architecture

1. The Theme of an Architecture

Architectures are not related to frameworks and should not be supported by any framework. Frameworks are tools to be used and should not confused with architecture.

2. The Purpose of an Architecture

At the center of a good architecture should be use cases. Thus, when designing the architecture, the structure must be designed to support these use cases and not to depend on any framework, tool or environment. A good software architecture followed by framework, database, web server and other environemental issues. The Framework option is always left as an open door.

A good architecture should always be easy to modify after making these decisions (database, webserver, etc.).

3. Frameworks Are Tools, Not Ways of Life

Frameworks can be very powerful and useful. However, the price of this should not be forgotten. The questions that one should ask himself when making this decision are: how to use and how to protect it.

In summary, strategy is needed to prevent the Framework from overriding architecture.

Chapter 22: The Clean Architecture

Architectures need to produce systems with the following characteristics:

● Framework-independent

● Testable

● UI independent

● Database independent

● Independent from any agency

The clean architecture

1. The Dependency Rule

The above circles represent different areas of a software. As you move to the inside circles, the level of software increases. The outside circles represent the mechanisms. The inner circles represent the policies. A circle located inside does not have information about the circles located outside. At the same time, the data structures that will not be used by the inside circles should be defined in the outside circles.

Chapter 23: Presenters and Humble Objects

1. The Humble Object Pattern

Humble Object Pattern is a design pattern that adopts to distinguish unit tests from behaviors that are difficult to test from behaviors that are easy to test. It divided these behaviors into two modules or classes. One of these modules is humble: it contains all the behaviors that are difficult to test.

For example, GUIs are units that are difficult to test. However, some GUI behaviors can be easily tested. In these cases, using Humble Object Pattern, we can split these two types of behavior into two different classes, Presenter and View.

2. Presenters and Views

Views are humble objects that are difficult to test. The codes inside these objects should be kept as simple as possible. These objects show the data in the GUI but do not process the data.

Presenter is a testable object. Presenter’s task is to accept the data from the application and present it in the required format so that Views can be displayed on the screen in a simple way. Presenter stores the data in View Models by converting them to the appropriate format, so that View data can be easily retrieved from View Models and displayed.

3. Database Gateways

Database gateways provide an interface between database operations and other parts.

4. Data Mappers

It is often difficult for people to predict which layer the object relational mapper (ORM, such as Hibernate) will be on.

Objects are not data structures. They are not, at least from the user’s point of view. If the data is set to private, the user cannot see any data in the object. All he can see are the public methods of the object. Therefore, objects are seen by users as clusters of operations.

On the contrary, a data structure consists of only public data variables. Therefore, ORMs are called data mappers because they transfer data from relational databases to data structures.

Back to the top, ORMs are located in the database layer for these reasons. In fact, ORMs are also a type of Humble Object that creates a boundary between the gateway interface and the database.

Chapter 24: Partial Boundaries

Full-fledged architectural boundaries are very costly. In order to be created, mutual boundary interfaces, data structures for input and output, and all other dependency management will be necessary for the two sides to be independently compile and deployed.

In most cases, a good architect can decide that the cost of a boundary will be high, but it must also leave space for this boundary to be created where necessary. Partial boundaries are used for these “or if necessary” cases.

1. Skip the Last Step

One way to create a partial boundary is to create independent components that can do all the necessary work, then collect them all in one component. Everything required (interface, input / output, etc.) is set in the same way, but all can be compile and deploy as a single component.

Although it requires as much code and design as it needs to literally build a boundary, it eliminates the problem of managing multiple components. There is also no payload to track version number or manage release.

2. One-Dimensional Boundaries

In a full-fledged boundary, the separation of maintenance for both sides is very costly in terms of initial setup and maintenance.

The Strategy pattern

As shown in the diagram above, such situations may result in architectural boundaries in the future. Dependency inversion plays a role in the separation of Client and ServiceImpl. It should be clearly stated that this decomposition can rapidly deteriorate, as indicated by dotted arrows. Without mutual interfaces, it will not be possible to prevent such backdoors from the diligence and discipline of developers and architects.

3. Facades

The Facade pattern

Boundaries are defined by Facede classes. In these Facade classes, all services are listed in methods and service calls are distributed to classes that should not be accessed by the client.

4. Conclusion

In this chapter, three different ways of creating a partial boundary are discussed. They all have benefits and bad sides. Each one is suitable for specific situations.

Chapter 25: Layers and Boundaries

AArchitectural boundaries are available everywhere. The task of the architects is to be careful about them and to realize when it is necessary, but also to realize that the boundaries created are literally costly. It should be noted, however, that the subsequent addition of neglected boundaries would be very costly.

Chapter 26: The Main Component

The main component is the lowest level policy of the system. It is the first entry point of the system and everything depends on it. Its mission is to create all the factories, strategies, other global opportunities and control the high-level parts of the system.

Chapter 27: Services: Great and Small

1. Service Architecture

It is thought that using service in the system creates an architecture in a natural way. However, this idea has proved to be inaccurate by being patented.

The architecture of a system is defined as the separation of the high-level policemen from the low-level policemen by using boundaries, taking into account the Dependency Rule. But services alone divide the behavior of the system into different parts.

2. The Decoupling Fallacy

The biggest benefit of the system is that the parts can be separated very strongly. In this way, all services can operate as different processes, even different processors. However, one of the biggest misconceptions is that these services still share resources on the processor or network. What’s more, these services are strongly linked to sharing data.

3. The Fallacy of Independent Development and Deployment

Another default benefit of the services is that the system can be broken down and assigned to development teams. Thus, each team can be responsible for the writing, maintaining and operating of a service. This provides scalable development.

Although some of this thought is true, it is difficult to say that it is completely true. Historically, some large enterprise systems with a monolith and component-based structure are as good as service-based systems. Therefore, services-based architectures are not the only option in scalable system development.

Another misunderstanding is that services do not always provide independent development, deployment, and operated systems. In some ways, such as data, these services may be interdependent.

That is all. For more information, you can read the book “Clean Architecture: A Craftsman’s Guide to Software Structure and Design (Robert C. Martin Series)”.

You can use the link below to go to the beginning of the article.

--

--