如何在C#中实现垂直切片架构?求架构解析与工具建议
垂直切片架构在C#中的实现指南
什么是垂直切片架构
垂直切片架构是一种以业务功能为核心的代码组织方式,和传统的分层架构(控制器层、服务层、数据访问层)完全不同。它把实现单一业务场景所需的所有代码打包成一个独立"切片"——比如"创建订单"这个功能,切片里会包含API端点、业务规则校验、数据库操作、请求/响应模型等所有相关代码,每个切片独立聚焦,互不干扰。
C#中实现垂直切片架构的步骤
- 按业务场景划分切片:先梳理项目的核心业务操作,比如电商场景的"创建订单"、"查询订单详情"、"取消订单",每个操作对应一个独立切片。
- 每个切片内包含完整依赖:以"创建订单"为例,切片目录下要放全相关代码:
- API端点(用Minimal API或专用控制器)
- 业务逻辑处理类(比如订单创建的规则校验、库存检查)
- 数据访问代码(直接用EF Core写查询/更新,不用复杂的通用仓储)
- 专属模型(请求DTO、响应DTO、实体类)
- 严格控制跨切片依赖:切片内部的代码只服务自身业务,不要引用其他切片的内部逻辑。如果有多个切片都要用的公共代码(比如通用枚举、基础实体、全局验证规则),抽成独立的共享项目。
- 用中介模式解耦请求与处理:可以借助MediatR,让API端点只负责接收请求,具体逻辑交给对应的Handler处理,代码更整洁,也方便后续扩展。
简化实现的库/框架
- MediatR:专门处理命令/查询的中介库,能轻松分离请求定义和业务逻辑,是垂直切片架构的常用工具。
- Entity Framework Core:直接在切片的Handler里写数据操作,省去冗余的仓储层,简化数据访问流程。
- FluentValidation:在切片内对请求模型做针对性校验,验证规则和业务逻辑放在一起,维护更方便。
- ASP.NET Core Minimal APIs:相比传统控制器,更适合垂直切片,每个切片可以单独定义轻量的API端点,代码更紧凑。
入门技巧与建议
- 从小功能入手:先挑一个简单的业务场景(比如"创建用户")做第一个切片,熟悉结构后再扩展其他功能,避免一开始就陷入复杂逻辑。
- 拒绝过度抽象:垂直切片强调务实,不要一开始就建通用仓储、基类等抽象层,只有当多个切片都需要复用某段逻辑时,再考虑抽象。
- 控制切片粒度:一个切片对应一个单一业务操作,比如"创建订单"和"支付订单"要分成两个独立切片,不要强行合并。
- 利用ASP.NET Core原生特性:用依赖注入注册每个切片的Handler、Validator;用中间件处理跨切片的通用逻辑(比如日志、异常捕获)。
- 参考代码结构示例:
YourProject/ ├── Shared/ # 公共代码,所有切片可引用 │ ├── Enums/ │ ├── BaseEntities/ │ └── CommonValidators/ ├── Features/ # 所有垂直切片的存放目录 │ ├── CreateOrder/ │ │ ├── CreateOrderCommand.cs │ │ ├── CreateOrderHandler.cs │ │ ├── CreateOrderRequest.cs │ │ ├── CreateOrderResponse.cs │ │ └── CreateOrderValidator.cs │ ├── GetOrderDetails/ │ │ ├── GetOrderQuery.cs │ │ ├── GetOrderHandler.cs │ │ └── GetOrderResponse.cs │ └── CancelOrder/ │ ├── CancelOrderCommand.cs │ └── CancelOrderHandler.cs └── YourProject.csproj - 核心代码片段示例:
命令定义与处理逻辑:
Minimal API注册:// CreateOrderCommand.cs public record CreateOrderCommand(Guid CustomerId, List<OrderItemDto> Items) : IRequest<CreateOrderResponse>; // CreateOrderHandler.cs public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, CreateOrderResponse> { private readonly AppDbContext _dbContext; private readonly IValidator<CreateOrderCommand> _validator; public CreateOrderHandler(AppDbContext dbContext, IValidator<CreateOrderCommand> validator) { _dbContext = dbContext; _validator = validator; } public async Task<CreateOrderResponse> Handle(CreateOrderCommand request, CancellationToken cancellationToken) { // 校验请求参数 var validationResult = await _validator.ValidateAsync(request, cancellationToken); if (!validationResult.IsValid) throw new ValidationException(validationResult.Errors); // 检查库存(简化示例) foreach (var item in request.Items) { var product = await _dbContext.Products.FindAsync(item.ProductId, cancellationToken); if (product == null || product.Stock < item.Quantity) throw new InvalidOperationException($"产品{item.ProductId}库存不足"); } // 创建订单实体并保存 var order = new Order { Id = Guid.NewGuid(), CustomerId = request.CustomerId, OrderDate = DateTime.UtcNow, Items = request.Items.Select(i => new OrderItem { ProductId = i.ProductId, Quantity = i.Quantity, UnitPrice = i.UnitPrice }).ToList() }; _dbContext.Orders.Add(order); await _dbContext.SaveChangesAsync(cancellationToken); return new CreateOrderResponse(order.Id); } }// Program.cs app.MapPost("/api/orders", async (CreateOrderCommand command, IMediator mediator) => { var response = await mediator.Send(command); return Results.Created($"/api/orders/{response.OrderId}", response); });
内容的提问来源于stack exchange,提问作者Ahmed Zlazel
相关产品推荐
相关产品推荐

