Thank you to our sponsors who keep this newsletter free to the reader:
Stop wasting time and focus on the skills employers are actually looking for today with Julio's .NET Backend Developer Roadmap. A fully interactive guide with 110+ topics that link to the best videos or articles on the web. Get it for free here.
Acorn offers a free cloud sandbox environment where you can deploy applications with the power of Kubernetes. Try it out by launching your own .NET App in the cloud with Acorn. Check out their .NET tutorial and see where your first Acorn takes you!
Layered architectures are the foundation of many software systems. However, layered architectures organize the system around technical layers. And the cohesion between layers is low.
What if you wanted to organize the system around features instead?
Minimize coupling between unrelated features and maximize coupling in a single feature.
Today I want to talk about Vertical Slice Architecture, which does precisely that.
Layered architectures organize the software system into layers or tiers. Each of the layers is typically one project in your solution. Some of the popular implementations are N-tier architecture or Clean architecture.
Layered architectures focus on separating the concerns of the various components. This makes it easier to understand and maintain the software system. And there are many benefits of structured software design, such as maintainability, flexibility, and loose coupling.
However, layered architectures also impose constraints or rigid rules on your system. The direction of dependencies between layers is pre-determined.
For example, in Clean Architecture:
- Domain should have no dependencies
- Application layer can reference the Domain
- Infrastructure can reference both Application and Domain
- Presentation can reference both Application and Domain
You end up having high coupling inside a layer and low coupling between layers. This doesn't mean layered architectures are bad. But it does mean you will have many abstractions between individual layers. And more abstractions mean increased complexity because there are more components to maintain.
Vertical Slice Architecture was born from the pain of working with layered architectures. They force you to make changes in many different layers to implement a feature.
Let's imagine what adding a new feature looks like in a layered architecture:
- Updating the domain model
- Modifying validation logic
- Creating a use case with MediatR
- Exposing an API endpoint from a controller
The cohesion is low because you are creating many files in different layers.
Vertical slices take a different approach:
Minimize coupling between slices, and maximize coupling in a slice.
Here's how you can visualize vertical slices:
All the files for a single use case are grouped inside one folder. So, the cohesion for a single use case is very high. This simplifies the development experience. It's easy to find all the relevant components for each feature since they are close together.
If you're building an API, the system already breaks down into commands (POST/PUT/DELETE) and queries (GET). By splitting the requests into commands and queries, you're getting the benefits of the CQRS pattern.
Vertical slices narrowly focus on a single feature. This allows you to treat each use case separately and tailor the implementation to the specific requirements. One vertical slice can use EF Core to implement a GET request. Another vertical slice can use Dapper with raw SQL queries.
Another benefit of implementing vertical slices like this is:
New features only add code, you're not changing shared code and worrying about side effects.
However, vertical slices have their own set of challenges. Because you are implementing much of the business logic inside a single use case, you need to be able to spot code smells. As the use case grows, it can end up doing too much. You will have to refactor the code by pushing logic to the domain.
Layered architectures, such as Clean architecture, organize the solution across layers. This results in a folder structure grouped by technical concerns.
Vertical slice architecture, on the other hand, organizes the code around features or use cases.
An interesting approach to structuring APIs around features is using the REPR pattern. It stands for Request-EndPoint-Response. This aligns perfectly with the idea of vertical slices. You can achieve this with the MediatR library, for example.
The REPR pattern defines that web API endpoints should have three components:
Here's an example solution structure in .NET.
You'll notice the
Features folder, which contains the vertical slices.
Each vertical slice implements one API request (or use case).
🔗 RunTracker.API |__ 📁 Database |__ 📁 Entities |__ #️⃣ Activity.cs |__ #️⃣ Workout.cs |__ #️⃣ ... |__ 📁 Features |__ 📁 Activities |__ 📁 GetActivity |__ #️⃣ ActivityResponse.cs |__ #️⃣ GetActivityEndpoint.cs |__ #️⃣ GetActivityQuery.cs |__ #️⃣ GetActivityQueryHandler.cs |__ 📁 CreateActivity |__ #️⃣ CreateActivity.cs |__ #️⃣ CreateActivity.Command.cs |__ #️⃣ CreateActivity.Endpoint.cs |__ #️⃣ CreateActivity.Handler.cs |__ #️⃣ CreateActivity.Validator.cs |__ 📁 Workouts |__ 📁 ... |__ 📁 Middleware |__ 📄 appsettings.json |__ 📄 appsettings.Development.json |__ #️⃣ Program.cs
A few more libraries for implementing the REPR pattern:
Some of you may not like the idea of grouping all the files related to a feature in a single folder.
However, there's a lot of value in grouping by features in general. You don't have to implement vertical slices. But you can apply this concept to your domain by grouping files around aggregates, for example. This is the approach I show in Pragmatic Clean Architecture.
Thanks for reading, and stay awesome!