Software Architectures examples : Vertical Slice

5 Years ago Jimmy Bogard launches Vertical Slice architecture as an alternative to traditional Clean Architecture. So let’s take a look:

What is Vertical Slicing about?

The new premise is: Minimize coupling between slices, and maximize coupling in a slice., so, we can define this architectural pattern with 3 key points:

  1. Code is split into slices (Features)
  2. Slices are isolated, they should not depend on each other.
  3. Slices are self-contained, so all the code necessary to run the feature is under the slice. (Domain, Infrastructure, etc.)

Example

For this example, I decided to refactor my previous example Asynchronous Command Queue in this series.

What is inside a slice?

One slice or feature contains all the necessary code to solve the business use case. (Request/Response, Command/Query/Event, Command/Query/EventHandler, Domain and Infrastructure).

To make this refactor easier, I first decided to join all namespaces code together inside the API, and then split by entities in my domain: PlayLists and Tracks, and the same with the tests. In order to split the code into slices (1.), I have joined both, queries and commands as features.

What happens if two slices use the same code?

In my example, I had decided to not be strict with the isolation (2.) and self-contained slices (3.). I think it’s more productive to have the shared code written only once. At this point, we still could decide if split the shared code or not by method, like in PlayListNotifier or PlayListQuery, but this depends on the isolation level you want to reach.

We still have good isolation. We can completely delete a single feature folder, and the rest features will not be affected.

What happens if two entities use the same code?

I made the same trade-off here, If multiple entities have features with common code I decided to extract it as a top-level concept shared. In this case, not only infrastructure code is here, but also the domain, cause in this case, PlayList is necessary for Tracks features.

As a self-contained feature, read model features, contain their own specific read models, but notice that this could be moved to shared in order to specify that multiple reading features are sharing the same model.

At this point, we still have isolated entities, cause if we delete Tracks, the PlayList module will not be affected.

When makes sense to use this architecture?

  • This is good for fast development cause there are fewer rules to follow, and the learning curve for new developers is low.
  • Solutions are more simple and projects tend to have fewer classes, in this example, we maybe don’t need many of the interfaces that we have cause we only have one single implementation, and now we are not using interfaces as strict contracts between layers.
  • If you have services that grow too much, maybe this could be an interesting solution, in this refactor we moved from 15 to 2 projects.
  • When you have too much complexity for simple features, like creating unnecessary abstractions or touching several projects to add a feature.
  • Reduce the number of mocks in tests. With this approach, filly integration tests make more sense.

Drawnbacks

  • Dependency anarchy: Boundaries are not defined, so you can & (Should), mix infrastructure and feature-specific code. In this topic, I feel more comfortable separating domain code from the rest of the code in order to have it isolated, because it rarely will change. But this always depends. For example, if we were using an ORM, until what point we will let the database decisions affect our domain?
  • Test trend to be from the feature perspective, so probably they are more long-running compared with the clean architecture approach where we tend to make more isolated testing.
  • If trade-offs are not made, will end in multiple duplications of concepts in order to archive pure feature isolation and self-contained slices. In my opinion shared code wins in the long term. How often had you deleted a feature in the past? 😉