VSA architecture
La Vertical Slice Architecture (VSA) est une approche de conception logicielle organisant une application non pas en couches techniques classiques (présentation, logique métier, accès aux données), mais en tranches verticales fonctionnelles. Chaque tranche regroupe tous les éléments nécessaires pour implémenter une fonctionnalité complète, depuis l'interface utilisateur jusqu'à la persistance des données. Cela signifie que les contrôleurs, services, modèles, commandes ou requêtes d'une même fonctionnalité se trouvent ensemble dans une même unité de code. L'objectif principal est de renforcer la cohérence autour des fonctionnalités métier, en évitant le cloisonnement rigide imposé par une architecture en couches. De cette manière, chaque fonctionnalité devient un module indépendant et bien délimité, plus facile à comprendre et à maintenir. La VSA repose donc sur l'idée que la structure du code doit refléter directement les besoins métier de l'application.
L'adoption de la Vertical Slice Architecture apporte plusieurs bénéfices notables par rapport aux approches traditionnelles. En regroupant tous les éléments liés à une fonctionnalité, elle facilite la lisibilité du code, car un développeur peut rapidement trouver et comprendre tout ce qui concerne une partie précise du système. Cette architecture encourage également une meilleure testabilité, puisque chaque tranche peut être isolée et testée indépendamment. Elle réduit aussi le risque de dépendances croisées entre différentes fonctionnalités, limitant ainsi le couplage entre modules. De plus, elle s'intègre particulièrement bien avec des pratiques modernes comme CQRS (Command Query Responsibility Segregation) ou Domain-Driven Design (DDD), mettant en avant la modularité et l'alignement avec le domaine métier. En comparaison avec une architecture en couches, la VSA favorise une évolution plus simple et plus souple des fonctionnalités existantes.
Un autre avantage essentiel de la Vertical Slice Architecture est sa capacité à soutenir des environnements complexes tels que les microservices ou les systèmes distribués. Chaque tranche verticale peut être considérée comme une unité fonctionnelle autonome, ce qui facilite son découpage ultérieur en service indépendant si l'application doit évoluer vers une architecture distribuée. Cette organisation encourage également une approche orientée domaine, où les développeurs réfléchissent aux besoins métiers avant de se concentrer sur les aspects techniques. Elle permet aussi de limiter l'effet boule de neige d'une modification : changer une fonctionnalité a peu de chances d'impacter le reste du système, car le code associé est contenu dans une seule tranche. En résumé, la Vertical Slice Architecture favorise la modularité, la clarté et la robustesse du logiciel, tout en s'adaptant aux tendances modernes du développement applicatif.
- La Vertical Slice Architecture est une approche de conception d'applications où le code est organisé par fonctionnalités complètes (slices), plutôt que par couches techniques traditionnelles (UI, logique métier, accès aux données).
- Chaque slice contient tous les éléments nécessaires pour une fonctionnalité donnée : interface utilisateur, logique métier, modèles, accès aux données,...
- Cela contraste avec l'architecture en couches (Layered Architecture), où tout le code d'un même type (contrôleurs, services, repositories) est regroupé, mais séparé des autres aspects de la fonctionnalité.
- L'idée est de rendre le code plus modulaire, plus testable et plus proche des besoins métier, en réduisant le couplage entre fonctionnalités.
- Cette approche est souvent utilisée avec CQRS, DDD, ou des microservices, car elle facilite l'évolution indépendante des fonctionnalités.
La Vertical Slice Architecture est surtout utilisée dans des environnements modernes favorisant la modularité et l'isolation des fonctionnalités. Voici quelques cas d'usage :
- Azure Functions / AWS Lambda / Google Cloud Functions : Ces services de type Serverless incarnent très bien la philosophie de la Vertical Slice Architecture. Chaque function correspond à une tranche fonctionnelle verticale : elle contient la logique complète d'une fonctionnalité précise (exemple : traitement d'une commande, validation d'un paiement, envoi d'un courriel). On ne sépare pas en couches de services et de contrôleurs classiques; au contraire, tout ce qui est nécessaire à la fonctionnalité se trouve dans un même module.
- Applications ASP.NET Core avec MediatR (CQRS + VSA) : Dans beaucoup de projets ASP.NET Core, on utilise le paquet MediatR pour implémenter CQRS et organiser le code par features. Chaque feature folder correspond à une vertical slice : un dossier Orders contient ses requêtes, commandes, gestionnaires et modèles sans dépendre des autres parties du système. C'est une des implémentations les plus connues de la VSA dans le monde .NET.
- Applications modulaires dans le domaine du e-commerce : Par exemple, dans une application de vente en ligne, on crée un module Products, un module Orders, un module Payments, chacun étant une slice verticale autonome. Chacun de ces modules peut évoluer indépendamment, être testé seul, voire être transformé en microservice par la suite. C'est très fréquent dans des projets construits avec DDD (Domain-Driven Design), car les slices correspondent naturellement aux bounded contexts.
- Microservices et Bounded Contexts en DDD : Chaque microservice peut être vu comme une vertical slice de l'application globale. Par exemple, un service Inventory gère tout ce qui concerne les stocks, sans dépendre du service Billing. La VSA sert donc de modèle interne pour concevoir ces services de façon cohérente et bien encapsulée.
Exemple simplifié
Si vous avez une application de commerce en ligne :
- En architecture en couches → vous aurez besoin d'un dossier Controllers, un dossier Services, un dossier Repositories.
- En Vertical Slice Architecture → vous aurez un dossier Orders, un dossier Payments, un dossier Customers, chacun contenant son contrôleur, ses services, ses modèles,...
Exemple en ASP.NET Core (avec C#)
L'idée clef du VSA est de regrouper tout ce qui concerne une fonctionnalité dans un seul "slice" vertical, plutôt que d'avoir des couches horizontales classiques (Controller → Service → Repository → Entity). Chaque slice contient tout : point de terminaison (endpoint), logique métier, validation, accès aux données.
Voici la structure du projet :
|
/VerticalSliceDemo | +- /Features | +- /Tasks | | +- CreateTask | | | +- CreateTaskCommand.cs | | | +- CreateTaskHandler.cs | | +- ListTasks | | +- ListTasksQuery.cs | | +- ListTasksHandler.cs +- /Infrastructure | +- AppDbContext.cs +- Program.cs +- Models +- TaskItem.cs |
Voici le code source du modèle TaskItem :
Voici le DbContext :
Création d'une tâche (slice vertical).
Voici le CreateTaskCommand.cs :
Voici le CreateTaskHandler.cs :
- using MediatR;
- using VerticalSliceDemo.Infrastructure;
- using VerticalSliceDemo.Models;
-
- namespace VerticalSliceDemo.Features.Tasks.CreateTask;
-
- public class CreateTaskHandler : IRequestHandler<CreateTaskCommand, int>
- {
- private readonly AppDbContext _db;
-
- public CreateTaskHandler(AppDbContext db)
- {
- _db = db;
- }
-
- public async Task<int> Handle(CreateTaskCommand request, CancellationToken cancellationToken)
- {
- var task = new TaskItem { Title = request.Title };
- _db.Tasks.Add(task);
- await _db.SaveChangesAsync(cancellationToken);
- return task.Id;
- }
- }
Liste des tâches (slice vertical).
Voici le ListTasksQuery.cs :
Voici le ListTasksHandler.cs :
- using MediatR;
- using Microsoft.EntityFrameworkCore;
- using VerticalSliceDemo.Infrastructure;
- using VerticalSliceDemo.Models;
-
- namespace VerticalSliceDemo.Features.Tasks.ListTasks;
-
- public class ListTasksHandler : IRequestHandler<ListTasksQuery, List<TaskItem>>
- {
- private readonly AppDbContext _db;
-
- public ListTasksHandler(AppDbContext db)
- {
- _db = db;
- }
-
- public async Task<List<TaskItem>> Handle(ListTasksQuery request, CancellationToken cancellationToken)
- {
- return await _db.Tasks.AsNoTracking().ToListAsync(cancellationToken);
- }
- }
Voici le Program.cs (ASP.NET Core minimal API) :
- using MediatR;
- using Microsoft.EntityFrameworkCore;
- using VerticalSliceDemo.Features.Tasks.CreateTask;
- using VerticalSliceDemo.Features.Tasks.ListTasks;
- using VerticalSliceDemo.Infrastructure;
-
- var builder = WebApplication.CreateBuilder(args);
-
- builder.Services.AddDbContext<AppDbContext>(opt => opt.UseInMemoryDatabase("TasksDb"));
- builder.Services.AddMediatR(typeof(Program));
-
- var app = builder.Build();
-
- app.MapPost("/tasks", async (CreateTaskCommand cmd, IMediator mediator) =>
- {
- var id = await mediator.Send(cmd);
- return Results.Created($"/tasks/{id}", new { id });
- });
-
- app.MapGet("/tasks", async (IMediator mediator) =>
- {
- var tasks = await mediator.Send(new ListTasksQuery());
- return Results.Ok(tasks);
- });
-
- app.Run();