System Architecture: Layered Architecture

Primer

When the system is in the stage from 0 to 1, in order to allow the product to go online quickly, system layering is generally not a major area of ​​software development. However, as the business becomes more complex and a large number of codes are entangled and coupled, the logic will be unclear at this time. , The modules are interdependent, the scalability is poor, and the problem of changing one part affects the whole body.

When the system is from 1 to 100...0, the system will become particularly complicated, and system stratification will be put on the agenda at this time, so what is stratification? Why layering? How to layer software? What are the guiding principles for software layering? This article will take you to find the answers one by one.

What is a layered architecture?

Layered architecture is a divide-and-conquer design idea and a layer-based architecture paradigm. Generally, developers will split a system into multiple modules or subsystems based on the concept of divide and conquer; then divide the split modules into groups of related dependencies and hierarchical relationships according to the business level; so as to realize functions according to functional groups and coupled isolation. We call this paradigm the layered architecture paradigm.

Example of layered architecture

Figure 1 Android system architecture diagram
Figure 2 Android architecture layer relationship diagram

The most typical example of a layered architecture is the Android operating system, which is a Linux-based mobile open source operation. The Android system adopts a top-down layered paradigm. Figure 1 is the official Android system architecture diagram provided by Google, and Figure 2 is the Android system The relationship between the five layers of the diagram.

The five layers of the Android operating system architecture are:

  • application layer applications
  • Application framework layer frameworks
  • System runtime layer native c/c++ libraries/android runtime
  • hardware abstraction layer hardware abstraction layer
  • Linux kernel layerlinux kernel

Why layering?

In the stage of software development from 0 to 1, developers generally achieve business isolation through module isolation. At this time, module isolation is the "focus" of software isolation; and when the system is from 1 to 100...0, because the system is in a super-large-scale state, Segregation by module is no longer practical. At this time, almost every software isolates the "points of concern" through layers to cope with changes in different requirements, so that such changes can be carried out independently, thus bringing the following benefits to developers:

  • High cohesion: layered design can simplify system design, allowing different layers to focus on their own business
  • Low coupling: Layers interact through interfaces or APIs, and the relying party does not need to know the details of the dependent party
  • Reuse: High reusability can be achieved after layering
  • Scalability: layered architecture can make it easier for us to do horizontal expansion

In addition, the layered architecture paradigm is also a powerful tool to isolate business complexity and technical complexity. "Domain-Driven Design Patterns, Principles and Practices" has such a discussion:

To avoid turning the code base into a big ball of mud and thus weakening the integrity of the domain model and ultimately usability, the system architecture needs to support the separation of technical complexity from domain complexity. The reasons for changes in technical implementations are clearly different from the reasons for changes in domain logic, which leads to infrastructure and domain logic issues that change at different rates.

The "changes at different rates" here actually mean that the reasons for the changes are different, which happens to be the embodiment of the Single-Responsibility Principle (SRP), which is why the business and infrastructure should be separated. Because the reasons for their changes are different.

To sum up, the essence of the layered paradigm is to simplify complex issues, let each layer of code perform its duties based on the principle of single responsibility, and realize the interaction between related layer objects based on the design idea of ​​"high cohesion and low coupling". This improves code maintainability and scalability. In small-scale software, the focus of responsibility isolation is modules or classes. In large-scale software design, the focus of responsibility isolation is layers. We isolate changes through layers, which is the ultimate goal of software layering.

How to layer?

There are many principles in the layering of software. Here, the author first analyzes and introduces the four principles of software layering, and then introduces a gradually refined layering proposed in "Pattern-Oriented Software Architecture: Volume 1 (Pattern System)" Architecture approach.

Guiding Principles

The reason why software needs to be layered is actually our subconscious cognitive rule: machine-oriented, user-oriented. Machines are the foundation on which software runs, and the systems we build serve users. The higher the layered architecture level, the more business-oriented and user-oriented its abstraction level is; the lower the layered architecture level is, the more unified its abstraction level becomes, and the more device-oriented it is. The classic three-tier architecture is derived from this cognitive rule: the upper layer focuses on user experience and interaction; the middle layer focuses on applications and business logic; the lower layer focuses on external resources and devices. Therefore, the first principle of layering is to divide different businesses into layers based on concerns.

The second principle of layering is to isolate changes. When layering, determine the boundaries of the layers according to different reasons for changes, at least to ensure that the impact of changes on each layer is minimized, but this is only the minimum goal, and the maximum goal is to strictly prohibit the interaction between layers. interference. For example, the modification of the database should only affect the data model of the infrastructure layer and the domain model of the domain layer, but if only the database access logic of the infrastructure layer is modified, it should not affect the domain model of the domain layer.

The third principle of layering is that the layers should be orthogonal. The so-called orthogonal does not mean that there is no relationship between the two layers, but that the two should be two straight lines that intersect perpendicularly. The only point of dependence between the two is the intersection of the two lines, that is, the collaboration point between the two layers. This point of collaboration is the abstract interface between the layers. Two orthogonal straight lines, no matter which straight line is extended, will not have any influence on the other straight line (the projection of the straight line). If it is non-orthogonal, when a line is extended, it will always project to another line, which means that the other line is affected by the extension of this line.

Another principle of layering is to ensure that components of the same layer are at the same abstraction level. This principle is borrowed from the "combinatorial approach" pattern proposed by Kent Beck in the book "Smalltalk Best Practice Patterns". This pattern requires all operations in a method to be at the same level of abstraction, which is called the "Single Level of Abstraction Principle (SLAP)".

layered guidance

After understanding many layering principles, let me introduce a gradually refined layered architecture method proposed in "Pattern-Oriented Software Architecture: Volume 1 (Pattern System)".

In the first step, abstract criteria for dividing tasks into different layers are defined. In real software development, the lower layers are usually divided according to the distance from the hardware, and the higher layers are divided according to the complexity of the concept.

The second step is to determine the level of the abstraction layer according to the abstraction criterion.

The third step is to name each layer and assign tasks.

The fourth step is to standardize services and ensure that any components or modules do not cross layers. While placing more components into the higher levels, the lower levels are kept slim. Thus forming an inverted pyramid.

The fifth step is to improve the hierarchical division and repeatedly execute the first to fourth steps.

The sixth step is to standardize the abstract interface of each layer to ensure that for the J+1 layer, the J layer should be a "black box". Design a unified interface that can provide all services of the J layer.

The seventh step is to determine the structure of each layer. Determining the hierarchical structure not only ensures that the relationship between layers is reasonable, but also requires that the components within the layer have appropriate granularity. In this way, the situation where the inter-layer relationship is perfect but the inner-layer relationship is chaotic is avoided.

The eighth step is to standardize the communication mechanism of adjacent layers. In the layered architecture, the commonly used communication mechanism is the push model. When the J layer calls the J-1 layer, all the required information is transmitted along with the service call.

The ninth step is to decouple adjacent layers. In general, in a layered architecture, the upper layer knows the lower layer, but the lower layer does not know the identity of the upper layer, thus forming a one-way coupling.

Collaboration between layers

In everyone's inherent cognition, the layered architecture is passed from top to bottom. From the perspective of abstraction level, the higher the abstraction level, the more general and public, the farther this level is isolated from the specific business. This level is what we usually call the platform layer or the caller of the framework.

Top-down requires the upper layer to depend on the lower layer, which conflicts with the Dependency Inversion Principle (DIP). The Dependency Inversion Principle requires that high-level modules should not depend on low-level modules, both should depend on abstractions. This principle gave me a clear reminder that whoever stipulated that in the top-down architecture, dependencies should be passed along the top-down direction, which is a wrong understanding. The underlying essence of the Dependency Inversion Principle is that we depend on unchanging or stable elements (classes, modules or layers). That is the second sentence of the principle: abstractions should not depend on details, and details should depend on abstractions.

The Dependency Inversion Principle is a manifestation of the "interface-oriented design" principle, that is, "programming for the interface, not for the implementation". The high-level module knows nothing about the implementation of the low-level module, and the benefits are:

  • The detailed implementation of low-level modules can be changed independently to avoid changes affecting high-level modules
  • Both high-level and low-level modules can be compiled independently of each other, thus achieving independent development and compilation
  • For high-level modules, the implementation of low-level modules is replaceable

If both high-level and low-level rely on abstraction, then there will be such a problem now, how to pass the specific implementation of the bottom layer to the high-level layer? Since the high level isolates the dependence on the concrete implementation through the "orthogonal point" of the two, that is, the abstract interface, then the dependence on the concrete implementation is transferred to the outside, and the concrete implementation will be determined by the external caller. The caller will pass the underlying specific implementation to the high-level when calling the code. Software development guru Martin Fowler vividly calls this mechanism "dependency injection".

Therefore, in order to well relieve the high-level dependence on the bottom layer, we need to combine dependency inversion and dependency injection to better understand them.

In addition, the information transfer between layers is not necessarily top-down, but may also be bottom-up. For example, the notification mechanism in the Android system. When the Android system receives the message, the Android system notifies the upper layer business module of the message. If the top-down message delivery is described as "request" (or call)", then the bottom-up message can be described as "notification". Thinking about "notification" from another angle, this is actually the communication between the upper layer and the lower layer. Observation, the state of the lower layer changes, and the change of the lower layer is passed to the upper layer through the observation mechanism. The upper layer consumes the message transmitted to the lower layer. This mode is often called the observer mode.

Whether it is to realize the "request (or call)" from the upper layer to the lower layer through the principle of dependency injection and dependency inversion, or to realize the "notification" from the lower layer to the upper layer through the observer mode. These have subverted our inherent understanding of the high-level dependence on the low-level in our thinking.

Now we have a clearer understanding of inter-layer assistance, so we must face up to the collaborative relationship between layers in the architecture during development, break the inherent thinking of high-level dependence on low-level, and explore from the perspective of decoupling (or reducing coupling) Possible collaborative relationships between layers.

In addition, we also need to determine the layered architectural principles (or constraints), such as whether to allow cross-layer calls, that is, each layer can use the services of all layers lower than it, not just adjacent lower layers. This is the so-called "loose layered system (Relaxed Layered System)".

Classic Hierarchical Paradigm

The classic layered paradigm mainly includes the classic three-tier architecture, the Ali four-tier architecture, and the DDD domain-driven layered architecture.

Classic three-tier architecture

The classic three-tier architecture divides system functional modules into presentation layer (UI), business logic layer (BLL) and data access layer (DAL) according to their functions. The three-tier architecture is shown in Figure 3. The presentation layer UI is located at the top of the three-tier architecture, which realizes the direct interaction between the system and the user, and the processing of message events; the business logic layer BLL, which realizes data processing and data transmission, and the interface It is connected with the data access layer to play a link between the past and the future; the data access layer DAL implements operations such as adding, deleting, modifying, and querying data, and feeds back the operation results to the business logic layer BLL.

Figure 3 Three-tier architecture

Alibaba's four-tier architecture

Alibaba's four-tier architecture adds a Manager layer to the original three-tier architecture, and refines the original classic three-tier architecture into an architecture diagram as shown in Figure 4

Figure 4 Alibaba's four-tier architecture diagram

  • Open interface layer:
    1. The open interface layer can directly depend on the Service layer or the WEB layer;
    2. Depending on the Service layer, the Service can be encapsulated into RPC and exposed to the outside world;
    3. Relying on the WEB layer, the business can be encapsulated into HTTP and exposed to the outside world.
  • Terminal display layer:
    1. Responsible for template rendering and display at each end;
    2. Currently, it is mainly velocity rendering, JS rendering, JSP rendering, mobile terminal display, etc.;
  • Web layer:
    1. Forwarding of access control, verification of various basic parameters, or simple processing of non-reusable services, etc.;
  • Service layer:
    1. Mainly responsible for the implementation of specific business logic services;
  • Manager general business processing layer, the main responsibilities include:
    1. Encapsulation of third-party platforms: encapsulation of common basic components of the Service layer, such as cache and general processing of middleware;
    2. Interact with the DAO layer and reuse multiple DAOs
  • DAO layer:
    1. Data access layer, data interaction with MySQL, Oracle, Redis, etc.

DDD layered architecture

DDD domain-driven layered architecture, domain-driven design is further improved on the basis of the classic three-tier architecture, and an application layer is introduced between the top user interface layer and the business logic layer. After the introduction of the application layer, the names of each layer have also been adjusted according to the domain-driven concept. Rename the business logic layer to the domain layer, and rename the data access layer to the infrastructure layer (Infrastructure Layer). Figure 5 shows the layered architecture of Eric Evans in his classic book "Domain Driven Design".

Figure 5 DDD domain-driven layered architecture

Summarize

We now have a clearer view of the layered architecture. Therefore, we must break the inherent thinking that talking about layered architecture must be the classic three-tier architecture or the four-tier architecture recommended by domain-driven design, and instead regard layering as a manifestation of the horizontal abstraction level of separation of concerns. That being the case, the number of layers of abstraction in the architecture is not fixed, and even the names of each layer may not follow the inherent (classic) layered architecture requirements. The layers of the design system need to be determined in combination with the specific business scenarios of the system. Of course, we also need to realize the advantages and disadvantages of the number of layers: too many layers will introduce too much indirection and increase unnecessary expenses, and too few layers may lead to insufficient separation of concerns, resulting in an unreasonable system structure.

The layered architecture paradigm is the most commonly used and easily abused architecture paradigm in software development. Developers can formulate their own layers according to the current software development constraints and requirements, so as to facilitate software development and developer assistance. Based on the layered paradigm, we can modify the code in the module without affecting other modules, which is the advantage that software layering brings us. It is also a characteristic of high cohesion and low coupling.

Guess you like

Origin blog.csdn.net/liuguang841118/article/details/128621374