|
@@ -0,0 +1,259 @@
|
|
1
|
+> 领域驱动设计
|
|
2
|
+- 思想体系
|
|
3
|
+ - DDD 是一种开发思想体系,旨在管理为复杂问题域编写的软件的创建和维护工作。
|
|
4
|
+ - 专注领域要高于其他一切需求。
|
|
5
|
+ - DDD是模式、原则和实践的集合,可以被用到软件设计以管理
|
|
6
|
+- 模式类型
|
|
7
|
+ - 战略模式:对任何应用程序都有用 重点提炼问题域(核心子域)->塑造应用程序架构
|
|
8
|
+ - 战术模式:用于实现富领域模型(只当模型在领域逻辑做够丰富时才有用)
|
|
9
|
+- 实践原则
|
|
10
|
+ - 专注于核心领域 强调在核心子域付出最多努力的需要
|
|
11
|
+ - 通过协作学习 开发团队与业务专家的协作
|
|
12
|
+ - 通过探索和实验来创建模型
|
|
13
|
+ - 通信 公共语言(UL)
|
|
14
|
+ - 理解模型的活用性 语言边界,避免歧义
|
|
15
|
+ - 让模型持续发展 持续致力与知识提炼
|
|
16
|
+
|
|
17
|
+> 通用语言
|
|
18
|
+- 抛开DDD 想到通用语言首先想到英语 特点:1)简单易学 2)使用效率高 3)国际通用
|
|
19
|
+- 概念:通过团队交流达成共识的能够准确传递业务规则的简单的语言
|
|
20
|
+- 价值 解决了交流障碍问题,使领域专家和开发人员协同合作
|
|
21
|
+
|
|
22
|
+> 领域(重在范围的界限)
|
|
23
|
+- 本质 :可以理解为 就是一个问题域,只要是同一个领域,那问题域就相同。所以,只要我们确定了系统所属的领域,那这个系统的核心业务,既要解决的关键问题、问题的范围边界就基本确定了。
|
|
24
|
+- 与传统开发过程的区别在于 DDD注重领域建模
|
|
25
|
+- 领域拆分: 子域、核心域、通用子域、支撑子域
|
|
26
|
+- 举个例子(比如创建个电商网站 需要涉及的业务。商品、用户、订单、报价、支付、物流、保修相关业务)
|
|
27
|
+ - 大致拆分:商品子域、用户子域、销售子域、订单子域、支付子域、物流子域、维修子域
|
|
28
|
+ - 确定核心域:开发电商网站的目的是为了寻求推广和销售利润的最大化。所以核心域就是销售子域。
|
|
29
|
+ - 通用子域:服务服务于整个业务领域。-》日志子域 可以为该网站提供一个日志系统,来记录一些日志。我们可以设计一个日志子域来供其他子域使用。
|
|
30
|
+ - 支撑子域:作用于业务系统的某些重要业务而非核心业务,它关注于业务的某一方面,来支撑完善业务系统。
|
|
31
|
+ - 我们划分的子域中除了销售子域,其他都可以说是支撑子域。
|
|
32
|
+ - 比如物流子域,就是专注于物流相关业务,支撑着订单发货以及物流跟踪的重要流程。
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+> 界限上下文
|
|
36
|
+- 可拆分为两个词,界限和上下文。 界限是指一个界限,具体的某一个范围。 上下文可理解为语境。
|
|
37
|
+- 例子 还是上述(电子商务)例子
|
|
38
|
+ - 在销售子域和商品子域中就是商品
|
|
39
|
+ - 在物流子域中特质货物,只关注部分属性
|
|
40
|
+- 命名 领域名+上下文 销售上下文 物流上下文
|
|
41
|
+
|
|
42
|
+> 领域模型
|
|
43
|
+- 概念:描述正在解决的问题及提出的解决方案
|
|
44
|
+- 特征:
|
|
45
|
+ - 综合了系统分析和设计
|
|
46
|
+ - 语言、模型、代码 三者紧密绑定
|
|
47
|
+ - 可以通过UML类图来展示
|
|
48
|
+- 例子 在线商城案例
|
|
49
|
+```
|
|
50
|
+Customer
|
|
51
|
+public class Customer
|
|
52
|
+{
|
|
53
|
+ public int Id{get;set;}
|
|
54
|
+ public string Name{get;set;}
|
|
55
|
+ public string BillingAddress{get;set;}
|
|
56
|
+ public string DeliveryAddress{get;set;}
|
|
57
|
+ Public string LoginName{get;set;}
|
|
58
|
+ public string LoginPassword{get;set;}
|
|
59
|
+ public string DayOfBirth{get;set;}
|
|
60
|
+}
|
|
61
|
+order
|
|
62
|
+public class Order
|
|
63
|
+{
|
|
64
|
+ public int Id{get;set;}
|
|
65
|
+ public string CreatedDate{get;set;}
|
|
66
|
+ public string CreatBy{get;set;}
|
|
67
|
+}
|
|
68
|
+OrderLine
|
|
69
|
+public class OrderLine
|
|
70
|
+{
|
|
71
|
+ public int Id{get;set;}
|
|
72
|
+ public string Quantity{get;set;}
|
|
73
|
+ public string Discount{get;set;}
|
|
74
|
+}
|
|
75
|
+Category
|
|
76
|
+public class Category
|
|
77
|
+{
|
|
78
|
+ public int Id{get;set;}
|
|
79
|
+ public string Name{get;set;}
|
|
80
|
+ public string Description{get;set;}
|
|
81
|
+}
|
|
82
|
+CreditCard
|
|
83
|
+public class CreditCart
|
|
84
|
+{
|
|
85
|
+ public int Id{get;set;}
|
|
86
|
+ public string Number{get;set;}
|
|
87
|
+ public string HolderName{get;set;}
|
|
88
|
+ public string ExpirationDate{get;set;}
|
|
89
|
+}
|
|
90
|
+Item
|
|
91
|
+public class Item
|
|
92
|
+{
|
|
93
|
+ public int Id{get;set;}
|
|
94
|
+ public string Name{get;set;}
|
|
95
|
+ public string Description{get;set;}
|
|
96
|
+ public string PurchasePrice{get;set;}
|
|
97
|
+ public string SalesPrice{get;set;}
|
|
98
|
+
|
|
99
|
+}
|
|
100
|
+```
|
|
101
|
+
|
|
102
|
+> 实体 (Entity)
|
|
103
|
+- 概念: 唯一身份标识+可变性[状态(属性)+行为(方法或领域事件或领域服务)]
|
|
104
|
+- 唯一标识: 针对上述例子 抽象出User实体 要定义其唯一标识。
|
|
105
|
+ - 性格、外貌、昵称、身份证号都可以是User的属性,但为了确保标识的稳定性,智能用身份证号设为唯一身份标识。
|
|
106
|
+ - 唯一标识的类型
|
|
107
|
+ - 一个简单的应用程序里,一个int类型的自增Id就可以作为唯一标识。优点就是占用空间小,查询速度快。
|
|
108
|
+ - 而在一些业务当中,要求唯一标识有意义,通过唯一标识就能识别出一些基本信息,比如支付宝的交易号,其中就包含了日期和用户ID。这种就属于字符串类型的标识,这就对唯一标识的生成提出了挑战。
|
|
109
|
+
|
|
110
|
+ - 唯一标识的生成时机
|
|
111
|
+ - 即时生成,即在持久化实体之前,先申请唯一标识,再更新到数据库。
|
|
112
|
+ - 延迟生成,即在持久化实体之后。
|
|
113
|
+ - 委派标识和领域标识
|
|
114
|
+ - 基于领域实体概念分析确定的唯一身份标识,我们可以称为领域实体标识
|
|
115
|
+ - 实现层超类型
|
|
116
|
+ - 定义层超类型接口
|
|
117
|
+ ```
|
|
118
|
+ public interface IEntity
|
|
119
|
+ {
|
|
120
|
+
|
|
121
|
+ }
|
|
122
|
+ public interface IEntity<TPrimaryKey> : IEntity
|
|
123
|
+ {
|
|
124
|
+ TPrimaryKey Id { get; set; }
|
|
125
|
+ }
|
|
126
|
+
|
|
127
|
+ ```
|
|
128
|
+ - 实现层超类型
|
|
129
|
+ ```
|
|
130
|
+ public class Entity : Entity<int>, IEntity
|
|
131
|
+ {
|
|
132
|
+
|
|
133
|
+ }
|
|
134
|
+ public class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
|
|
135
|
+ {
|
|
136
|
+ public virtual TPrimaryKey Id { get; set; }
|
|
137
|
+ public override bool Equals(object obj)
|
|
138
|
+ {
|
|
139
|
+ if (obj == null || !(obj is Entity<TPrimaryKey>))
|
|
140
|
+ {
|
|
141
|
+ return false;
|
|
142
|
+ }
|
|
143
|
+ if (ReferenceEquals(this, obj))
|
|
144
|
+ {
|
|
145
|
+ return true;
|
|
146
|
+ }
|
|
147
|
+ var other = (Entity<TPrimaryKey>) obj;
|
|
148
|
+ var typeOfThis = GetType();
|
|
149
|
+ var typeOfOther = other.GetType();
|
|
150
|
+ if (!typeOfThis.GetTypeInfo().IsAssignableFrom(typeOfOther) && !typeOfOther.GetTypeInfo().IsAssignableFrom(typeOfThis))
|
|
151
|
+ {
|
|
152
|
+ return false;
|
|
153
|
+ }
|
|
154
|
+ return Id.Equals(other.Id);
|
|
155
|
+ }
|
|
156
|
+ public override int GetHashCode()
|
|
157
|
+ {
|
|
158
|
+ return Id.GetHashCode();
|
|
159
|
+ }
|
|
160
|
+ public static bool operator ==(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right)
|
|
161
|
+ {
|
|
162
|
+ if (Equals(left, null))
|
|
163
|
+ {
|
|
164
|
+ return Equals(right, null);
|
|
165
|
+ }
|
|
166
|
+ return left.Equals(right);
|
|
167
|
+ }
|
|
168
|
+ public static bool operator !=(Entity<TPrimaryKey> left, Entity<TPrimaryKey> right)
|
|
169
|
+ {
|
|
170
|
+ return !(left == right);
|
|
171
|
+ }
|
|
172
|
+ }
|
|
173
|
+ ```
|
|
174
|
+ - 可变性
|
|
175
|
+ - 实体的状态 eg:订单状态:未支付、正常、已发货、关闭
|
|
176
|
+ - 实体的行为 eg:订单行为:支付、发货、关闭
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+> 值对象
|
|
180
|
+- 概念:值+对象=》将一个值用对象的方式进行标书,表达一个具体的固定不变的概念
|
|
181
|
+- 值特征:
|
|
182
|
+ - 表示一个具体的概念
|
|
183
|
+ - 通过值得属性对其识别
|
|
184
|
+ - 属性判断
|
|
185
|
+ - 固定不变
|
|
186
|
+- 案例分析(购物网站都会维护客户收货地址信息来进行发货处理,一个地址信息一般主要包含省份、城市、区县、街道、邮政编码信息。)
|
|
187
|
+ - 地址是一个值,不会随着时间而变化,它包含了地址所需要的完整属性(省份、城市、区县、街道、邮政编码)
|
|
188
|
+ ```
|
|
189
|
+ public class Address
|
|
190
|
+ {
|
|
191
|
+ /// <summary>
|
|
192
|
+ /// 省份
|
|
193
|
+ /// </summary>
|
|
194
|
+ public string Province { get; private set; }
|
|
195
|
+
|
|
196
|
+ /// <summary>
|
|
197
|
+ /// 城市
|
|
198
|
+ /// </summary>
|
|
199
|
+ public string City { get; private set; }
|
|
200
|
+
|
|
201
|
+ /// <summary>
|
|
202
|
+ /// 区县
|
|
203
|
+ /// </summary>
|
|
204
|
+ public string County { get; private set; }
|
|
205
|
+
|
|
206
|
+ /// <summary>
|
|
207
|
+ /// 街道
|
|
208
|
+ /// </summary>
|
|
209
|
+ public string Street { get; private set; }
|
|
210
|
+
|
|
211
|
+ /// <summary>
|
|
212
|
+ /// 邮政编码
|
|
213
|
+ /// </summary>
|
|
214
|
+ public string Zip { get; private set; }
|
|
215
|
+
|
|
216
|
+ public Address(string province, string city,
|
|
217
|
+ string county, string street, string zip)
|
|
218
|
+ {
|
|
219
|
+ this.Province = province;
|
|
220
|
+ this.City = city;
|
|
221
|
+ this.County = county;
|
|
222
|
+ this.Street = street;
|
|
223
|
+ this.Zip = zip;
|
|
224
|
+ }
|
|
225
|
+
|
|
226
|
+ public override bool Equals(object obj)
|
|
227
|
+ {
|
|
228
|
+ bool isEqual = false;
|
|
229
|
+ if (obj != null && this.GetType() == obj.GetType())
|
|
230
|
+ {
|
|
231
|
+ var that = obj as Address;
|
|
232
|
+ isEqual = this.Province == that.Province
|
|
233
|
+ && this.City == that.City
|
|
234
|
+ && this.County == that.County
|
|
235
|
+ && this.Street == that.Street
|
|
236
|
+ && this.Zip == that.Zip;
|
|
237
|
+ }
|
|
238
|
+ return IsEqual;
|
|
239
|
+ }
|
|
240
|
+ public override int GetHashCode()
|
|
241
|
+ {
|
|
242
|
+ return this.ToString().GetHashCode();
|
|
243
|
+ }
|
|
244
|
+ public override string ToString()
|
|
245
|
+ {
|
|
246
|
+ string address = $"{this.Province}{this.City}" +
|
|
247
|
+ $"{this.County}{this.Street}({this.Zip})";
|
|
248
|
+ return address;
|
|
249
|
+
|
|
250
|
+ }
|
|
251
|
+ }
|
|
252
|
+ ```
|
|
253
|
+ - 作用
|
|
254
|
+ - 符合通用语言,更简单明了的表达简单业务概念
|
|
255
|
+ - 简化设计,减少不必要的数据库表设计
|
|
256
|
+ - 值对象不会孤立存在,可作为所属实体的数据列
|
|
257
|
+ - 多个值对象序可以劣化到单个列
|
|
258
|
+
|
|
259
|
+
|