常见软件架构和设计原则

一、软件架构演进

演进主线:代码混杂 → 按技术分层 → 依赖倒置 → 以业务领域为核心,隔离业务与外部基础设施

阶段 特征 问题
大泥球 Big Ball of Mud 所有代码混在一起,业务、DB、UI耦合,硬编码 改动一处多处受影响,难以测试维护
三层架构(3‑Tier) 技术分层:表现层Controller → 业务Service → DAO数据访问层;上层直接依赖下层 业务逻辑依赖数据库/框架;更换外部技术需要修改业务代码;业务逻辑难单元测试
依赖倒置DIP登场 高层业务不依赖低层实现;业务定义抽象接口,底层实现去实现接口 理论基础,没有给出代码结构
DDD领域驱动设计 软件核心是业务领域而非数据库框架;通用语言、领域建模 提供业务建模方法论,不规定物理代码架构
现代架构(洋葱/六边形/整洁架构) 以业务内核为中心,依赖向内;外部框架、数据库全部作为外围适配器 解决业务与技术耦合问题

核心转变:早期业务代码适配数据库框架;现代架构:外部技术适配业务内核。

二、三大现代架构模式

三者思想同源,均以依赖倒置DIP为基石,内层绝对不依赖外层,依赖全部指向内核。

1. 六边形架构(端口‑适配器架构 Port‑Adapter)

  • 提出:Alistair Cockburn 2005
  • 核心思想:应用是封闭内核,外部系统不能直接访问内核,全部通过端口+适配器交互
  • 端口Port:内核定义的接口
    • 入端口(驱动端口):外部驱动应用(HTTP、定时任务)
    • 出端口(被驱动端口):应用调用外部(DB、MQ、第三方API)
  • 适配器Adapter:端口的具体实现
    • 入适配器(驱动适配器):Controller,把外部请求转为内核调用
    • 出适配器(被驱动适配器):Repository实现、MQ客户端,实现内核定义接口
  • 侧重点:隔离外部异构系统

2. 洋葱架构 Onion Architecture

  • 提出:Jeffrey Palermo 2008
  • 圈层由内向外:领域模型层 → 领域服务层 → 应用服务层 → UI/基础设施层(最外层)
    1. 领域模型层 Domain Model:实体、值对象、聚合根;无任何框架/数据库依赖
    2. 领域服务层 Domain Services:跨实体业务逻辑、仓储接口定义
    3. 应用服务层 Application Services:用例编排,调用领域对象;无业务规则;处理事务
    4. UI/基础设施层(最外层):Controller、RPC入口、Repository实现、MQ、第三方适配器
  • 侧重点:DDD友好,给出清晰包结构实践

3. 整洁架构 Clean Architecture

  • 提出:Robert C. Martin(Uncle Bob)2012
  • 同心圆由内向外:实体Entities → 用例Use Cases → 接口适配器Interface Adapters → 框架与驱动Frameworks & Drivers
    1. 实体 Entities:企业级业务规则,可被多个应用共用;可以是带有方法的对象,也可以是一组数据结构和函数
    2. 用例Use Cases:应用业务规则,编排实体完成场景
    3. 接口适配器:DTO转换、Controller、Repository实现
    4. 框架驱动层:Web框架、数据库、MQ等工具
  • 侧重点:区分"企业业务规则"和"应用场景规则",标准化依赖约束

三者关系:
六边形侧重外部隔离;洋葱侧重DDD代码包分层;整洁架构做理论标准化;开发中经常混用。

❗关键约束:接口定义在内核(Domain),实现放在Infrastructure,禁止内核import外部框架包

三、CQRS 命令查询职责分离

CQRS:Command‑Query Responsibility Segregation,命令查询职责分离,源自CQS。

  1. Command 命令(写):修改状态,不返回业务数据;执行领域业务逻辑;操作写模型。
  2. Query 查询(读):读取数据,不修改状态;独立读模型,为查询场景优化。

两种落地方式:

  1. 简单版:读写共用数据库,仅代码模型分离。
  2. 事件驱动版:命令完成发布领域事件,查询侧消费事件更新读库,读写可以使用不同存储,最终一致性。

✅收益:读写独立伸缩;查询不受领域模型约束;适配事件溯源。
❌代价:复杂度上升;需要处理最终一致性;维护两套模型。

适用场景:业务复杂、查询视图繁多、读写压力差异大;普通CRUD系统不要强行引入。

Event‑Sourcing 事件溯源:不保存实体当前状态,持久化全部领域事件,回放事件重建状态;常与CQRS配套。

四、DDD 领域驱动设计

DDD不是框架,是复杂业务的建模方法论;分为战略DDD(划分边界)、战术DDD(对象建模)。
DDD负责业务建模;洋葱/六边形负责代码物理架构;二者可以搭配使用。

4.1 战略DDD(先划边界,再写代码)

  1. 领域 Domain:软件整体业务范围。
  2. 子域 Subdomain(业务视角)
    • 核心子域:企业核心价值,重点投入建模。
    • 支撑子域:保障业务运行,无核心竞争力。
    • 通用子域:通用能力,优先外购/开源。
  3. 限界上下文 Bounded Context(模型边界)
    • 在该边界内,通用语言(Ubiquitous Language)生效;同一词汇在不同上下文含义不同。
    • 子域 ≠ 限界上下文 ≠ 微服务;限界上下文是逻辑模型边界,微服务是部署边界。
  4. 防腐层 ACL:上下文之间交互,做模型转换,避免外部模型污染本上下文。

4.2 战术DDD(限界上下文内部建模)

  1. 实体 Entity
    • 具备唯一ID,拥有生命周期,状态可变;相等判断依据ID;可以包含业务行为。
  2. 值对象 Value Object
    • 无独立ID;由一组属性构成;不可变;相等判断对比全部属性;依附实体存在。

    判断口诀:需要独立追踪生命周期→实体;仅用来描述属性→值对象。

  3. 聚合 Aggregate & 聚合根 Aggregate Root(AR)
    • 聚合:一组强相关对象的内聚整体;定义业务一致性边界。
    • 聚合根AR:聚合对外唯一入口;外部只能通过聚合根访问内部子实体;子实体不对外暴露。
    • 事务约束:一个事务只修改一个聚合根;Repository只针对聚合根。
  4. 领域服务 Domain Service
    • 位于领域层;存放不属于单个实体、跨多个聚合根的业务规则;无状态;不操作数据库。
  5. 应用服务 Application Service
    • 位于应用层;无业务规则;负责用例编排、事务、仓储调用、流程调度。

    区分:应用服务管流程;领域服务管业务规则。

  6. 领域事件 Domain Event
    • 聚合产生,代表已经发生的业务事实;命名使用过去式;用于解耦业务流程。
  7. 仓储 Repository
    • 接口定义在领域层,实现在基础设施层;面向聚合根,抽象为内存集合。

4.3 DDD + 洋葱架构分层归属

  • Domain领域层:实体、值对象、聚合根、领域服务、领域事件、Repository接口
  • Application应用层:应用服务、输入输出DTO
  • Infrastructure基础设施层:Repository实现、MQ、第三方适配器、ACL防腐层
  • UI层:Controller、RPC接口

4.4 DDD常见坑

  1. 贫血模型:领域对象只有get/set,全部业务逻辑写在应用服务;提倡充血模型,业务逻辑下沉领域对象。
  2. Repository接口写在基础设施层,违反依赖倒置。
  3. 聚合设计过大,一个事务修改多个聚合根。
  4. 简单CRUD强行落地DDD,造成过度设计。

✅适合DDD:业务复杂、规则多、长期迭代;❌不适合:简单CRUD、一次性原型。

五、SOLID 面向对象五大原则

是DDD、洋葱、整洁架构底层理论基础

  1. SRP 单一职责原则

一个类只有一个引起它变化的原因;一个类只承担一类职责。

  1. OCP 开闭原则

对扩展开放,对修改关闭;新增能力尽量新增代码,少修改已有稳定代码;依靠抽象、多态实现。

  1. LSP 里氏替换原则

子类可以完全替换父类;继承不能破坏父类约定;慎用继承;优先组合。

  1. ISP 接口隔离原则

客户端不依赖不需要的接口;拆分大而全的胖接口为细粒度接口。

  1. DIP 依赖倒置原则

高层模块不依赖低层模块,二者依赖抽象;抽象不依赖细节,细节依赖抽象。
DIP是设计思想;DI依赖注入是实现该思想的技术手段。

SOLID关系:SRP是基础,OCP是目标,LSP保障多态,ISP优化接口,DIP管控依赖方向。

六、其他经典软件设计原则

  1. 高内聚、低耦合

    • 高内聚:相关逻辑内聚在同一个模块/类;
    • 低耦合:模块之间依赖尽可能少,依赖优先抽象接口。
    • 几乎所有设计原则最终目标。
  2. 迪米特法则 LoD(最少知识原则)

不要和陌生人说话;避免对象A深入访问对象B内部的对象;行为委托给对象自身提供方法。

  1. 组合优于继承

has‑a组合优先于is‑a继承;继承强耦合容易破坏LSP。

  1. DRY Don't Repeat Yourself 不要重复自己

消除知识逻辑重复;不是单纯消除长得像的代码;语义不同不要强行合并。

  1. KISS Keep It Simple, Stupid 保持简单

优先简单方案,避免不必要复杂。

  1. YAGNI You Aren't Gonna Need It 你不会需要它

不为假想未来需求提前开发;真实需求到来再重构,对抗过度设计。

  1. CQS 命令查询分离

查询返回数据不修改状态;命令修改状态不返回业务数据;CQRS思想源头。

  1. 正交性原则

模块之间互相独立;修改A不会影响B;业务逻辑与技术实现正交隔离。

  1. 契约式设计

定义前置条件、后置条件、不变量;DDD聚合的业务不变性就是契约。

七、核心概念对照速查表

概念 所在层 核心作用
领域服务 Domain领域层 存放业务规则,跨聚合逻辑
应用服务 Application应用层 流程编排,无业务规则
Repository接口 Domain领域层 聚合根查询保存抽象
Repository实现 Infrastructure基础设施层 数据库具体实现
聚合根 Domain领域层 聚合对外唯一访问入口
限界上下文 战略DDD 业务模型边界
端口‑适配器 六边形架构 隔离外部系统

八、实战落地建议

  1. 拒绝教条主义:原则、架构都是权衡取舍,不是硬性法律。
  2. 简单业务优先KISS/YAGNI,普通三层足够;复杂核心业务引入DDD、洋葱/六边形架构。
  3. 不要上来就微服务;先划分逻辑限界上下文,后期再考虑物理拆分。
  4. 优先保证业务规则落在领域对象,不要把业务逻辑散落在Service。
  5. CQRS、事件溯源不要滥用,会显著增加复杂度。
posted @ 2026-09-06 16:49  灰马非马  阅读(28)  评论(0)    收藏  举报