垂直切片架构详细指南(含 C# 12 代码示例)
阅读:34
点赞:0
一. 概述
近年来,垂直切片架构在开发者中越来越受欢迎,因其促进了可维护性、可扩展性和可测试性的应用程序构建。该架构通过功能而非传统层次(如展示、业务逻辑和数据访问)组织代码。每个切片包含其所需的所有代码。
二. 垂直切片架构的定义
垂直切片架构将应用程序划分为独立的组件(切片),每个切片都包含从用户界面到数据库的完整堆栈。这种方法与传统分层架构相对,增强了特性之间的独立性。
主要优势
-
高内聚性:特性自包含,易于管理和理解。 -
低耦合性:减少特性之间的依赖,增强灵活性和可维护性。 -
集中测试:独立切片允许更专注和隔离的测试。 -
易于协作:不同团队可以独立开发各自的切片,降低冲突风险。
三. 在 C# 12 中实现垂直切片架构
以 C# 12 为基础,我们将创建一个简单的电子商务应用,展示产品管理功能。
项目结构
ECommerceApp
│
├── src
│ ├── Common
│ │ ├── NotFoundException.cs
│ ├── Controllers
│ │ ├── OrdersController.cs
│ │ ├── ProductsController.cs
│ ├── Infrastructure
│ │ ├── AppDbContext.cs
│ ├── Products
│ │ ├── AddProduct
│ │ │ ├── AddProductCommand.cs
│ │ │ ├── AddProductHandler.cs
│ │ │ ├── AddProductResponse.cs
│ │ ├── GetProduct
│ │ │ ├── GetProductQuery.cs
│ │ │ ├── GetProductHandler.cs
│ │ │ ├── GetProductResponse.cs
│ ├── Orders
│ │ ├── GetOrder
│ │ │ ├── GetOrderCommand.cs
│ │ │ ├── GetOrderHandler.cs
│ │ │ ├── GetOrderResponse.cs
│ │ ├── PlaceOrder
│ │ │ ├── PlaceOrderCommand.cs
│ │ │ ├── PlaceOrderHandler.cs
│ │ │ ├── PlaceOrderResponse.cs
│ ├── ECommerceApp.csproj
└── Program.cs
这种结构确保每个切片包含所需的所有命令、处理程序和响应。
四. 命令
在垂直切片架构中,我们使用命令封装操作请求。示例包括 AddProductCommand
和 PlaceOrderCommand
:
AddProductCommand.cs
using MediatR;
namespace ECommerceApp.Products.AddProduct;
public record AddProductCommand(string Name, decimal Price, string Description) : IRequest;
PlaceOrderCommand.cs
using MediatR;
namespace ECommerceApp.Orders.PlaceOrder;
public record PlaceOrderCommand(string CustomerName, decimal TotalAmount) : IRequest;
五. 处理程序
接下来,需要处理程序来处理命令,其中包含添加产品和订单的逻辑。
AddProductHandler.cs
using ECommerceApp.Products.Common;
using MediatR;
namespace ECommerceApp.Products.AddProduct;
public class AddProductHandler : IRequestHandler<AddProductCommand, AddProductResponse>
{
private readonly IProductRepository _productRepository;
public AddProductHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task Handle(AddProductCommand request, CancellationToken cancellationToken)
{
var product = new Product
{
Name = request.Name,
Price = request.Price,
Description = request.Description
};
await _productRepository.AddProductAsync(product);
return new AddProductResponse(product.Id, product.Name);
}
}
PlaceOrderHandler.cs
using ECommerceApp.Orders.Common;
using MediatR;
namespace ECommerceApp.Orders.PlaceOrder;
public class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand, PlaceOrderResponse>
{
private readonly IOrderRepository _orderRepository;
public PlaceOrderHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task Handle(PlaceOrderCommand request, CancellationToken cancellationToken)
{
var order = new Order
{
Id = Guid.NewGuid(),
CustomerName = request.CustomerName,
TotalAmount = request.TotalAmount,
OrderDate = DateTime.UtcNow
};
await _orderRepository.AddOrderAsync(order);
return new PlaceOrderResponse(order.Id, order.CustomerName);
}
}
六. 响应
响应对象用于传递命令执行的结果。
AddProductResponse.cs
namespace ECommerceApp.Products.AddProduct;
public record AddProductResponse(Guid ProductId, string ProductName);
PlaceOrderResponse.cs
namespace ECommerceApp.Orders.PlaceOrder;
public record PlaceOrderResponse(Guid OrderId, string CustomerName);
七. 仓储
仓储模式通常用于与数据存储进行交互。
IProductRepository.cs
namespace ECommerceApp.Products.Common;
public interface IProductRepository
{
Task AddProductAsync(Product product);
Task GetProductByIdAsync(Guid id);
}
ProductRepository.cs
using ECommerceApp.Infrastructure;
namespace ECommerceApp.Products.Common;
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public async Task AddProductAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
}
public async Task GetProductByIdAsync(Guid id) => await _context.Products.FindAsync(id);
}
八. API 控制器
API 控制器是应用程序的入口点,每个特性都封装所有所需的逻辑。
ProductsController.cs
using ECommerceApp.Products.AddProduct;
using ECommerceApp.Products.GetProduct;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace ECommerceApp.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id:guid}")]
public async Task GetProductById(Guid id)
{
var query = new GetProductQuery(id);
var product = await _mediator.Send(query);
if (product == null)
{
return NotFound($"Product with ID {id} not found.");
}
return Ok(product);
}
[HttpPost]
public async Task AddProduct([FromBody] AddProductCommand command)
{
if (command == null)
{
return BadRequest("Invalid product details.");
}
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetProductById), new { id = result.ProductId }, result);
}
}
OrdersController.cs
using ECommerceApp.Orders.GetOrder;
using ECommerceApp.Orders.PlaceOrder;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace ECommerceApp.Controllers;
[Route("api/[controller]")]
[ApiController]
public class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
public OrdersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task PlaceOrder([FromBody] PlaceOrderCommand command)
{
if (command == null)
{
return BadRequest("Invalid order details.");
}
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetOrderById), new { id = result.OrderId }, result);
}
[HttpGet("{id:guid}")]
public async Task GetOrderById(Guid id)
{
var query = new GetOrderQuery(id);
var order = await _mediator.Send(query);
if (order == null)
{
return NotFound($"Order with ID {id} not found.");
}
return Ok(order);
}
}
九. 整合
可以在 Program.cs
中配置应用程序以使用切片。
Program.cs
using ECommerceApp.Infrastructure;
using ECommerceApp.Orders.Common;
using ECommerceApp.Products.Common;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext(options =>
options.UseSqlServer
(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddMediatR(typeof(Program).Assembly);
var app = builder.Build();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
十. 结论
垂直切片架构在 C# 12 中提供了一个现代、灵活和易于管理的应用程序设计模式。通过将特性与其相关代码集中到切片中,开发者可以提升应用程序的可维护性和可扩展性,最终实现更高的开发效率。