I’ve used n-tier architecture in projects that I’ve developed for a long time. Apart from the classically used BAAL(Business Access Layer), DAL(Data Access Layer), and PL(Presentation Layer), also these layers may separate themselves.

It sounds good to have each task done by a different layer but after a short time, things are getting mixed up.

Recently, we decided to develop code with Vertical Slice Architecture in a project consisting of multiple microservices. I want to share what I’ve experienced in this process, what are the pros and cons.

N-Tier Architecture

This visual is one of the best describing n-tier architecture. You have seen many pictures like this.

N-Tier Architecture

N-tier Architecture divides the application into logical layers. It is a method used to separate the responsibilities of each layer and eliminate dependencies. The responsibility of each layer is different. The layers are arranged in a logical order in accordance with the hierarchy.

Exam :
The presentation(PL) layer can’t directly communicate with the data(DAL) layer. Talks with business layer(BAL), doesn’t know what’s in the data layer. Likewise, the data layer(DAL) exchanges data with the (BAL) business layer, not directly communicating with the presentation layer(PL).

Vertical Slice Architecture

One of the most illustrative visuals explaining vertical architecture is this. The mastermind behind Vertical Slice Architecture is Jimmy Bogard, who has provided us with packages like ‘AutoMapper, MediatR’

Vertical Slice Architecture

In a multi-layered architecture, communication between layers occurs horizontally, whereas in vertical architecture, as the name suggests, communication happens in vertical blocks. Essentially, instead of communication, we can say it finishes its work internally without directly notifying the outside world. :)

To elaborate further, in a multi-layered architecture, when we want to perform a new CRUD operation, we often need to touch many layers. Let’s take an example using Corex.Sample.

Let’s say we need an endpoint named ‘UserRegister.’ This endpoint will receive parameters ‘Email’ and ‘Password’ through the API, and upon receiving this data, it will create a user record. After creating the record, instead of returning all user information, let’s say our scenario dictates that only the ‘Email’ information will be returned.

  1. Creating separate classes for ‘Inputs’ and ‘Outputs’ within ViewModels allows for greater flexibility in case of changes in the input/output of the Register process. By establishing these classes, adapting to alterations becomes much easier.
For Inputs, these are the parameters obtained from the page, while Outputs represent the result I’ll return after the registration process is completed.

2. You provide the UserRegister method within the IUserRepository interface and then implement this method in a derived class.

The IUserRepository interface contains a method named ‘UserRegister,’ which is implemented by the ‘UserRepository’ class.

3. The validation layer requires me to write validations for Input and DTO (Data Transfer Object) models mapped to entities.

In InputValidation, I specify validations for ‘UserRegisterInputModel,’ while in DtoValidation, I outline the validations necessary before the user registration process.

4. The ‘UserRegister’ method added to the UserRepository requires us to make additions to the Unit of Work. In this case, UserDataOperation handles operations related to repositories.

Because we want to avoid direct interaction between repositories and the Presentation Layer, we’re adding it to UserDataOperation.

5. So, the final step involves adding the ‘Register’ method to UserOperationManager. This allows UserDataOperation to interact with UserValidationOperation, completing the process.

Only the Presentation Layer interacts with UserOperationManager.

Over the years, I’ve employed a multi-layered architectural approach in the projects I’ve developed and guided my team in that direction. It wasn’t too difficult for me to move away from this approach, although I haven’t entirely abandoned it. There will still be places where it’ll be used. However, I can say that I’ve now prioritized Vertical Slice Architecture. We’ll discuss its positives and negatives separately.

In Vertical Slice architecture, each request is referred to as a ‘feature.’ I’m talking about a structure where the feature defines its input and output and directly incorporates business validations without needing to touch multiple layers as shown in the n-tier approach. What I used to do horizontally in the n-tier approach, I can accomplish vertically here.

Vertical Slice Features

Using CQRS and MediatR with Vertical Slice makes it quite straightforward. This is why requests labeled in blue such as ‘GetOrders’ and ‘GetOrderDetails’ are called ‘Query Features,’ while those marked in red like ‘Approve Invoice’ and ‘Cancel Order’ are termed ‘Command Features.’

I’ll be sharing a detailed example project shortly, but before that, I’d like to share a visual for better understanding.

GetUserById

Let’s think from a developer’s perspective. Imagine being asked to create an action that takes an “Id” parameter and returns a “User” record. In such cases, we should immediately consider the concept of a feature. That would mean we need a feature named “GetUserById.”

In a multi-layered architecture, we’d typically address this within the Business Access Layer (BAL) of the User using a method named “GetById.” However, in a vertical architecture, we see this as a new feature.

Due to the vertical alignment of Request, Response, and the Handler handling the action, any changes we make are specific to this feature alone. So, when a future enhancement comes in, I’m confident that the changes I make will only impact this particular feature. Hence, in vertical architecture, we should avoid using abstract classes as much as possible.

Using an abstract class increases our dependency on the outside. One might think, “Why not use abstract classes then?” Trust me, I’ve been one of the biggest fans of abstract classes. However, as the number of classes derived from abstract classes increases, management becomes challenging.

The feature structure provides a clear path for writing precise integration tests. I know best if the feature I’ve written meets my requirements. By immediately applying tests to the changes I make, I can ensure the consistency of the code effortlessly.

Now, let’s move on to the advantages and disadvantages.

Advantages of N-Tier Architecture:

  1. Ease of Developer Adaptation: Due to the heavy use of abstract classes, developers can learn less code and adapt quickly.
  2. Clear Definition of Responsibilities for Each Layer: Sharp delineation in defining the roles of each layer.
  3. Scalability through Multiple Layers in Growing Projects: Allows flexibility by opening up numerous layers as projects expand.

Disadvantages of N-Tier Architecture:

  1. Long Compilation and Deployment Times: Due to the abundance of class libraries, compiling and deploying can take longer.
  2. Time Loss due to Working Across Multiple Layers when Adding New Features: Adding a new feature requires work across multiple layers, leading to time loss.
  3. Reduced Developer Productivity and Risk of Issues with Abundant Abstract Classes: The abundance of abstract classes can hinder developer growth and alterations in abstract classes may lead to significant issues.
  4. Excessive Layering Leading to Delay in Delivering Immediate Value: Due to rigid rules among layers, the delivery of immediate value might take longer than necessary.
  5. Potential for Understanding Confusion over Time: Over time, the “baklava” or “spaghetti” code problem can confuse where specific actions reside and how they interact among layers.

Advantages of Vertical Slice Architecture:

  1. Reduced Compilation and Deployment Times due to Fewer Class Libraries: Since there are significantly fewer class libraries, compilation and deployment times are considerably shorter.
  2. Facilitates Development with a Single Class when Adding a New Feature: Adding a new feature can be developed using just a single class.
  3. Developer Freedom within Each Feature for Ease of Modification: Developers have freedom within each feature, enabling them to comfortably make edits, ensuring that changes only affect the specific feature they’re working on.
  4. Alignment with CQRS Structure due to Feature-Based Development: Development on a feature basis aligns well with the Command Query Responsibility Segregation (CQRS) structure.
  5. Enhancement of Developer Competence by Having Abstract Classes as the Lowest Level: With abstract classes being at the lowest level, developers can enhance their skills.

Disadvantages of Vertical Slice Architecture:

  1. Significance of Developer Competence in a Structure with Minimal Use of Abstract Classes: Developer competence becomes crucial in a structure where abstract classes are minimally used.
  2. Possibility of Code Redundancy due to Each Feature’s Independence: Since each feature is independent, there might be a possibility of code redundancy.

In our next piece, we’ll be crafting a sample project. I’ll be creating a GitHub project using Vertical Slice architecture while incorporating CQRS and leveraging MediatR.

Sources :

--

--