Software Architecture. Vertical Slicing Architectures

← Inicio

  1. Introduction
  2. Implementation considerations
  3. References

Introduction

[!note] The term “Vertical Slice Architecture” was popularized by Jimmy Bogard (creator of AutoMapper) in a 2018 blog post and NDC conference talk.

Capas técnicas (horizontal) vs. slices por funcionalidad (vertical):

flowchart TB
    subgraph H["Layered (horizontal)"]
        direction TB
        UI1["Controllers"] --> SV1["Services"] --> RP1["Repositories"]
    end
    subgraph V["Vertical Slice"]
        direction LR
        F1["Crear Pedido<br/>UI+lógica+datos"]
        F2["Cancelar Pedido<br/>UI+lógica+datos"]
        F3["Listar Pedidos<br/>UI+lógica+datos"]
    end
  • Vertical slicing: A technique to group the code by use cases or business capabilities, instead of technical layers. It allows more freedom and independence for each slice, and reduces coupling and complexity. It can be combined with concentric architectures such as onion or hexagonal.
  • Modular monolith: An alternative to microservices that consists of breaking a large monolithic codebase into smaller modules that are loosely coupled and have clear boundaries. It is easier to change and refactor than microservices, and avoids the challenges of distributed systems.
  • Finding module boundaries: A challenging task that requires balancing local and global complexity, aligning with business domains and teams, and avoiding scope creep and domain model dilution. The author suggests some tactics such as grouping by business capabilities, not concepts, and using domain-driven design principles.
  • Decoupling modules: A necessary step to ensure the modules are cohesive and independent. The author recommends some strategies such as cutting domain entities at the boundary, using dependency inversion, applying event-driven architecture, and using feature toggles.
  • How to decouple domain entities: The author explains that the first step is to unlink the object links between the entities and keep the foreign keys in the database. The second step is to identify the owner of data based on who changes it. The third step is to create an internal API for each module and use data decoupling objects to communicate with other modules.
  • How to deal with cyclic dependencies: The author suggests to question the reasons for having cyclic dependencies and to consider alternatives such as extracting common modules, using events, applying dependency inversion, creating a facade API, or merging the modules back.
  • How to test a modular monolith: The author recommends to use integration tests as the default choice and to test the whole story of each use case. The author also shows a testing pyramid for microservices and compares it with a testing honeycomb for modular monoliths.
  • How to release a modular monolith: The author acknowledges that there is some friction in releasing a modular monolith with multiple teams involved, but also shares an example of a team that managed to release it every week by optimizing their pipeline. The author advises to invest in automation and to explore refactoring opportunities.

Implementation considerations

  • How to decouple domain entities: The author explains that the first step is to replace the object references between the entities with primitive types that represent the foreign keys in the database. For example, instead of having a Customer object in an Order object, you would have a customerId field of type Long. The second step is to assign the responsibility of data based on who modifies it. For example, if the Order module changes the status of an order, then the Order entity should own the status field. The third step is to define an interface for each module and use DTOs (Data Transfer Objects) to communicate with other modules. For example, if the Customer module needs to access some data from the Order module, it should use the OrderService interface and the OrderDTO class.

  • How to deal with cyclic dependencies: The author suggests to reconsider the reasons for having cyclic dependencies and to explore alternatives such as extracting common modules, using events, applying dependency inversion, creating a facade API, or merging the modules back. For example, if the Order module depends on the Customer module and vice versa, you could extract a Payment module that both modules depend on, or use an event bus to publish and subscribe to events between the modules, or invert the dependencies by using abstract interfaces, or create a CustomerOrderService that acts as a facade for both modules, or simply combine the Order and Customer modules into one.

    References

  • Vertical Slice Architecture — Jimmy Bogard, 2018 (original blog post)
  • Modular Monolith — Victor Rentea (YouTube live)

Notas relacionadas


Volver arriba

Matias Miguez — Ingeniería de Software, IA, Tecnología

This site uses Just the Docs, a documentation theme for Jekyll.