系统设计的核心挑战在于如何管理复杂的业务逻辑与外部技术细节之间的关系。在传统的三层架构中,业务逻辑往往与数据访问技术紧密耦合,导致系统难以测试和扩展。C#实现Clean Architecture(整洁架构)通过严格的分层和依赖反转原则,将核心业务逻辑隔离在独立的领域层中,使其不依赖于任何外部框架或数据库。这种设计不仅提升了代码的可维护性,还使得业务规则能够独立于UI、数据库或第三方服务进行演进和测试。

理解Clean Architecture的核心层与依赖规则
整洁架构的核心思想是将系统划分为多个同心圆层级,从外到内依次是基础设施层、接口适配器层、用例层和实体层。最关键的设计原则是依赖反转原则(DIP),它要求源代码层面的依赖关系必须只能指向内部。也就是说,内层的代码绝对不能知道外层代码的任何细节,尤其是不能引用外层的框架或数据模型。
实体层处于最内圈,包含了企业级的业务规则和实体对象。这一层是完全独立的,不依赖于任何外部技术。用例层包含了应用的业务规则,负责协调实体的流转。接口适配器层则负责将外部的数据格式转换为内层需要的格式,例如控制器、视图模型和数据库映射器。最外层的基础设施层包含了框架、驱动程序、数据库等具体技术实现细节。
这种依赖规则的直接好处是,当你更换数据库(比如从SQL Server换成PostgreSQL)或者更换UI框架时,内层的核心业务逻辑代码不需要做任何修改。外层的实现只需要遵循内层定义的接口契约即可,这极大地提高了系统的可测试性和可扩展性。
C#整洁架构项目结构搭建与分层实现
在C#中落地整洁架构,通常会在Visual Studio解决方案中创建多个类库项目。典型的项目结构包括四个核心项目:Domain(实体层)、Application(用例层)、Infrastructure(基础设施层)和Presentation(表示层)。Domain项目不依赖任何其他项目;Application项目依赖Domain项目;Infrastructure项目依赖Application和Domain项目;Presentation项目依赖Application项目。通过这种项目引用关系,在编译层面就强制保证了依赖方向只能向内。
Domain层主要负责定义实体和仓储接口。实体应该是纯粹的C#对象(POCO),不带有任何EF Core的特性。下面是一个简单的实体和接口定义示例:
namespace CleanArch.Domain.Entities
{
public class Order
{
public Guid Id { get; private set; }
public string CustomerName { get; private set; }
public decimal TotalAmount { get; private set; }
public DateTime CreatedAt { get; private set; }
public Order(string customerName, decimal totalAmount)
{
Id = Guid.NewGuid();
CustomerName = customerName;
TotalAmount = totalAmount;
CreatedAt = DateTime.UtcNow;
}
}
}
namespace CleanArch.Domain.Interfaces
{
public interface IOrderRepository
{
Task<Order> GetByIdAsync(Guid id);
Task AddAsync(Order order);
}
}Application层负责实现具体的业务用例,通常结合CQRS模式使用MediatR库来处理请求。这一层定义了命令和查询,并实现对应的处理器。它依赖Domain层来调用仓储接口完成业务操作,但并不关心仓储的具体实现方式。下面是一个创建订单命令的处理器示例:
using MediatR;
using CleanArch.Domain.Entities;
using CleanArch.Domain.Interfaces;
namespace CleanArch.Application.Features.Orders.Commands
{
public record CreateOrderCommand(string CustomerName, decimal TotalAmount) : IRequest<Guid>;
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, Guid>
{
private readonly IOrderRepository _orderRepository;
public CreateOrderCommandHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var order = new Order(request.CustomerName, request.TotalAmount);
await _orderRepository.AddAsync(order);
return order.Id;
}
}
}通过在Application层定义接口并在处理器中调用,我们将业务逻辑与数据持久化彻底解耦。Application层只关心业务流程,而数据的保存细节交由外层去实现。这种设计使得我们可以轻松地对业务逻辑进行单元测试,只需Mock掉IOrderRepository即可。
基础设施层与表示层的依赖反转落地
Infrastructure层是具体技术实现的大本营。在这里,我们需要实现Application或Domain层定义的接口。例如,我们使用Entity Framework Core来实现IOrderRepository接口。这一层会包含DbContext、数据库实体映射配置以及具体的CRUD操作代码。由于依赖反转,Infrastructure层必须引用Application和Domain项目,但Application层对Infrastructure层一无所知。
下面是Infrastructure层中EF Core的DbContext和仓储实现示例。注意看代码中如何将领域实体与数据库上下文进行关联,同时实现内层定义的接口:
using Microsoft.EntityFrameworkCore;
using CleanArch.Domain.Entities;
using CleanArch.Domain.Interfaces;
namespace CleanArch.Infrastructure.Persistence
{
public class AppDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasKey(o => o.Id);
modelBuilder.Entity<Order>().Property(o => o.CustomerName).IsRequired();
}
}
public class OrderRepository : IOrderRepository
{
private readonly AppDbContext _context;
public OrderRepository(AppDbContext context)
{
_context = context;
}
public async Task<Order> GetByIdAsync(Guid id)
{
return await _context.Orders.FindAsync(id);
}
public async Task AddAsync(Order order)
{
await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
}
}
}Presentation层通常是ASP.NET Core Web API或MVC项目,它是系统的外部入口。这一层负责接收HTTP请求,将请求参数转换为Application层的命令或查询,并通过MediatR发送出去。最关键的依赖注入配置也发生在这里。在Program.cs文件中,我们需要将Infrastructure层的实现注册到内层的接口上,从而在运行时完成依赖反转的闭环。
下面是表示层中控制器的实现以及依赖注入的配置示例。控制器只依赖Application层的接口,完全不知道数据库的存在:
using MediatR;
using Microsoft.AspNetCore.Mvc;
using CleanArch.Application.Features.Orders.Commands;
namespace CleanArch.Presentation.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IMediator _mediator;
public OrdersController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderCommand command)
{
var orderId = await _mediator.Send(command);
return CreatedAtAction(nameof(CreateOrder), new { id = orderId }, orderId);
}
}
}
// Program.cs 中的依赖注入配置
// builder.Services.AddDbContext<AppDbContext>(options =>
// options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// builder.Services.AddScoped<IOrderRepository, OrderRepository>();
// builder.Services.AddMediatR(typeof(CreateOrderCommand).Assembly);通过这种结构,整个系统形成了一个清晰的依赖链条。外部请求从Presentation层进入,通过MediatR路由到Application层的处理器,处理器调用Domain层的实体和接口,最终在运行时由Infrastructure层的实现完成数据库操作。这种架构模式虽然增加了项目的初始复杂度和文件数量,但对于中大型长期维护的项目来说,它带来的可测试性、可扩展性和代码组织清晰度是无可比拟的。
C# Clean Architecture整洁架构项目结构修改时间:2026-08-26 14:38:06