欢迎光临
我们一直在努力

架构之Clean Architecture

架构之Clean Architecture

Clean Architecture 是 Robert C. Martin 提出的一套架构原则。它把软件划分成同心圆,强调业务规则位于中心,具体技术位于外围,所有依赖都指向中心。

它的核心不是某一种目录结构,而是依赖规则:内层代码不能依赖外层代码,越靠近中心越稳定,越靠近中心越不应该变化。

一、Clean Architecture 的同心圆

#mermaid-svg-1e1Qjt6OtDKXtSXb{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1e1Qjt6OtDKXtSXb .error-icon{fill:#552222;}#mermaid-svg-1e1Qjt6OtDKXtSXb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1e1Qjt6OtDKXtSXb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .marker.cross{stroke:#333333;}#mermaid-svg-1e1Qjt6OtDKXtSXb svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1e1Qjt6OtDKXtSXb p{margin:0;}#mermaid-svg-1e1Qjt6OtDKXtSXb .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster-label text{fill:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster-label span{color:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster-label span p{background-color:transparent;}#mermaid-svg-1e1Qjt6OtDKXtSXb .label text,#mermaid-svg-1e1Qjt6OtDKXtSXb span{fill:#333;color:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .node rect,#mermaid-svg-1e1Qjt6OtDKXtSXb .node circle,#mermaid-svg-1e1Qjt6OtDKXtSXb .node ellipse,#mermaid-svg-1e1Qjt6OtDKXtSXb .node polygon,#mermaid-svg-1e1Qjt6OtDKXtSXb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .rough-node .label text,#mermaid-svg-1e1Qjt6OtDKXtSXb .node .label text,#mermaid-svg-1e1Qjt6OtDKXtSXb .image-shape .label,#mermaid-svg-1e1Qjt6OtDKXtSXb .icon-shape .label{text-anchor:middle;}#mermaid-svg-1e1Qjt6OtDKXtSXb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .rough-node .label,#mermaid-svg-1e1Qjt6OtDKXtSXb .node .label,#mermaid-svg-1e1Qjt6OtDKXtSXb .image-shape .label,#mermaid-svg-1e1Qjt6OtDKXtSXb .icon-shape .label{text-align:center;}#mermaid-svg-1e1Qjt6OtDKXtSXb .node.clickable{cursor:pointer;}#mermaid-svg-1e1Qjt6OtDKXtSXb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .arrowheadPath{fill:#333333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1e1Qjt6OtDKXtSXb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1e1Qjt6OtDKXtSXb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1e1Qjt6OtDKXtSXb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster text{fill:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb .cluster span{color:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1e1Qjt6OtDKXtSXb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1e1Qjt6OtDKXtSXb rect.text{fill:none;stroke-width:0;}#mermaid-svg-1e1Qjt6OtDKXtSXb .icon-shape,#mermaid-svg-1e1Qjt6OtDKXtSXb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1e1Qjt6OtDKXtSXb .icon-shape p,#mermaid-svg-1e1Qjt6OtDKXtSXb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1e1Qjt6OtDKXtSXb .icon-shape .label rect,#mermaid-svg-1e1Qjt6OtDKXtSXb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1e1Qjt6OtDKXtSXb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1e1Qjt6OtDKXtSXb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1e1Qjt6OtDKXtSXb :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

实体 / 核心业务规则

用例 / 应用业务规则

接口适配器

框架与驱动器

常见划分如下:

层职责
实体层 企业级或领域级核心业务规则
用例层 具体应用流程和业务规则
接口适配层 Controller、Presenter、Gateway 等转换
框架与驱动层 Web、数据库、UI、外部服务

二、依赖规则

Clean Architecture 最重要的一条规则是:

源代码依赖只能从外向内,不能从内向外。

#mermaid-svg-TII3yQWpwG7U8Rjb{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TII3yQWpwG7U8Rjb .error-icon{fill:#552222;}#mermaid-svg-TII3yQWpwG7U8Rjb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TII3yQWpwG7U8Rjb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TII3yQWpwG7U8Rjb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TII3yQWpwG7U8Rjb .marker.cross{stroke:#333333;}#mermaid-svg-TII3yQWpwG7U8Rjb svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TII3yQWpwG7U8Rjb p{margin:0;}#mermaid-svg-TII3yQWpwG7U8Rjb .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster-label text{fill:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster-label span{color:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster-label span p{background-color:transparent;}#mermaid-svg-TII3yQWpwG7U8Rjb .label text,#mermaid-svg-TII3yQWpwG7U8Rjb span{fill:#333;color:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb .node rect,#mermaid-svg-TII3yQWpwG7U8Rjb .node circle,#mermaid-svg-TII3yQWpwG7U8Rjb .node ellipse,#mermaid-svg-TII3yQWpwG7U8Rjb .node polygon,#mermaid-svg-TII3yQWpwG7U8Rjb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TII3yQWpwG7U8Rjb .rough-node .label text,#mermaid-svg-TII3yQWpwG7U8Rjb .node .label text,#mermaid-svg-TII3yQWpwG7U8Rjb .image-shape .label,#mermaid-svg-TII3yQWpwG7U8Rjb .icon-shape .label{text-anchor:middle;}#mermaid-svg-TII3yQWpwG7U8Rjb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TII3yQWpwG7U8Rjb .rough-node .label,#mermaid-svg-TII3yQWpwG7U8Rjb .node .label,#mermaid-svg-TII3yQWpwG7U8Rjb .image-shape .label,#mermaid-svg-TII3yQWpwG7U8Rjb .icon-shape .label{text-align:center;}#mermaid-svg-TII3yQWpwG7U8Rjb .node.clickable{cursor:pointer;}#mermaid-svg-TII3yQWpwG7U8Rjb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TII3yQWpwG7U8Rjb .arrowheadPath{fill:#333333;}#mermaid-svg-TII3yQWpwG7U8Rjb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TII3yQWpwG7U8Rjb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TII3yQWpwG7U8Rjb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TII3yQWpwG7U8Rjb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TII3yQWpwG7U8Rjb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TII3yQWpwG7U8Rjb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster text{fill:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb .cluster span{color:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TII3yQWpwG7U8Rjb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TII3yQWpwG7U8Rjb rect.text{fill:none;stroke-width:0;}#mermaid-svg-TII3yQWpwG7U8Rjb .icon-shape,#mermaid-svg-TII3yQWpwG7U8Rjb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TII3yQWpwG7U8Rjb .icon-shape p,#mermaid-svg-TII3yQWpwG7U8Rjb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TII3yQWpwG7U8Rjb .icon-shape .label rect,#mermaid-svg-TII3yQWpwG7U8Rjb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TII3yQWpwG7U8Rjb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TII3yQWpwG7U8Rjb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TII3yQWpwG7U8Rjb :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

外层的 Framework

Interface Adapters

Use Cases

Entities

这意味着:

  • 实体不能知道用例层如何工作;
  • 用例不能知道使用哪个数据库或 Web 框架;
  • 接口适配层只负责协议和模型转换;
  • 框架与驱动层可以被替换,而不影响核心规则。

三、依赖倒置如何实现

如果用例层需要使用数据库,它不能直接依赖数据库实现,而要在用例层定义接口:

public interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
}

数据库实现放在外层:

public class JpaOrderRepository implements OrderRepository {
// 依赖 JPA 的实现
}

依赖关系是:

JpaOrderRepository -> OrderRepository
OrderRepository <- UseCase

这样,数据库实现依赖用例层定义的接口,而不是用例层依赖数据库。这是 Clean Architecture 能保持核心稳定的关键。

四、Clean Architecture 与分层架构的区别

传统分层架构通常按技术分层,Clean Architecture 按业务稳定程度分层。

维度传统分层Clean Architecture
核心依据 技术角色 业务规则与稳定程度
领域模型位置 可能在中间或下层 永远在最中心
依赖方向 上层依赖下层 外层依赖内层
可测试性 依赖容器和数据库较多 核心逻辑可纯内存测试
框架地位 常常贯穿全系统 只存在于最外层

五、Clean Architecture 的优点

  • 业务规则独立于框架,容易测试;
  • 数据库、Web、UI 可以独立替换;
  • 长期演进中,核心业务不容易被技术细节污染;
  • 模块边界清晰,团队更容易理解依赖关系;
  • 与洋葱架构、六边形架构可以互相借鉴。

六、常见误用

问题表现
目录套壳 只建了 entity、usecase、adapter 包,没有真正约束依赖
贫血实体 业务规则全部跑到了用例层之外
过度转换 每一层都做大量重复 DTO 转换
过度抽象 简单 CRUD 也引入复杂接口
循环依赖 内层通过隐式方式依赖外层

Clean Architecture 并不要求所有项目都实现完全一样的目录。它的重点是依赖方向、测试能力和稳定边界的清晰程度。

结语

Clean Architecture 的目标,是让软件中最有价值的东西远离最易变化的东西。业务规则应该比数据库长寿,领域概念应该比框架更稳定。

当团队把依赖方向变成代码层面的硬约束,架构就不再只是画图,而成为每天写代码时都能执行的纪律。

清洁的不是目录,而是依赖。
让业务规则留在中心,让技术细节留在边缘。
框架会更换,核心不该随它一起坍塌。

赞(0)
未经允许不得转载:171主机测评 » 架构之Clean Architecture
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址