C# 架构精通 — 重构遗留 ASP.NET Core 应用以实现清洁架构(第10部分)
Source: Dev.to
Introduction
大多数 ASP.NET Core 系统并不是一开始就坏掉的——它们是逐渐变坏的。
遗留系统很少是糟糕开发者的产物;它们往往是优秀开发者在压力下的结果。
在第 10 部分,我们将介绍高级团队如何通过增量方式、在不进行重写、一次性大改或功能冻结的情况下,将遗留的 ASP.NET Core 应用重构为 Clean Architecture。这是一种受控的演进,而不是拆除。
永远不要重写可以重塑的东西。
重写会失败,因为业务逻辑理解不深、边缘情况没有文档记录,且交付会中断。
Clean Architecture 重构的目标是创建“接缝”,而不是追求完美。
Identifying Problem Areas
在动代码之前,先寻找以下现象:
- 臃肿的控制器
- 上帝服务(God services)
DbContext泄漏- 不可测试的逻辑
- 对变更的恐惧
这些是起点的指示,而不是需要整体重新设计的目标。
Protect Behavior with Characterization Tests
// Characterization test
[Fact]
public async Task CreateOrder_Current_Behavior_Is_Preserved()
{
var response = await client.PostAsJsonAsync("/orders", request);
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
这些测试:
- 捕获当前行为
- 防止意外回归
- 为重构提供安全保障
它们不需要写得很漂亮。
Refactoring Steps
Extract Use Cases
遗留的控制器往往同时包含业务规则、数据访问和映射逻辑。
通过抽取用例(use case)来重构,而不是一次性重新设计所有内容。
Before
public IActionResult Create(OrderDto dto) { /* everything */ }
After
public IActionResult Create(OrderDto dto)
{
_useCase.Execute(dto);
return Ok();
}
Create an Application Layer
Application/
└─ Orders/
└─ CreateOrderUseCase.cs
控制器变成调用应用层的适配器。
Introduce Repository Interfaces
不要立刻移除 EF Core。相反:
- 引入仓库接口。
- 包装
DbContext的访问。 - 将 EF 的使用向外迁移,形成迁移接缝。
public interface IOrderRepository
{
Task SaveAsync(Order order);
}
Improve the Domain Model
遗留的领域模型往往是贫血的。从小处着手:
- 值对象
- 不变量
- 行为丰富的实体
不需要一个“完美”的领域模型——只要比昨天的边界更好即可。
Refactoring Rules
- 依赖必须始终指向内部。
- 将 接口 向内部移动,实现 向外部移动。
- 使用 DI 在系统边缘进行组装。
What to Avoid
- 一次性大改写
- 框架彻底清除
- 过早抽象
- 领域模型过度建模
- 冻结功能开发
重构必须与交付共存。
Winning Indicators
当出现以下情况时,你就在取得成功:
- 编写测试变得更容易
- 控制器体积缩小
- 逻辑向内部迁移
- EF Core 的可见度降低
- 变更感觉更安全
- 架构逐步得到改进
Conclusion
Clean Architecture 不是终点,而是方向。遗留系统不需要完美——它们需要向正确方向的动能。为安全而重构,让系统持续前进。
Written by Cristian Sifuentes — helping teams modernize legacy .NET systems without rewrites, fear, or lost velocity.