Search  
Always will be ready notify the world about expectations as easy as possible: job change page
Jun 21

The core question: Are Vertical Slices just modular monoliths

The core question: Are Vertical Slices just modular monoliths
Author:
Source:
Views:
2189

A few days ago I was asked if Vertical Slice Architecture is not, in the end, Modular Monolith Architecture. I found this question very interesting and exciting enough to think about it and write an article about it. This exploration is not just an academic exercise; it is a reflection of my personal journey and practical experience in the world of software engineering. In this article, I’ll try to untangle this interesting question, drawing on my own insights and the lessons I’ve learned from years of practical work in the field.

Real-world application, trial and error, and the constant process of learning and relearning have shaped the perspectives I share here.

Feature Slices

In my opinion, the essence of the definition of Vertical Slice Architecture, it is the idea of grouping code according to business functionality. The idea can be applied at different levels of abstraction. A slice can be a module, a group of use cases under the same aggregate, and even a use case itself. As I have often described and shown in my articles, I have focused on the use of so-called feature slices, which basically contain everything that is required for the feature.

In my opinion, this best fits Jimmy Bogart’s description of interactions with a system that are either queries or command requests. From the user’s point of view, it’s always either “Give me something” or “Do something”. This is how Jimmy Bogart shows it in his examples, which I used as a guide. In my opinion, two points are essential:

  1. Each vertical slice can decide for itself how best to fulfill the request.
  2. Minimize coupling between slices and maximize coupling within a slice.

So the way I see it is that each module is the root (i.e. Users), which contains smaller slices down to the level of an atomic operation like CreateUser, GetUsers, etc.

This is fairly easy to organize in the code. Basically, this can be done with a folder structure. The top folder could be called “Features”, the next level could represent the modules, e.g. Users, and below that the actual features themselves.

Features/
├── Users/
│   ├── GetUsers/
│   ├── CreateUser/
│   ├── SetUserActive/
│   └── ...
├── Orders/
│   ├── PlaceOrder/
│   ├── CancelOrder/
│   └── ...

My experience has been that this approach provides clarity in code organization and freedom in slice implementation.

But now to the question, is the Vertical Slice architecture the same as a modular monolith? Or what are the differences?

The Monoliths

Let’s first clarify what monolith actually means. Well, it really means nothing more than that the entire application has been packaged into a single executable file, such as a .jar file for a Java application, a .dll file for a .NET core web application, or a single Docker container that encapsulates all parts of the application.

But back to the question: If we look at the structure above and apply the definition of a monolith, we can see that our approach is a monolith. Even though the features in the code exist in isolation from each other, and in the best case have no direct dependencies, they are packaged into an executable during build and deployed. Adding a new module or feature requires rolling out the entire application. When I advocated the use of microservices about 10 years ago, this was of course always an argument against monoliths. Is it really a disadvantage? Absolutely not! Automated deployment, for example using GitHub Actions on an Azure Web App, works fine as a zero downtime deployment. So I see no downside.

Teams can also work on modules independently, as long as they make sure there are no dependencies between modules or feature slices. The question of how slices communicate with each other is actually quite straightforward. Communication between different modules or feature slices in an application can be efficiently managed through the use of events or notifications. This approach is perfectly in line with the idea of keeping modules independent, yet connected.

In the context of using MediatR, this communication is facilitated by publishing notifications. When a specific event occurs within a slice or module, it can publish a notification using MediatR. This notification acts like a broadcast message, signaling to other parts of the application that something has happened.

From this perspective, the Vertical Slice architecture is a modular monolith. I would even say one that works and guarantees maintainability and easy comprehensibility.

Monolithic Modularity

While vertical feature slices are a good choice for building a modular monolith, they’re not the only way to go. We can achieve a similar level of modularity by using a traditional layered architecture, as long as we take care to decouple the modules properly. This approach is quite compatible with the capabilities of .NET.

In a .NET environment, it can be very effective to treat each module as a separate project. This allows us to encapsulate the module-specific code in its own scope. When it comes to the practical aspects, such as building or deploying a Web application, these modules appear as separate DLLs. Not only are they isolated during development, but they retain their independence as dependencies in a final build. This method embeds modularity into the fabric of the application.

Integrating each module with a bounded context, and Onion Architecture takes this a step further. Onion architecture isn’t just about layering; it’s about creating a robust core of business logic surrounded by layers that handle external concerns such as user interfaces and data access. The good thing about this design is its ability to adapt to changes in the external layers without disrupting the core business rules. It’s like building a fortress with a solid foundation and adaptable walls, ensuring that the core of the application remains untouched by the chaos of the outside world.

MyApplication.sln

├── Modules/
│   ├── UsersModule/
│   │   ├── UsersModule.csproj
│   │   ├── Domain/
│   │   │   └── ...
│   │   ├── Application/
│   │   │   ├── Services/
│   │   │   ├── DTOs/
│   │   │   └── ...
│   │   └── Infrastructure/
│   │       ├── Repositories/
│   │       └── ...
│   │
│   ├── OrdersModule/
│   │   ├── OrdersModule.csproj
│   │   ├── Domain/
│   │   │   └── ...
│   │   ├── Application/
│   │   │   ├── Services/
│   │   │   ├── DTOs/
│   │   │   └── ...
│   │   └── Infrastructure/
│   │       ├── Repositories/
│   │       └── ...
│   │
│   └── ...

└── WebApp/
    ├── WebApp.csproj
    ├── Pages/
    ├── Program.cs
    └── ...

Eric Evans’ insights in his book “Domain-Driven Design” are closely related to this approach, which I have personally implemented in numerous projects over the years. The cornerstone of designing effective modules is to achieve minimal coupling while ensuring high internal coherence within each module. This principle is not just theoretical; it is practical wisdom that guides the structuring of robust, maintainable software.

When using the .NET framework, one of its design advantages becomes apparent in this context: the avoidance of circular references between projects or modules. This is not just a technical feature, but a strategic advantage. It results in a design where modules are interrelated, but still independent, thus avoiding the tangled dependencies that can make development and maintenance difficult.

In practice, this means that each module in an application is self-contained, robust, and independent, with only the infrastructure and shared modules as common dependencies. This independence is essential. It allows teams to work on different modules in parallel without stepping on each other’s toes.

Modularizing, avoiding dependencies, and isolating concerns are at the core of this, as we’ve seen. While we’ve touched on vertical feature slices as one way to carve out a modular monolith, it’s not set in stone. An alternative way is to construct the monolith with physically distinct modules, each potentially encompassing a different architectural style.

Flexibility of Feature Slices

Turning the script around, let’s consider the possibility of vertical slicing without committing to a monolithic structure. Think of each slice as an independent lambda function, all sharing a common data model. This approach isn’t just about organization; it’s a strategic choice for scalability.

Imagine we are developing an application using the vertical slice approach. As the project evolves, a particular slice becomes a computing powerhouse and requires more resources. Does that mean we have to scale up the entire application? Not at all. This is the real advantage of this approach. We can take that high-performance slice, turn it into a standalone Azure function or AWS Lambda function, and scale it independently as needed.

The key here is the low cohesion between the slices. Because each slice is a self-contained unit, extracting and scaling a slice doesn’t have to be a big deal. It’s like identifying a part of a machine that needs an upgrade and replacing just that part, rather than rebuilding the entire machine.

This methodology is very flexible. It doesn’t force us to commit to a distributed approach from the start. Instead, it allows us to shift gears and adapt the architecture based on the evolving needs of the application. In this way, we are building an adaptive ecosystem that can grow and change as needed.

Conclusion

In summary, both Vertical Slice Architecture and Modular Monolith Architecture are often implemented as a single deployable unit, but they differ significantly in their approach to structuring the internal components of an application. Vertical Slice Architecture focuses on organizing code based on business functionality, where each feature slice is a self-contained unit that includes everything needed for a specific feature or business requirement. This approach promotes independence and modularity within the application, allowing each slice to be developed, tested, and maintained separately.

Modular monolithic architecture, while also typically a single deployable unit, emphasizes the modular organization of the overall application structure. It involves dividing the application into distinct modules, each with its own domain logic and responsibilities. Although these modules are part of the same deployable unit, they are designed to be as independent as possible, which facilitates maintenance and scalability within the monolith.

The key difference, therefore, is the approach to modularity and organization within the single deployable unit. Vertical Slice Architecture offers a feature-centric, cross-cutting approach to modularity, while Modular Monolith Architecture focuses on a more compartmentalized, module-based approach. Both aim to achieve maintainability and scalability within a monolithic application, but through different structural paradigms.

Cheers!

Similar
Aug 27
Author: Anton Martyniuk
Multitenancy is a software architecture that allows a single instance of a software application to serve multiple customers, called tenants. Each tenant's data is isolated and remains invisible to other tenants for security reasons. This architecture is commonly used in...
Nov 21
Author: Shazni Shiraz
The way we build software keeps evolving. While Clean Architecture has been a favorite for .NET developers, Vertical Slice Architecture is gaining attention with its feature-focused approach. So, let’s take a quick dive into Vertical Slice, compare it with Clean...
one week ago
Author: Artem Polishchuk
Choosing the right architecture style is important for applications that should be scalable, maintainable, and aligned with business requirements. Each architecture style has unique characteristics, and the choice can impact how the application performs, scales, and evolves. This article explores...
Jan 18
Author: Albert Llousas
Are you wondering if there is life beyond the conventional layered, clean or hexagonal code architectures? It turns out there is an old concept known as Functional Core, Imperative Shell (FCIS). Let’s take a closer look and see how it...
Send message
Type
Email
Your name
*Message