mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-11-17 14:43:43 +08:00
fix img
This commit is contained in:
@@ -560,16 +560,16 @@ function hide_canvas() {
|
||||
<p>业务需求希望系统能够根据员工的信息自动生成简历(Resume),这似乎预示着需要定义 Resume 领域类;然而,由于简历内容来自员工,该需求的真实目的是生成一份简历文档,属于一个无状态的领域行为,并不需要定义对应的领域类。</p>
|
||||
<p>工作日志(WorkLog)由日报(DailyReport)与项目日志(ProjectLog)组成。每一份工作日志都由多个日志项(LogItem)组成。工作日志需要定义日志状态(WorkLogStatus),用以区分草稿(Draft)日志和正式日志。</p>
|
||||
<p>由此,可以得到初步的领域分析模型:</p>
|
||||
<p><img src="assets/e4c74ad0-30a3-11ea-a38f-6fc1d57c5e01" alt="72635021.png" /></p>
|
||||
<p><img src="assets/e4c74ad0-30a3-11ea-a38f-6fc1d57c5e01" alt="png" /></p>
|
||||
<h4>领域设计模型</h4>
|
||||
<p>在获得领域分析模型后,可以按照聚合设计的庖丁解牛过程对领域分析模型进行细化。首先,需要理顺对象图,即进一步明确类之间的关系,同时识别模型中的实体与值对象。</p>
|
||||
<p>员工需要项目经验、技能与语言技能,但这些信息并非员工必须的属性,故而它们之间存在 OO 聚合(Aggregation)关系。员工和合同之间存在普通的依赖关系。工作日志与日报、项目日志之间并非组合关系,而是代表“is”的继承关系,即日报与项目日志都是一种工作日志,可将 WorkLog 定义为父类,由 DailyReport 与 ProjectLog 继承它。WorkLog 父类定义了共同的属性 WorkLogStatus,用以区分草稿日志和正式日志。由于项目日志的内容来自项目上下文的任务分配,并非员工自行填写,因此项目日志的状态只能是正式日志,它的日志项内容也有别于日报。这是它们之间存在的差异。</p>
|
||||
<p>毫无疑问,Employee、Contract 与 DailyReport、ProjectLog 都是实体。每个日志项也应该定义为实体,因为两个内容完全相同的日志项,只要其身份标识不同,也应该认为是不同的日志项。 ProjectExperience 与 Project 似乎应定义为实体,但是在员工上下文,如果两个项目的值完全相同,就可以认为是同一个对象,故而应定义为值对象。理顺对象图后的领域模型如下所示,其中黄色代表实体、蓝色代表值对象:</p>
|
||||
<p><img src="assets/efb5e820-30a3-11ea-a7c5-53b775928f13" alt="73573723.png" /></p>
|
||||
<p><img src="assets/efb5e820-30a3-11ea-a7c5-53b775928f13" alt="png" /></p>
|
||||
<p>接下来,分解关系薄弱处,从而划定聚合边界。虽然 Employee 与 ProjectExperience 等属性都是 OO 的聚合关系,但由于 ProjectExperience 等皆被定义为值对象,不可能拆分到单独的 DDD 聚合中,应定义在 Employee 聚合边界内。由此可获得初步的聚合边界:</p>
|
||||
<p><img src="assets/06c16990-30a4-11ea-8f5c-09cd511348a5" alt="74091424.png" /></p>
|
||||
<p><img src="assets/06c16990-30a4-11ea-8f5c-09cd511348a5" alt="png" /></p>
|
||||
<p>最后,需要遵循聚合设计的原则调整聚合的边界,这需要考察概念的完整性和独立性、业务规则的不变性以及数据的一致性。由于员工聚合边界内的类除了 Employee 是实体外,其余皆为值对象,因此不用调整员工聚合边界。DailyReport 与 ProjectLog 虽然都继承了 WorkLog 父类,但是它们之间的领域行为和属性存在一定差异,彼此之间也不存在概念完整性与不变性,因此应该将其定义为两个独立的聚合。调整后的聚合设计为:</p>
|
||||
<p><img src="assets/19ec5570-30a4-11ea-8988-21b4b8a88b82" alt="74565046.png" /></p>
|
||||
<p><img src="assets/19ec5570-30a4-11ea-8988-21b4b8a88b82" alt="png" /></p>
|
||||
<p>四个聚合的根实体分别为 Employee、Contract、DailyReport 与 ProjectLog,以 <code><<AR>></code> 缩写标识聚合根(Aggregate Root)。</p>
|
||||
<p>类图表达的领域设计模型仍有不足之处,因为它仅体现了类与类之间的静态关系。通过场景驱动设计,对具有业务价值的领域场景进行任务分解,再针对角色构造型分配职责,可以获得该场景对应的时序图。时序图可以更好地体现类之间的动态协作关系,并由此获得领域服务。</p>
|
||||
<p>以“员工入职”领域场景为例,分解的任务如下所示:</p>
|
||||
@@ -605,7 +605,7 @@ function hide_canvas() {
|
||||
}
|
||||
</code></pre>
|
||||
<p>请求消息对象与装配器对象皆未定义在角色构造型中。它们属于应用层,但并非应用服务。这算是<strong>场景驱动设计的一种特例</strong>,即原子任务的执行并非由领域服务发起。该领域场景的协作过程如时序图所示:</p>
|
||||
<p><img src="assets/3db45b40-36d7-11ea-bbe7-b11653d02f36" alt="33294705.png" /></p>
|
||||
<p><img src="assets/3db45b40-36d7-11ea-bbe7-b11653d02f36" alt="png" /></p>
|
||||
<p>显然,在思考领域场景的执行流程时,编写时序图脚本或者绘制时序图有利于我们想清楚任务实现的细节,弄明白职责分配的合理性与类之间的正确协作方式。任务分解是静态的,时序图却是动态的,它能驱动设计人员寻找到更好的协作方式。</p>
|
||||
<p><strong>说明</strong>:文中的每个领域场景都会在 <a href="https://github.com/agiledon/eas-ddd/issues">Github 项目的 Issue</a> 中列出,其中领域场景对应 Story 类型的 Issue,每个主要的任务对应 Task 类型的 Issue。在提交组成领域场景实现模型的代码时,每次提交的 Comment 都会标记对应 Issue 的编号。</p>
|
||||
<h4>领域实现模型</h4>
|
||||
|
||||
Reference in New Issue
Block a user