Layered (N-Layer) Architecture with SOLID Design Principles
In this article, we are going to learn Layered (N-Layer) Monolithic Architecture and how Layered (N-Layer) Architecture still so useful for current projects.
By this article, we are going to learn Layered (N-Layer) Monolithic Architecture, Benefits and Challenges of Layered (N-Layer) Architecture and Design our E-Commerce application with Layered (N-Layer) Architecture.
I have just published a new course — Design Microservices Architecture with Patterns & Principles.
Architecture Design Journey
There are many approaches and patterns that evolved over decades of software development, and all have their own benefits and challenges.
In the last article, we have learned and designed e-commerce application with Monolithic Architecture style. Now we will focus on how it can be separate logical layers in order to provide better organize code and isolation.
- Why we are learning Layered Architecture ?
Because according to our learning flow, we ask ourselves
- What's wrong with this current architecture ?
- How can we improve the current design ?
With these questions we created a problem about Monolithic architecture and try to solve this problem during the article. Problem is:
Problem is Code Become Too Complex Over Time. And as a solution we have decided to learn Layered Architecture with SOLID Design Principles.
Introduction — Layered (N-Layer) Architecture
The layered architecture pattern is the most commonly used architecture pattern in software engineering. This architectural pattern is also known as the N-tier architecture style or the multi-layered architecture style.
Layered (N-Layer) Architecture is a software design pattern that organizes an application’s components into separate layers, each responsible for specific tasks. In this architecture, each layer only communicates with the layers with a clear separation of concerns. The main purpose of a layered architecture is to organize the components of an application with similar functionalities into horizontal logical layers.
In these frameworks, components that are related or that are similar are usually placed on the same layers. However, each layer performs a specific role within the application.
So when we said separating horizontal logical layers, we should clearly understand that these layers are logical not physicals layers. So that means we are still using Monolithic architecture with 1 application server but inside of this 1 application server we have separated logical layers to organize similar components into code.
Layered architecture patterns are N-tiered patterns where the components are organized in horizontal layers. This is the traditional method for designing most software. This means that all the components are interconnected but don’t depend on each other.
To summarize we can say that Layered Architecture is about organizing code for separation of concerns. The main characteristic of Layered Architecture is layers of isolation. This means that layers can be modified and the change won’t affect other layers. That means, changes are isolated to the specific layer which is modified.
Components of a Layered Architecture
If we look at the Components of a Layered Architecture, the most common usage is 3 layers: Those are
- User Interface — Presentation layer
- Business Logic — Business layer
- Data Access Layers
Presentation Layer
This layer is responsible for handling user interface logic, displaying information to users, and collecting user input. It can include components such as web pages, mobile app screens, or desktop applications.
Business Logic — Business layer
The Business layer contains the business logic of the application. It handles user requests, processes data, and performs operations that do not involve data storage or retrieval. This layer can also include components that perform data validation, error handling, and workflow management.
Data Access Layers
The Data Access layer provides the underlying technical infrastructure and services needed by the other layers. It includes components such as database access libraries, file system access, network communication, and other low-level system services.
Other layers may include middleware, batch processing, and API. It’s important to note the layers are logical. Although they’re developed in isolation, they may all be deployed to the same target platform.
Why we use Layered Architecture in Monolithic applications ?
We use Layered Architecture in Monolithic applications to promote modularity, maintainability, and scalability. It makes it easier to manage complex applications by breaking them down into smaller, more manageable components, making it easier to test, maintain and update. By separating the application into different layers, we can also improve the security of the application and promote code reuse.
After learning the basics of N-Layer architecture, we should also understand the principles that we will apply on our architecture afterwards.
Design principles — Separation of Concerns (SoC)
Think about that what is the motivation the use N-Layer architecture ? Why we separate the layers to N ? What is the underlying principle to create this N-Layer architecture. This principle is Separation of Concerns (SoC).
SoC says that the elements in the software should be unique to them,
not to share their responsibilities with other staff members. While developing the software, we can see the necessary of separating responsibilities with following this principle.
We can say that, we distinguish between the concepts of layer and tiers with certain responsibilities. If we go further a little more, the namespaces of our software components can also be used as limits to allocate responsibilities.
The dependencies of the components within the software and
the cohesion within the components are two important concepts for the SoC principle. It should always be preferred that the dependency of the components is low and that the relationship of responsibility in a component is high.
So low-coupling, high-cohesion is indispensable. If the dependency of commitment is low, then the control of the software will be easier in our hand, since the liability will be distributed per component. Being close to each other in their responsibilities within the components will becomes re-usability.
We can give an example of SoC that development of Web UI. As you can see that UI element button needs to configure by 3 structure; HTML — CSS — JS.
Design principles — SOLID
SOLID is one of the main principle groups to use almost every software projects.
This one though, stands for:
- Single Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
So if you think about it, it’s really 5 principles all packed into one.
So let’s unpack them one by one.
Single Responsibility
The idea is that each of your classes (or modules, units of code, functions, etc) should responsible only one functionality. Why ?
Because if you want to change your code, you should first have a good reason, then you should know where is that single point where that reason applies. We can use this base principles when designing the system also.
Open-Closed Principle
When you write your code, the last thing you want to do is go back to it and change it again and again whenever you implement a new functionality. You want the legacy code to work, be tested to work, and allow new functionality to be built on top of it.
In the same way, when we design the system, it should able to extend without changing existing architecture. For example using pub/sub pattern with message brokers is a good example for that. If we want to notify some other microservices, instead of calling their services, we can publish an event and they can consume them from message broker.
By this way, if there will be a new microservice need to consume this event, we don’t change anything for existing architecture, the only thing to subscribe that event from message broker that’s all. So our design open for extensions and close for modifications.
Liskov Substitution Principle
I am not going to give deep explanation about that. Because we are looking this principles from the system design perspective. So we can say that, our systems can be substitute each other easily.
In our case we can use plug-in services that we can shift them easily, For example if we use RabbitMQ as a message broker, we can shift to Kafka easily with abstracting Event Bus implementations.
Interface segregation principle
It basically says split your interfaces — meaning the desired functionality of your code — into pieces that are fully used.
Dependency Inversion Principle
Also known as dependency injection. If you have dependencies in your code, you should allow for them to be injected into your logic.
How do you do that ? Of course By allowing them to be passed in as parameters.
This is a very useful principle that we can use system architecture design.
We don’t want to dependent microservices and direct communication due to possible network problems. In order to broke this dependencies, we can use message brokers and event-driven architecture that provide to de-couple services. As you can see that we understand SOLID principles and how we can use them when architecting system designs.
Design Layered Architecture — E-Commerce App
If we design e-commerce application with Layered Monolithic architecture, you can see the image below:
There is a big single Monolithic Application Server but Application has logical layers which’s are UI, Business and Data Access. And there is one big relational databases.
Design Microservice Architecture — E-Commerce App
If we design e-commerce application with Microservice architecture, you can see the image below:
Product microservice can use NoSQL document database Shopping Cart microservice can use NoSQL key-value pair database and Order microservice can use Relational database as per microservice data storage requirements.
What’s Next ?
- Monolithic Architecture is Shining
- Macro-services to Nano-services: Evolution of Software Architecture
- Microservices Architecture: Problems and Solutions with Pattern and Principles
Step by Step Design Architectures w/ Course
I have just published a new course — Design Microservices Architecture with Patterns & Principles.
In this course, we’re going to learn how to Design Microservices Architecture with using Design Patterns, Principles and the Best Practices. We will start with designing Monolithic to Event-Driven Microservices step by step and together using the right architecture design patterns and techniques.