如何避免陷入意大利面条式代码:C# 中的六边形架构

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

当项目变成一团乱麻时

你是否曾经打开过一个一年前编写的 C# 项目,然后惊恐地发现 任何修改都冒着破坏一切的风险?业务逻辑与 Entity Framework 紧密耦合,ASP.NET 控制器保存着数据库的秘密,而单元测试变成了与真实数据库的地狱式集成。熟悉吗?

这是缺乏架构的典型症状。好消息是:有一个经过验证的解决方案——六边形架构(也称为 端口与适配器)。这种由 Alistair Cockburn 提出的模式不仅仅是一个时髦的术语。对于 C# 的中大型项目来说,它是一种救星。让我们来了解它是如何工作的,并立即编写代码。



六边形架构的本质:核心与外围

想象一个六边形。中心是您的应用程序,包含用纯 C# 编写的干净业务逻辑,没有外部依赖。边缘是端口(接口),核心通过它们与外部世界通信。适配器是具体的实现:ASP.NET 控制器、EF Core 仓库、RabbitMQ 客户端。

主要规则:核心对外部世界一无所知。它只知道自己的端口。这使得无需更改业务逻辑即可更换数据库、用户界面或消息队列。



这对 C# 开发者有什么好处?

  • 可测试性: 无需数据库、HTTP 和文件系统即可测试业务逻辑。
  • 灵活性: 想将 PostgreSQL 替换为 MongoDB?只需更改适配器。
  • 可读性: 代码变得像需求规范,而不是依赖关系的纠缠。
  • 遵循 SOLID: 特别是依赖反转原则。


C# 项目结构

在 六边形架构 中,通常有三个主要层。对于 C#,可以方便地将它们组织为同一个解决方案中的独立项目:



MyApp.sln├── MyApp.Domain          // 核心:实体、值对象、端口(接口)├── MyApp.Application     // 用例(应用服务)├── MyApp.Infrastructure  // 适配器:EF Core、外部 API、日志记录└── MyApp.Api             // 输入适配器:ASP.NET 控制器 / Minimal API


请注意:Domain 和 Application 是纯类库,没有对 Microsoft.AspNetCore 或 EntityFramework 的引用。只有 System 和 System.Threading.Tasks。



编写代码:真实示例

假设我们正在构建一个订单系统。需要创建订单并保存它。



步骤 1. 核心(Domain)

这里包含实体和 端口——定义核心如何与世界通信的接口。



// MyApp.Domain/Entities/Order.cspublic 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; }}

// MyApp.Domain/Ports/IOrderRepository.cs (输出端口)public interface IOrderRepository{ Task<Order> GetByIdAsync(Guid id); Task AddAsync(Order order);}

// MyApp.Domain/Ports/IOrderNotifier.cs (输出端口——通知)public interface IOrderNotifier{ Task NotifyOrderCreatedAsync(Order order);}


请注意:IOrderRepository 和 IOrderNotifier 是端口。核心定义了它 想要什么,但没有定义 如何 实现。



步骤 2. 用例

博客

书籍推荐