02 Architecture Layers Why We Must Do It This Way

02 Architecture Layers - Why We Must Do It This Way #

During the initial phase of building a system from scratch, we usually don’t consider layering in order to quickly bring the system online. However, as the business becomes more complex and a large amount of code becomes intertwined, issues such as unclear logic, module interdependencies, poor code scalability, and the need for extensive changes arise.

At this point, it becomes necessary to layer the system architecture. But how do we go about layering the architecture? And what does architectural layering have to do with high-concurrency architecture design? In this lesson, I will guide you in finding the answers.

What is Layered Architecture #

Layered software architecture is a common design approach in software engineering. It involves dividing the overall system into N layers, each with its own responsibilities, and multiple layers work together to provide complete functionality.

When we first become programmers, we are often “taught” that a system should be designed using the “MVC” (Model-View-Controller) architecture. This architecture divides the overall system into three layers: Model, View, and Controller. It separates the user interface from the business logic and connects them through the controller, effectively decoupling presentation and logic. It is a standard software layered architecture.

img

Another common approach to layering is dividing the overall architecture into presentation layer, logic layer, and data access layer:

  • The presentation layer is responsible for displaying data and receiving user commands. It is the layer that is closest to the user.
  • The logic layer consists of the implementation of complex business logic.
  • The data access layer primarily handles the interaction with storage and retrieval of data.

This is the simplest way to layer an architecture. In fact, we are already unintentionally designing systems using a three-layer architecture. For example, when building a project, we usually create three directories: Web, Service, and Dao, which correspond to the presentation layer, logic layer, and data access layer, respectively.

img

In addition, if we pay attention, we can find many examples of layering. For example, the OSI network model that we learned in college divides the entire network into seven layers, from bottom to top: physical layer, data link layer, network layer, transport layer, session layer, presentation layer, and application layer.

In our work, we often use the TCP/IP protocol, which simplifies the network into four layers: link layer, network layer, transport layer, and application layer. Each layer has its own responsibilities and helps each other. The network layer is responsible for end-to-end addressing and connection establishment, and the transport layer is responsible for end-to-end data transfer, and so on. At the same time, there is data interaction between adjacent layers. This way, the concerns are isolated, and each layer can focus on its own tasks.

img

The Linux file system is also designed in layers, as you can see clearly from the diagram below. At the topmost layer of the file system is the Virtual File System (VFS), which masks the differences between different file systems and provides a unified system call interface. The layer below VFS consists of various file systems such as Ext3 and Ext4. Further down, we have a separate layer, the Generic Block Device layer, which is designed to abstract implementation details of different hardware devices. Below this layer are different types of disks.

As we can see, some layers are responsible for abstracting different implementations of the underlying layers in order to shield the upper layers from implementation details. For example, VFS provides a unified calling interface to the upper layer (system call layer) and defines an implementation model for different file systems in the lower layers. When a new file system implementation is added, it only needs to follow this model to seamlessly integrate into the Linux file system.

img

So why do so many systems need layered design? The answer is that layered design has certain advantages.

What are the benefits of a layered architecture #

A layered design simplifies system design and allows different people to focus on different layers of the system. Imagine designing a network program without a layered architecture - it would be a painful task.

You would need to be an expert in networks and understand the interfaces of various network devices in order to send data packets to them. You would also need to pay attention to the details of data transmission and handle complex issues such as network congestion and data timeout retransmission. Of course, you would also need to ensure the secure transmission of data over the network, preventing others from eavesdropping or tampering with it.

With a layered architecture, you only need to focus on designing the application layer of the program, while leaving the rest to the layers below.

Furthermore, layering allows for high reusability. For example, when designing System A, if we find that a certain layer has a certain level of genericity, we can extract it and use it when designing System B. This reduces the development cycle and improves development efficiency.

Lastly, a layered architecture makes it easier to achieve horizontal scalability. Without layering, when traffic increases, we would need to scale the entire system. However, when we divide the system into layers, as mentioned above, we can scale according to specific issues.

For example, if the business logic involves complex calculations that bottleneck the CPU’s performance, we can extract the logic layer and deploy it independently, and then only scale the logic layer. This is much less costly compared to scaling the entire system.

This also explains the relationship between layered architecture and the design of highly concurrent systems. As we learned in “01 | Highly Concurrent Systems: What Are the Common Design Methods?”, horizontal scalability is one of the common methods for designing highly concurrent systems. Since layered architecture can facilitate horizontal scalability, it is safe to say that a system that can support high concurrency must be layered.

How to Implement System Layering #

After discussing the advantages of layering, what key factors do we need to consider when designing a layered architecture?

In my opinion, the most important point is to clearly define the boundaries of each layer. You may ask, “Isn’t it easy to define the boundaries of each layer if we follow the three-tier architecture?”

That’s right, when the business logic is simple, the boundaries between layers are clear, and when developing new features, we know where to write the code. However, as the business logic becomes more complex, the boundaries become increasingly blurry. Let me give you an example.

Every system has a user system, and the basic interface is a method that returns user information. This method calls the GetUser method in the logic layer, which interacts with the User DB to retrieve data, as shown on the left side of the following diagram.

At this point, a requirement is raised to automatically create a user if they do not exist when displaying user information in the app. However, a separate HTML5 page needs to retain the previous logic, meaning it should not create a user. This blurs the boundaries of the logic layer, as the presentation layer now carries part of the business logic (orchestrating the GetUser and CreateUser interfaces).

img

So how should we handle this? Referring to Alibaba’s Java Development Manual v1.4.0 (Detailed Edition), we can refine the original three-tier architecture into the following structure:

img

Let me explain the role of each layer in this layered architecture.

  • Presentation Layer: Responsible for rendering and displaying templates on various platforms. Currently, it mainly involves Velocity rendering, JS rendering, JSP rendering, and mobile display.
  • Open Interface Layer: Encapsulates service layer methods into open interfaces, and performs gateway security control and traffic control.
  • Web Layer: Primarily responsible for request forwarding, basic parameter validation, and handling simple business that is not reusable.
  • Service Layer: The business logic layer.
  • Manager Layer: The general business processing layer. This layer serves two main purposes. Firstly, it allows you to delegate some common capabilities from the service layer to this layer, such as cache and storage interaction strategies, and middleware integration. Secondly, it allows you to package third-party interface calls, such as payment service or approval service calls, in this layer.
  • DAO Layer: The data access layer, which interacts with underlying databases like MySQL, Oracle, and HBase.
  • External Interfaces or Third-Party Platforms: Including RPC open interfaces from other departments, basic platforms, and HTTP interfaces from other companies.

In this layered architecture, we mainly introduce the Manager layer, which interacts with the Service layer as follows: The Manager layer provides atomic service interfaces, and the Service layer is responsible for orchestrating atomic interfaces based on business logic.

In the above example, the Manager layer provides interfaces for creating users and retrieving user information, while the Service layer is responsible for orchestrating these two interfaces. This way, the business logic that was scattered in the presentation layer is consolidated into the Service layer, making the boundaries clear for each layer.

In addition to the above, another factor to consider in layered architecture is that adjacent layers depend on each other and data flows can only pass between adjacent layers.

Taking the three-tier architecture as an example, once data enters the presentation layer, it must flow to the logic layer for business logic processing, and then to the data access layer for interaction with the database. You may wonder, “Can’t we directly access the data access layer from the presentation layer, or even directly read from the database, if the business logic is simple?”

Functionally, it is possible, but from a long-term architectural design perspective, this will lead to confusion in layer hierarchies. For example, if the presentation layer or the business layer can directly manipulate the database and the database address changes, you will need to make changes in multiple layers, thus losing the purpose of layering. Moreover, it would be disastrous for maintenance or refactoring in the future.

Limitations of Layered Architecture #

Nothing can be perfect, and while layered architecture has its advantages, it also has its limitations. One of the main limitations is that it increases code complexity.

It is obvious that instead of directly querying the database to get the results after receiving a request, we have to insert multiple layers in between, which may simply involve data transfer. Sometimes, even a small requirement change would require modifying code at all layers. This increases development costs and adds complexity to debugging. Previously, if I needed to debug a method by directly accessing the database, but now I have to debug multiple methods at multiple layers.

Another possible limitation is that if we deploy each layer independently and have them interact through the network, multi-layer architecture can lead to performance loss. This is why service-oriented architecture has slightly worse performance compared to monolithic architecture, known as the “one more hop” problem.

So, should we choose a layered architecture? The answer is definitely yes.

You should understand that every architectural solution has its advantages and limitations. If even the vast universe is not perfect, how can we expect our architecture to be? Layered architecture certainly increases the complexity of the system and may have performance drawbacks. However, compared to the benefits it can bring, these limitations are acceptable or can be addressed through alternative solutions. When making decisions, we should not generalize and give up just because of a few drawbacks.

Summary of the Course #

Today, I have introduced you to the advantages and disadvantages of the layered architecture, as well as how we can implement it in our actual work. I want you to understand that the layered architecture is the external manifestation of software design principles, and it is a way of implementation. Some well-known software design principles are reflected in the layered architecture.

For example, the Single Responsibility Principle states that each class should have a single responsibility. In the context of the layered architecture, this can be extended to mean that each layer has a single responsibility, and there is a clear boundary between layers. The Law of Demeter, which states that an object should have as few dependencies as possible, is reflected in the layered architecture by limiting data interaction to adjacent layers and not crossing layers. The Open-Closed Principle requires software to be open to extension but closed to modification. In essence, it separates the abstraction layer from the implementation layer. The abstraction layer generalizes and summarizes common features of the implementation layer, which cannot be modified. However, the specific implementation can be extended infinitely and replaced freely.

Mastering these design principles will naturally lead to a better understanding of the benefits of layered architecture design and help us make better design decisions.