Simplifying-development-with-vertical-slices

Simplifying development with vertical slices

Symphony Logo
Symphony
May 16th, 2024

In the fast-changing world of software development, the quest for more efficient and manageable architectural patterns never stops. The introduction of Vertical Slice Architecture presents a compelling alternative to traditional architectural patterns such as Onion, Clean, and Hexagonal architectures. 

Inspired by a presentation that shows the benefits of Vertical Slice Architecture among others in a .NET system using the MediatR library, this article explores the implementation of this architectural pattern in a Java-based system utilizing Spring Boot and Axon Framework. 

The sample application, named RivalQuest, demonstrates the implementation with three vertical slices: Game, User, and Ranking, employing Domain Driven Design (DDD) principles along with Command Query Responsibility Segregation (CQRS).

What is Vertical Slice Architecture?

Vertical Slice Architecture (VSA) focuses on dividing the application into vertical slices rather than traditional layers. Each slice represents a specific feature or functionality, encompassing all necessary components from the UI to the database. This approach facilitates independent development, testing, and deployment of features, significantly enhancing agility and scalability in development processes.

66475e0ecc691e5779e92130

RivalQuest: Sample application

RivalQuest, available on GitHub, is a showcase application implementing VSA in a Java environment. By using Spring Boot as the base framework and the Axon Framework for DDD and CQRS patterns, RivalQuest demonstrates a new approach to building scalable and maintainable applications.

The slices: Game, Player, Ranking

Each vertical slice in RivalQuest - Game, User, and Ranking represents a distinct domain area, encapsulating its logic, data model, and interactions.

This isolation allows for more focused development and easier maintenance. Additionally, by using functional-style endpoint definitions, RivalQuest offers a clean and intuitive API surface for each slice, further emphasizing the autonomy and cohesiveness of the slices.

66475c42cc691e5779e91f73

66475822cc691e5779e91bf8

You can see here how the RegisterUser feature is organized by defining all specific classes (or records) like Request, Command, and Response inline, and using them inside the endpoint specific to the RegisterUser feature, which is defined using functional style. In this case, the Command handler is also specific only to this feature so it is defined internally as well.

The implementation follows the REPR design pattern, making it easy to follow execution by starting from the request to the response. By consolidating the request, endpoint, and response within a slice, you minimize the time spent navigating through layers, thereby enhancing the maintainability and ease of working with endpoints.

66475865cc691e5779e91c16

In a similar way to the RegisterUser feature, GetUser is defined by specifying Query and QueryHandler inside a specific feature file.

664758a4cc691e5779e91c5f

In contrast to the RegisterUser feature, here we can see that CommandHandler is not defined inside the feature; instead, it’s defined within a specific GameAggregate because it’s needed to process the Command.

Each slice or even feature can be implemented using different patterns. In the RivalQuest example, inside the Game slice, there is GameAggregate implemented using DDD and Event Sourcing, while within the User slice, a simpler implementation is employed using UserService. The Ranking slice is implemented similarly to User but it’s specific and more coupled because it listens for events from the Game slice and utilizes the User slice to obtain additional information about the User. 

Sharing code between features

It doesn’t mean that each feature is independent. There is still communication between features through Commands and Queries, where shared Contract Models need to be defined and shared between features (or copied). It’s fine to share code even more broadly across the entire application, and that part of the code is referred to as the Shared Kernel.

Abstractions still exist

One of the most common misconceptions is that using vertical slices will eliminate abstractions. That’s not true; abstractions should still be used, but they are not mandatory and are used in a slightly different way. We should carefully consider when creating abstractions if we need them at all. Most abstractions in traditional layered architectures, which are based on technical concepts, are useless.

Use case-driven development

VSA features are more focused on use cases that are agile-compatible, organizing code around them, rather than using CRUD, which is not focused on use cases. This way makes the code more expressive about what it actually does.

Benefits over traditional architectures

VSA, as illustrated through RivalQuest, offers several advantages over more traditional approaches:

  • Organized by business feature, not by technical layers

  • Coupling within slice: while coupling cannot be entirely avoided, using vertical slices and their functional cohesion within logical boundaries can be limited to a specific slice

  • Enhanced Focus: developers can concentrate on one slice at a time, reducing cognitive load and increasing efficiency

  • Improved scalability: each slice can scale independently, offering granular control over resource allocation. It’s microservice-friendly, allowing for an efficient extraction of specific set of features into separate microservices 

  • Increased flexibility: changes to one slice have minimal impact on others, facilitating easier updates and modifications. This minimizes the number of merge conflicts, which are possible only on a limited amount of code

  • Simplified deployment: independent slices can be deployed separately, enabling more agile delivery cycles

  • Simplified testing: testing becomes more straightforward by focusing on individual slices, ensuring each feature functions as intended

  • Naturally plays well with popular concepts: VSA integrates seamlessly with popular concepts like Microservices Architecture, Modular Monolith, DDD, CQRS, and Event Sourcing, simplifying its implementation 

Potential considerations

Adopting the VSA approach in software development offers numerous benefits, however, like any architectural pattern, it also presents its own set of considerations and challenges. Here are some potential considerations of using the VSA:

  • Learning curve and adoption: teams unfamiliar with VSA may encounter a learning curve. Understanding how to effectively design and implement vertical slices requires a solid grasp of DDD, CQRS, and potentially other patterns such as Event Sourcing. Training and ramp-up time should be considered, especially for teams transitioning from more traditional architectures

  • Shared code: repeated code across multiple slices may indicate a lack of shared components or libraries. To avoid this, we should identify common functionalities and refactor them into shared components to prevent duplication and encourage reusability

Conclusion

RivalQuest exemplifies the principles of VSA through its modular design and implementation with Spring Boot and Axon Framework. Breaking away from traditional layered architectures, RivalQuest demonstrates how applications can become more agile, scalable, and easier to manage. This approach, illustrated with a practical example, highlights the potential for VSA to simplify and enhance the development process across different technology stacks.

About the author

Nirmel Murtic is a Principal Software Engineer at Symphony's Sarajevo branch, where he has been working since 2016. With 16 years of experience in software engineering, he specializes in the Java (Spring) stack for building web services. He is also proficient in .NET Core, Python, and cloud technologies such as Azure and AWS. Nirmel combines his technical expertise with strong leadership skills, effectively guiding teams to achieve project goals. Known for his quick adaptability and excellent communication, he plays a crucial role in driving development projects forward at Symphony.

Contact us if you have any questions about our company or products.

We will try to provide an answer within a few days.