35 Always Talking About Mvc Layered Architecture, but Do You Really Understand the Layers

35 Do You Really Understand Layered Architecture that You Always Talk About? #

Hello, I am Zheng Ye.

As a programmer, you must have heard of layered architecture, such as the most common three-layer structure of Java backend applications. In the article “15 | Practice Together: Step-by-Step Breakdown of Tasks”, I mentioned:

  • The data access layer, traditionally called DAO (Data Access Object), or repository in Domain-Driven Development (DDD).
  • The service layer, which provides application services.
  • The resource layer, which provides external access resources. In the traditional approach, this is achieved through the controller.

This has almost become the standard pattern for writing Java services. But have you ever wondered why we need to use layered architecture?

Decomposition in design #

In fact, layering is not a particularly intuitive approach. Intuitively, the approach should be to write everything together.

Before programming frameworks became popular, people used to mix pages and logic together. If you have a chance to look at not-so-well-written PHP programs, you will probably see this phenomenon.

Even in the Java community, which values architecture so much, layering didn’t come about until much later. Early JSP and PHP programs didn’t have many fundamental differences.

So why do we need to layer our code? The reason is simple - when code becomes complex, the difficulty of maintaining it increases dramatically. When a problem arises, trying to locate it in code that is all jumbled up is essentially like finding a needle in a haystack.

When discussing task decomposition earlier, I continuously emphasized the idea that people are good at solving small problems. But what about big problems? Just break them down into smaller ones.

Layered architecture is actually a form of decomposition in design.

Going back to the three-tier architecture mentioned earlier, it is the most widely popular architecture pattern in the industry. It started with MVC, which stands for Model, View, and Controller.

The concept of MVC originated in GUI (Graphical User Interface) programming, where people wanted to separate the display part (View) from the data model of the UI (Model) and have the Controller handle the interaction between them. This concept worked well in GUI programming, but only limited to the parts that have interaction with the UI.

Many people mistakenly think that this is also suitable for server-side programs, and they misunderstand the model part as the model in the database or even as a database access. Therefore, you might see someone accessing the database in the Controller.

I don’t know if you’re familiar with Ruby on Rails, but it is a web development framework that changed the industry’s understanding and brought many disruptive practices. It adopts this kind of programming model. When I was writing Rails programs back then, I found that as the business complexity increased, the code became difficult to maintain. I thought about it for a long time and finally realized that the conventional approach in Rails was missing the service layer design.

This problem erupted in the Java field earlier than in Rails because of the superiority of the Ruby language, making data access implemented in Rails very elegant. It was precisely because data access in Rails was too easy that many services were actually written in the Model layer. When the code scale wasn’t large, the code didn’t seem complex and even appeared graceful.

On the other hand, in the Java field, data access had to be written line by line, so it was not feasible to put the code in the Model layer. Putting it in the Controller would also complicate the code. Therefore, the Service layer, specifically designed for business logic, emerged.

With that, the foundation of common Java server-side development is established. However, with the rise of REST services, the Resource layer replaced the Controller layer.

So far, I have explained the origins of the common three-tier architecture in Java services. In reality, layering is almost ubiquitous in software development because good layering often requires good abstraction.

The Ubiquitous Layers #

As programmers, we deal with layers almost every day. For example, programmers are familiar with network programming models, whether it’s the seven layers of the ISO model or the five layers of TCP/IP.

But have you noticed that although you have to learn so many layers when studying, in most cases, you only need to understand the top layer, such as HTTP, when using them.

Many people’s understanding of the lower-level protocols remains at the level of what they have learned because in most cases, unless you need to write a protocol stack, it’s difficult for you to use them. Even if you occasionally use them, 90% of the problems can be solved by search engines, so you rarely have the motivation to learn systematically.

The reason why you can confidently “ignore” the lower-level protocols is that the layered architecture of the network model is implemented so well that as an upper layer user, you can almost ignore the lower layers. And this is the real value of layering: building a good abstraction.

This well-built abstraction can be seen everywhere in software development. For example, as a programmer, you write code that runs on a CPU every day, but have you read the instruction set? The reason you don’t have to understand it is because there are compilers that create layers for you, allowing you to think about problems using the “abstraction” provided by programming languages.

For example, as a programmer writing Java programs every day, do you know how Java manages memory? This is a problem that keeps many C/C++ programmers awake at night, but you don’t have to worry about it because of the “abstraction” provided by Java. By the way, you may not have even noticed that programming languages are also a form of abstraction.

Abstraction and Development #

Only with abstraction can people build more complex things on top of it. If today’s games still rely on pixel programming for the screen, then the development of amazing game visual effects can only be done by a few true experts. Most of our games today should still be at the level of “Super Mario”.

Similarly, in recent years, the front-end field has been booming. But have you ever wondered why the concept of the web appeared early on, but front-end as a specialized position has only flourished in the past decade?

In 2009, Ryan Dahl released Node.js, and people truly realized that JavaScript can be used not only for browsers but also for server development.

As a result, the JavaScript community experienced rapid growth, and various tools that were already popular in other communities finally began to develop in the JavaScript world. It is with the support of these tools that people can build more complex projects using JavaScript, and thus the front-end field has seen great development.

Today, JavaScript has evolved into the only cross-platform language, and of course, the best development is still happening in its stronghold: the front-end field. Front-end developers now have the fortunate dilemma of various front-end frameworks emerging one after another.

Here, the emergence of Node.js has made JavaScript a better abstraction.

Building Your Abstractions #

Once you understand that layering is essentially building abstractions, you may wonder how to apply it to your own work.

The most crucial step in building abstractions is creating your core model. What is a core model? It’s the part of your code that represents your business logic. In other words, everything else may change, but this part cannot.

This may still sound a bit abstract, so let’s go back to the three-tier architecture mentioned earlier.

When discussing the evolution of the three-tier architecture, a transition was mentioned: the rise of REST services, which gradually replaced Controllers with resource layers.

In other words, the way we access services may change. In the context of computer programming, this trend becomes even more apparent, from command-line to network, from Client-Server (CS) to Browser-Server (BS), from browsers to mobile devices. So, how you access services should not be your main concern.

Similarly, relational databases are not your main concern either; they are just a mainstream choice today. In the past, we used files, and today we have various NoSQL options.

So, neither of the two layers in the three-tier architecture is of high importance. What is important then? The answer is quite obvious: it’s the remaining part, which we commonly refer to as the service layer, although this name doesn’t accurately reflect its role. A more appropriate term would be the Domain Model.

This is our core model and the area to focus on when designing software.

Why is “service layer” not a good term? It overlooks an important component of the domain model: domain objects.

Many people have a serious misconception about domain objects, thinking that they belong to the data layer. Data storage is just one use of domain objects; their more significant use is in various domain services.

This misconception leads to another common design mistake: including only data access (i.e., getters and setters) in domain objects, without any logic.

If they are only used for data storage, having data access is enough. However, if they are domain objects, they should have business logic. For example, if you want to allow a user to change their password, the user object should have a “changePassword” method instead of just using “setPassword” every time.

Strictly speaking, domain objects and storage objects should be separate classes, but since they are often very similar, many people tend to use just one class, which is a minor issue. However, many people also apply this internal approach to external scenarios, such as third-party integrations.

Many teams directly use objects from third-party code in their business code. Any changes made to the third-party code will require modifications to your code, causing your team to work tirelessly to keep up.

The best solution to this problem is to keep them separate. Your domain layer should only depend on your domain objects, and any content received from third parties should be converted into your domain objects. This approach is known as an anti-corruption layer.

When we see the domain model as the core of the entire design, our perspective on the other layers also changes. They become merely ways to adapt to different contexts. This concept is what some people refer to as the hexagonal architecture.

Designing a good domain model is a vast topic, and I recommend that you learn about Domain-Driven Design (DDD), which we will discuss further later.

The discussion can still be extended: how can an already built domain model be better utilized by other parts? One good practice is to encapsulate it in a Domain-Specific Language (DSL). However, this is also a substantial topic that won’t be explored further here.

Summary #

I started with the most common three-tier architecture in server-side development and explained their origins. Layered architecture is actually a design decomposition, putting different content in different places to reduce the cost of software development and maintenance.

More importantly, layering provides abstraction. This type of abstraction is ubiquitous in the field of computer science, whether it is programming languages or network protocols, all embody the value of layered abstraction. With layered abstraction, people can better build more complex things on top of abstractions.

In daily work, we should focus on building our own domain models, as they are the core and least changeable aspects of our work.

If there is one thing you can remember from today’s content, please remember: build your domain model well.

Finally, I would like you to think about what other technologies embody the idea of layered abstraction. Feel free to share your thoughts in the comments.

Thank you for reading, and if you found this article helpful, please feel free to share it with your friends.