不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

Go高级工程师_第9课_框架之上的业务分层

传统分层

MVC

远古时代的 web 项⽬:

当前后端分离成为主流共识:

MVC -> DLC

业务逻辑越来越复杂,拆成单独的 logic:

业务代码越来越多了,看不懂怎么办

贫⾎模式 充⾎模式
业务逻辑都在 logic 层内 要让 domain object 即 entity 有更多逻辑
Struct、Class 上没有任何逻辑,或只有少量逻辑 通过聚合来组合 entity 的逻辑

贫⾎模式代码示例

业务逻辑被埋没在存储业务中:

使⽤充⾎模式改造后

定义 Customer Repo:

相关的 Repo 实现都较简单:

主流程根据不同的⽤例选择执⾏不同的对象函数,⽽不是⽤数据建模,将所有处理集中在同⼀个函数⾥

SOLID 中的 DIP(Dependency Inversion Principle)

通过 DIP,我们可以做到依赖⽅向与程序控制⽅向相反

整洁架构 style

整洁架构

Uncle Bob 提出的整洁架构:

Uncle Bob 同时也是⾯向对象编程中的SOLID 原则的提出者

整洁架构的核⼼观点

  • 不与框架绑定:业务不应该与某个 web 框架绑定,应该做到想换就换。
  • 可测试:业务逻辑应该在没有 UI、database 环境、web server 等所有外部环境的前提下做到可测试。
  • 不与 UI 绑定:不与具体的 UI 库绑定,项⽬应该做到随意切换外部 UI,⽐如可以将 web UI 替换为 console UI,同时⽆需业务逻辑做修改。
  • 不与数据库绑定:可以把 Oracle 换成 SQL Server,也可以换成Mongo,换成 BigTable,换成 CouchDB。业务不依赖具体存储⽅式。
  • 不依赖任何外部代理:你的业务应该对外部环境⼀⽆所知。

不与框架绑定

  • 业务代码⼊⼝不应与任何协议绑定
  • 框架代码(如 gin.Context)不要⼊侵到业务层

可测试

在没有 UI、database 环境、web server 等所有外部环境的前提下做到可测试。

不与 UI 绑定

我们做的基本上都是前后分离的系统,不太容易与 UI 发⽣绑定:

不与数据库绑定

不与数据库绑定:可以把 Oracle 换成 SQL Server,也可以换成Mongo,换成 BigTable,换成 CouchDB。业务不依赖具体存储⽅式。

需要借助 DDD 中的 Repo 设计⽅式:

不依赖任何外部代理

没有外部 agent 也能启动,做到⽐较难。

⽐如你是不是经常会碰到:

  • 没有配置下发的 agent 的环节,本系统跑不起来。
  • 没有 service mesh 模块,本系统跑不起来。
  • 没有 metrics 采集模块,本系统跑不起来。

DDD style

DDD 社区就是名词多

  • Entity
  • Value Object
  • Repository
  • Aggregate
  • Aggregate Root

ports && adapters

六边形架构,也被称为端⼝与适配器架构,所有外部类型都有适配器与之对应,外部通过 API 与内部交互。——《实现领域驱动设计-ch4》

Ports = interface
Adapters = instances

DDD 中的战术设计-Value Object(Value Type)

Value Type 是不可变(immutable),可⽐较(comparable)的值
例如:
数字: 1,2,3;
字符串:“⼈⺠⽇报”,“⽕星探测”
结构体:var addr = address {“100010”, “xi er qi”, “33.133”, “21.3243”}

DDD 中的战术设计-Entity

  • Entity 的关键是其有 ID 作为唯⼀标识
  • Value type 则没有 ID
  • 有 ID 意味着 entity 是可变(mutable)的,会随着时间更新

DDD 中的战术设计-Aggregate

• Aggregate 和 entity 设计上类似,也有 ID,也是可变(mutable)的
• 聚合可以⽤ entity + value object 构成,如右图
• 每⼀个聚合对应⼀个 Repo interfac
• 聚合需要对聚合内的数据⼀致性负责,可以认为聚合是数据⼀致性的边界
• 聚合之外的⼀致性采⽤最终⼀致性保证

Aggregate:

|
Repo:

  • ⼀个聚合可以只有⼀个 entity
  • 也可以有多个 entity 和 value object

DDD 中的战术设计-Aggregate Root

• 聚合根也是聚合
• 与普通聚合唯⼀差别:聚合根对外暴露,要关联某个领域内的对象,⼀定是通过聚合根的 id 来进⾏关联的。
• 在聚合内的⼀些 entity ⼀般不对外暴露,但随着时间的推移也可能变成聚合根。

DDD 中的战术设计-Repo Pattern

DDD 中的战术设计

插件化架构

其它知识

依赖注⼊⼯具

main 模块是上帝模块,需要负责初始化所有内部类

⼀些可以参考的 API 设计指南

Paypal 的 API 设计指南
Paypal 的 API 设计指南(中⽂)
⽹友的 API 设计指南
微软的 API 设计指南
微软的 API 设计指南(中⽂)
Google 的 API 设计指南

References

The Clean Architecture
使⽤ Go 实践 clean architecture
整洁架构相关的代码
Design a DDD-oriented microservice
wire简介
整洁架构重新思考
Bounded-context 和 microservice 怎么对应
贫⾎模式还是充⾎模式
Go 与 DDD 中的战术设计
DDD 代码模板
DDD 代码模板
⼦领域和 bounded context 的关系
设计微服务的 domain mode
Netflix 的六边形架构实践
将贫⾎项⽬重构为 DDD 模式
将贫⾎项⽬重构为 DDD 模式

------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!