|
@@ -0,0 +1,103 @@
|
|
1
|
+> 为什么采用DDD领域驱动?
|
|
2
|
+
|
|
3
|
+- 一句话来说就是解耦,便于扩展。领域驱动也是目前正流行的微服务的前提条件之一。
|
|
4
|
+> 2019年的智能照明版属于DDD领域驱动吗?
|
|
5
|
+
|
|
6
|
+- 2019年的智能照明版中包含了一些DDD的设计思想。比如洋葱头式由内而外的分层;比如用到了MediatR, 是中介者模式的体现,里面包含了Command和对应的Handler;比如CQRS的思想......这些都是DDD的重要思想。但是,2019年的这个版本并没有实践出DDD的精髓。在DDD的设计中,领域模型必须具有松耦合、原子性的特点。所谓的松耦合是指领域实体之间的关系。在2019年的这个版本中是强耦合,领域实体基本上和数据库表是一一对应关系,表和表之间存在外键约束。当系统不断扩展的时候,这种强关系会让领域关系变得纠缠不清。另外,在涉及多表操作的时候,2019年的这个版本有时会通过事务来处理,即通过代码维护领域关系。而真正的DDD会把事务内嵌在Aggregate中。所谓Aggregate有且只有一个聚合根Aggregate Root。
|
|
7
|
+
|
|
8
|
+- 洋葱头式由内而外的分层:
|
|
9
|
+ - 核心层是与领域或技术无关的基础构件块,它包含一些通用的构件块。
|
|
10
|
+ - 领域层(Domain)是业务逻辑的地方,每个类的方法都是按照领域通用语言中的概念进行命名
|
|
11
|
+ - API层是领域层的入口,它使用领域中的术语和对象
|
|
12
|
+ - 基础架构(Infrastructure)层是最外部的一层,它包含了对接各种技术的适配器,例如数据库、用户界面以及外部服务。
|
|
13
|
+
|
|
14
|
+- CQRS的思想:CQRS,全称Command Query Responsibility Segregation。直译过来就是命令查询的职责分离。
|
|
15
|
+ - Command指的是增加/更新数据的处理。
|
|
16
|
+ - Query指的是查询数据的处理。它不会造成数据的变更
|
|
17
|
+
|
|
18
|
+> Aggregate
|
|
19
|
+
|
|
20
|
+- 是一种模式,在代码中实现的具体形式很简单
|
|
21
|
+ - 定义一个Entity,作为Aggregate Root,称之为聚合根
|
|
22
|
+ - 遵循Aggregate的完整性规则对领域数据进行操作
|
|
23
|
+
|
|
24
|
+- Aggregate的完整性规则
|
|
25
|
+ - 所有的代码只能通过 Aggregate Root,即聚合根这个特殊的 Entity 访问系统的 Entity,而不能随便的操作任一的 Entity。
|
|
26
|
+ - 每个事务范围只能更新一个 Aggregate Root 及它所关联的 Entity 状态。
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+> Event Storming
|
|
30
|
+- 我们听说过"头脑风暴",大致是让团队成员一起参与讨论出主意。在DDD中,Event Storming,事件风暴,是让领域专家(熟悉业务的人)、技术人员、产品经理等齐聚一堂,用大家都可以理解的形式表达业务逻辑。通常来说,会在一个比较空的房间内腾出一面墙,大家把想法通过便签贴到墙上。
|
|
31
|
+
|
|
32
|
+- 举一个例子:假设用户在某个系统中发布信息,Event Storming中的关键要素大致包括:
|
|
33
|
+ - Event:触发领域状态改变的动作。比如这里叫,NewsPublished
|
|
34
|
+ - Aggregate:这里的消息就是领域实体。比如这里叫,News
|
|
35
|
+ - Command: 所有领域状态的改变,肯定是用户通过系统界面发出的,用户从界面发出的指令叫Command(还记得MediatR中的xxxCommand:IRequest<T>, xxxCommandHandler<TResult, T>吗?这里的IRequest接口就代表着用户发出的Command,每个Command都有响应的Handler)
|
|
36
|
+ - User:操作系统的用户或角色
|
|
37
|
+ - Information:用户发出Command信息需要基本的前提信息
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+- 如果我们能清晰地知道Command, Command所需要的Information, 是哪个用户或角色执行Command, 对那个Aggregate执行Command, 以及Event是什么,那基本上所有相关人员对系统的业务逻辑已经做了比较清晰的、统一的描述。
|
|
41
|
+
|
|
42
|
+# 用ProcessOn报警写报警版的Event Storming
|
|
43
|
+ - 报警相关的发生在Warning Aggregate上。Aggregate用浅黄色表示
|
|
44
|
+ - 事件(Event)用橙色表示 。事件的一般形式为:Aggregate+动词过去式
|
|
45
|
+ - Command, 浅蓝色,一般形式为:动词+Aggregate
|
|
46
|
+ - 备注用紫色表示
|
|
47
|
+ - 用浅绿色来表示读模型。与 CQRS 的 Query 含义相同
|
|
48
|
+
|
|
49
|
+- 根据APP界面,报警被创建后,用户申请维修,这时系统会查询用户的报警套餐(User Pacakge), 这里又出来一个User Package的Aggregate,而且由于是查询,返回的是Read Model(一般用绿色), Read Model交给界面
|
|
50
|
+- 这个界面上有两种可能,一个是用户套餐中的免费维修次数还有,另一个是免费维修次数用完了。如果还有维修次数,用户确认。如果没有维修次数,用户查询所有套餐(Packages),再次Read Model和界面。
|
|
51
|
+
|
|
52
|
+> 报警首页
|
|
53
|
+
|
|
54
|
+>报警相关
|
|
55
|
+- 系统->Create Warning(Command)->Warning(Aggregate)->Warning Created(Event)->当前报警
|
|
56
|
+- 用户->ApplyFix(Command)->Warning(Aggregate)
|
|
57
|
+- 系统->Query User Package(Command)->User Package(Aggregate)->User Package Read Model
|
|
58
|
+ - 根据APP界面,报警被创建后,用户申请维修,这时系统会查询用户的报警套餐(User Pacakge), 这里又出来一个User Package的Aggregate,而且由于是查询,返回的是Read Model(一般用绿色), Read Model交给界面
|
|
59
|
+- 用户->Query Pakages(command)->Packages(Aggregate)->Packages Read Model
|
|
60
|
+ - 这个界面上有两种可能,一个是用户套餐中的免费维修次数还有,另一个是免费维修次数用完了。如果还有维修次数,用户确认。如果没有维修次数,用户查询所有套餐(Packages),再次Read Model和界面。
|
|
61
|
+- 用户-> Confirm Apply Fix(Command)->Warning (Aggregate)->Warning Apply Fix Confirmed(Event)
|
|
62
|
+- 客服->Dispatch Warning(Command)->Warning(Aggregate)->Warning Dispatched(Event)
|
|
63
|
+- 电工-> Accept Warning(command)->Warning->Warning Accepted(Event)
|
|
64
|
+- 电工-> Confirm Warning Repair->Warning->Warning Repair Confirmed(Event)
|
|
65
|
+- 系统->Finish Warning->Warning ->Warning Finished(Event)
|
|
66
|
+
|
|
67
|
+# 开关管理
|
|
68
|
+> 位置分类相关
|
|
69
|
+- 安装人员-> Create Electric Box(Command)->Electric Box(Aggregate)->Electric Box Creted
|
|
70
|
+- 安装人员-> Create Category(Command)->Category用途(Aggregate)->Category Creted
|
|
71
|
+- 安装人员-> Create Line Type(Command)->Line Type开关类型(Aggregate)->Category Line Type
|
|
72
|
+- 安装人员-> Add Mac To Electric Box(Command)->Mac(Aggregate)->Mac Added To Electric Box(Event)
|
|
73
|
+- 安装人员-> Add Line To Category(Command)->Line线路(Aggregate)->Lined Added To Category(Event)
|
|
74
|
+- 安装人员-> Add Line To Line Type(Command)->Line线路(Aggregate)->Lined Added To Line Type(Event)
|
|
75
|
+- 安装人员-> set Line (Command)->Line线路(Aggregate)->Line Set(Event)
|
|
76
|
+
|
|
77
|
+> 场景
|
|
78
|
+- 安装人员+用户 TimingInfo+LineInfo(Read model)->Create Scene(Command)->SCene(Aggregate)->Scene Created(Event)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+> 电费和套餐
|
|
82
|
+
|
|
83
|
+- 管理员->Set User Package->User Package->User Package set
|
|
84
|
+- 管理员->Set User Electricity Type-> User Electricity Type-> User Electricity Type set
|
|
85
|
+- 管理员-> Set Fixed Electricity->User Fixed Electricity->User Fixed Electricity Set
|
|
86
|
+- 管理Set High Low Electricity ->User High Low Electricity->User High Low Electricity Set
|
|
87
|
+- 管理员-> Set Step Electricity->User Step Electricity->User Step Electricity Set
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+> 领域设计
|
|
95
|
+- 所有的领域应该有一个抽象基类,而且是一个泛型类。
|
|
96
|
+```
|
|
97
|
+public abstract class Entity<T>
|
|
98
|
+{
|
|
99
|
+ public T Id{get;protect set;}
|
|
100
|
+
|
|
101
|
+}
|
|
102
|
+```
|
|
103
|
+- DDD中有一个很重要的概念 ,叫做Object Value(值对象)。会把一个Aggregate里的对象类型看作是值类型。这样做一方面是减少Aggregate对关系的依赖,一方面可以很好判断Aggregage的状态。同样也是一个泛型抽象基类,如果一个Object类型的对象想要被当作值类型处理,那就继承这个泛型抽象基类。
|