垂直切片架构详细指南(含 C# 12 代码示例)

发布:2024-10-20 10:07 阅读:34 点赞:0

一. 概述

近年来,垂直切片架构在开发者中越来越受欢迎,因其促进了可维护性、可扩展性和可测试性的应用程序构建。该架构通过功能而非传统层次(如展示、业务逻辑和数据访问)组织代码。每个切片包含其所需的所有代码。

二. 垂直切片架构的定义

垂直切片架构将应用程序划分为独立的组件(切片),每个切片都包含从用户界面到数据库的完整堆栈。这种方法与传统分层架构相对,增强了特性之间的独立性。

主要优势

  1. 高内聚性:特性自包含,易于管理和理解。
  2. 低耦合性:减少特性之间的依赖,增强灵活性和可维护性。
  3. 集中测试:独立切片允许更专注和隔离的测试。
  4. 易于协作:不同团队可以独立开发各自的切片,降低冲突风险。

三. 在 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

这种结构确保每个切片包含所需的所有命令、处理程序和响应。

四. 命令

在垂直切片架构中,我们使用命令封装操作请求。示例包括 AddProductCommandPlaceOrderCommand

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<AddProductCommandAddProductResponse>
{
    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<PlaceOrderCommandPlaceOrderResponse>
{
    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 中提供了一个现代、灵活和易于管理的应用程序设计模式。通过将特性与其相关代码集中到切片中,开发者可以提升应用程序的可维护性和可扩展性,最终实现更高的开发效率。