Vertical Slice Architecture 是一种按功能组织代码的约定而非框架,每个切片(如 CreateOrder)包含完整功能代码并严格禁止跨切片引用,依赖通过 Shared 项目或显式注册隔离,核心在于团队对边界的纪律性遵守。

Vertical Slice Architecture 在 C# 中不是框架而是组织约定
它不依赖特定库,而是通过项目结构、命名空间和依赖流向强制“按功能切片”,每个切片(如 CreateOrder)包含该功能所需的全部代码:API 入口、DTO、验证、业务逻辑、仓储调用,甚至测试。关键不是“怎么引入”,而是“怎么约束自己不跨切片引用”。
如何组织项目结构避免横向污染
常见错误是把 Controllers、Services、Models 按层平铺,结果一个修改牵动整个解决方案。正确做法是按功能建文件夹,每个切片自成闭环:
- 根目录下直接建
Features/Orders/CreateOrder/,里面放CreateOrderCommand.cs、CreateOrderHandler.cs、CreateOrderEndpoint.cs、CreateOrderValidator.cs - 所有类型都放在
Features.Orders.CreateOrder命名空间下,禁止跨切片引用(比如Features.Customers.GetCustomerQuery不得出现在CreateOrderHandler中) - 共享类型(如
Result<t></t>、AppException)必须放在独立的Shared项目或命名空间中,且只允许被引用,不能反向依赖
MediatR 是最常用但非必需的实现载体
MediatR 能自然支撑请求/响应模型,让每个切片以 IRequest/IRequestHandler 形式封装,但它只是工具——你完全可以用原生 Func<trequest cancellationtoken task>></trequest> 或自定义接口替代。重点在于:每个切片对外暴露单一入口点,内部不暴露实现细节。
示例切片入口:
public record CreateOrderCommand(string CustomerId, List<OrderItemDto> Items) : IRequest<Result<Guid>>;
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, Result<Guid>>
{
private readonly IOrderRepository _repo;
public CreateOrderHandler(IOrderRepository repo) => _repo = repo;
<pre class="brush:php;toolbar:false;">public async Task<Result<Guid>> Handle(CreateOrderCommand req, CancellationToken ct)
{
// 业务逻辑内聚在此,不泄露给其他切片
var order = new Order(req.CustomerId);
foreach (var item in req.Items) order.AddItem(item.ProductId, item.Quantity);
await _repo.AddAsync(order, ct);
return Result.Success(order.Id);
}}
Startup 和依赖注入需配合切片边界
注册时不能全局扫 Assembly.GetExecutingAssembly(),否则会无意中拉入其他切片的 Handler。推荐方式:
- 在每个切片目录里加
CreateOrderRegistration.cs,显式注册该切片所需服务:services.AddScoped<irequesthandler result>>, CreateOrderHandler>();</irequesthandler> - 或者用
MediatR的AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateOrderCommand).Assembly)),但必须确保每个切片编译为独立程序集(即每个Features.Xxx是单独的 .csproj) - API 端点也应按切片注册:
app.MapPost("/orders", CreateOrderEndpoint.Handle);,而不是集中写在Program.cs顶部
真正的难点不在代码怎么写,而在团队是否能守住“一个切片只做一件事、不越界复用、不为‘复用’牺牲边界”的纪律。一旦开始在 CreateOrderHandler 里直接 new GetCustomerQuery,垂直切片就退化成带文件夹的三层架构了。










