AI 时代的 Clean Architecture:防止架构液化
Source: Dev.to
在 AI 时代的清洁架构:防止架构液化
“当系统的边界变得模糊,代码的职责混乱不清时,我们称之为‘架构液化’。” — 作者
前言
随着生成式 AI、自动化工具以及低代码平台的快速发展,软件架构正面临前所未有的挑战。我们可以在几分钟内生成完整的 CRUD 应用,却很容易忽视 职责分离、可维护性 与 可测试性 等核心原则。本文探讨在这种环境下,如何通过 清洁架构(Clean Architecture) 的理念来保持系统的稳固,避免出现“液化”现象。
什么是架构液化?
- 职责混淆:业务逻辑、数据访问、UI 代码相互交织,难以定位问题根源。
- 技术债务急速累积:快速生成的代码缺乏文档、测试和审查,导致后期维护成本爆炸。
- 不可预测的演化:每次引入新模型或 AI 生成的代码,都可能破坏已有的边界和契约。
图示:
(图片保持不变)
清洁架构的核心原则
| 原则 | 含义 | 在 AI 时代的意义 |
|---|---|---|
| 依赖规则 | 代码只能依赖更抽象的层级,外层不能影响内层。 | 防止 AI 生成的实现细节渗透到业务核心。 |
| 分层结构 | 实体 → 用例 → 接口适配器 → 框架/驱动。 | 为 AI 工具提供明确的插入点,保持层次清晰。 |
| 接口隔离 | 每个模块只暴露必要的接口。 | 降低 AI 生成代码的耦合度,便于后续替换。 |
| 单一职责 | 每个类/函数只做一件事。 | 防止一次性生成的大块代码违背 SRP。 |
实践指南:在 AI 助手中使用清洁架构
1. 先定义 用例(Use Cases) 再生成实现
# 用例示例(伪代码,保持不翻译)
class CreateOrderUseCase {
execute(orderDto) { /* ... */ }
}
- 步骤:先在文档或 UML 中明确业务流程。
- AI 提示:让模型仅生成
CreateOrderUseCase的内部实现,而不是整个控制器或数据库层。
2. 使用 接口(Ports) 作为 AI 生成代码的“安全网”
# 接口定义(保持原样)
interface OrderRepository {
save(order): Promise<void>;
}
- AI 只负责实现
OrderRepository的具体类(如MongoOrderRepository),而不修改接口本身。
3. 将 框架代码 与业务代码严格分离
# 框架适配器(保持原样)
export const createOrderHandler = async (req, res) => {
const useCase = new CreateOrderUseCase(orderRepo);
// ...
};
- 让 AI 生成的代码仅限于适配层(如 Express、FastAPI),业务层保持手写或审查后合并。
4. 强制 自动化测试 作为生成代码的门槛
# 测试示例(保持原样)
describe('CreateOrderUseCase', () => {
it('should persist a new order', async () => {
// ...
});
});
- 在 CI 流程中加入 AI 生成代码的覆盖率检查,未通过的提交直接阻止合并。
常见陷阱与对策
| 陷阱 | 描述 | 对策 |
|---|---|---|
| 一次性生成全部 | 直接让 AI 生成完整的 CRUD 应用。 | 按层级分批生成,先定义接口再实现。 |
| 忽视审查 | 直接合并 AI 生成的代码。 | 引入 代码审查(Code Review) 与 静态分析。 |
| 缺少文档 | 生成的代码没有注释或设计文档。 | 使用 AI 辅助生成 UML、API 文档,并强制提交。 |
| 过度依赖特定框架 | 代码紧耦合于某个框架的实现细节。 | 通过 适配器模式 把框架代码隔离在外层。 |
示例项目结构
src/
├─ domain/ # 实体、业务规则
│ └─ models/
├─ usecases/ # 用例层
│ └─ CreateOrderUseCase.ts
├─ ports/ # 接口(Ports)
│ └─ OrderRepository.ts
├─ adapters/ # 实现(Adapters)
│ └─ infra/
│ └─ MongoOrderRepository.ts
├─ frameworks/ # 框架/驱动层
│ └─ express/
│ └─ orderController.ts
└─ tests/
└─ usecases/
└─ CreateOrderUseCase.test.ts
要点:AI 生成的代码只应出现在
adapters/与frameworks/目录下,domain/与usecases/必须保持手写或经过严格审查。
结语
在 AI 赋能的时代,清洁架构 并不是阻碍创新的枷锁,而是 防止系统液化、保持长期可维护性的防火墙。通过明确的层次、严格的接口以及自动化测试,我们可以让 AI 成为 可靠的生产力工具,而不是潜在的技术债务来源。
本文作者: UXter
原文链接: https://dev.to/uxter/clean-architecture-in-the-age-of-ai-preventing-architectural-liquefaction-5d8d
如有疑问或想进一步讨论,欢迎在评论区留言。
介绍
AI 使执行变得廉价;模型在局部进行优化,而不是针对整体架构。在许多团队中,副作用并不是糟糕的代码或构建失败,而是更具结构性的东西:架构液化。
什么是架构液化?
架构液化 是在持续的概率代码生成和加速的变更周期下,结构边界的逐步丧失。它不会在单个 PR 中发生——层边界变软,依赖关系出现逆向,契约漂移,不变式削弱,“临时”捷径堆积。一切仍然能工作,直到变更成本悄然倍增。没有明确的约束,熵随我们更快交付而增长。
清晰架构作为确定性外壳
清晰架构常被描述为一种分层纪律。在 AI 辅助开发的语境下,它可以发挥另一种作用:作为围绕概率执行的 确定性外壳。这不是教条或审美偏好——它是一种稳定机制。当边界明确且依赖方向被强制时:
- 解决方案空间 收窄。
- 漂移变得 可检测。
- 结构违规 更早显现。
- 局部优化不能悄然破坏全局设计。
架构因此成为一个 控制面。
AI 强制之前
在没有 AI 的时代,出现架构违规需要付出努力。开发者必须有意识地决定去突破边界。
AI 强制之后
现在,违规可以在几秒钟内生成,而且由于 AI 生成的代码往往“看起来正确”,结构侵蚀更难被察觉。真正的代价并不是当下的糟糕代码,而是漂移保持不可见,直到一次重构突然触及半数代码库。你的提示和规则越“灵活”且未明确规定,液化的速度就越快——模型会在局部最容易的方向填补空白。
使用项目规则强制边界
我曾经把我们所有的架构原则——边界、依赖规则、各自所在位置——写进 docs/ 文件夹中的纯 Markdown 文档,然后将它们接入 Cursor 作为项目规则,使其在每个提示中自动注入。
tree ./docs/
.
├── ARCHITECTURAL-STYLE-GUIDE.md
├── CLEAN-NEST-APP.md
├── architecture
│ ├── adapters.md
│ ├── core.md
│ ├── controllers.md
│ ├── events.md
│ ├── inter-module-communication.md
│ ├── modules.md
│ ├── structure.md
│ ├── testing.md
│ └── when-to-simplify.md
└── guides
├── cheat-sheet.md
├── common-patterns.md
└── quick-start.md
在此之前,Cursor 常常把仓库调用直接写进控制器,或把基础设施的导入泄漏到领域层——它只是简单地遵循代码库中看到的模式。规则生效后,它开始通过用例进行路由,并保持适配器不进入核心层。它仍然不是完美的:有时会过度设计或选择错误的抽象。但跨层违规的频率大幅下降。模型现在有了一个明确的优化目标,而不仅仅是优化“能运行的代码”。
假设与可检验性
那个单一数据点符合假设:
明确的边界加上执行可以减少结构漂移,即使代码是 AI 生成的。
为了使其可检验,我们需要漂移指标(例如,依赖违规、跨层调用)、随时间的审查成本,以及在修复违规时的重构范围。如果遵守严格规则的团队漂移程度与其他团队一样,或者审查和重构成本尽管有执行仍持续增长,则该假设将被证伪。我正在准备具体的方法来定义和跟踪这些——漂移指标和成本——以便后续帖子使用。
重新思考 AI 重度工作流中的清洁架构
Clean Architecture 通常被描述为边界、向内的依赖、业务逻辑与其他部分隔离。的确如此——但在 AI 重度工作流中,更有用的视角是:
概率执行,确定性治理。
我们并不是消除不确定性;我们只是给它套上一个盒子,使模型的选择保持在盒子内部。架构本身就成为这个盒子。
未解之问
- 如果你在开发中大量使用 AI,你的边界是变得更强还是更弱?
- 维持脑中结构的成本是上升还是下降?
我还没有结论——只有一个假设。AI 已经优化了执行;我们是优化了稳定性,还是仅仅加速了熵的产生,这仍是未解之谜。显而易见的结构往往是当一切加速时最先瓦解的。接下来的文章中,我将探讨其他防止事物液化的方法。
