DDD 应对具体业务场景,Domain Model 重新设计

简介:

  还有就是关于领域服务:

所谓服务,它强调与其他对象的联系。不像实体和值对象,服务完全是根据能够为客户做什么来定义的。服务往往代表一种行为,而不是一个实体,是一个动词而不是一个名词。服务可以有一个抽象的、有意图的定义,这与一个对象的定义有所不同。服务应该还有一个定义好的职责,它的职责和接口被定义为领域模型的一部分。操作名应该来自通用语言,如果通用语言中还没有这个操作名,则应该把它添加进去。调用的参数和返回的结果应该是领域对象。

  以上关于领域服务的概念来自《领域驱动设计-软件核心复杂性应对之道》,其实这一段描述只是这本书中的一点,还有很多精彩的讲解,朋友们感兴趣的话可以参考下,关于领域服务的描述,主要有几个注意点:

  • 强调与其他对象的管理;
  • 代表一种行为,是动词而非名词;
  • 职责和定义是领域模型的一部分;
  • 操作名来来自通用语言(比如发消息,那就可以定义为 SendMessage,谁都可以看得懂);
  • 调用的参数和返回结果应该是领域对象(比如实体)

  在领域驱动设计中,除了领域服务外,还包含基础层服务和应用层服务,有时候我们如果处理的不小心的话,容易把他们三个搞混掉,在领域驱动设计这本书中讲了一个转账事例,用来区分这三者之间的职责关系,如下:

  应用层-资金转账应用服务

  1. 读取输入(例如XML请求)
  2. 发送消息给领域服务,要求处理
  3. 监听确认消息
  4. 决定用基础结构层的服务发送通告

  领域层-资金转账领域服务

  1. 必要的账户和分类账对象的相互作用,完成正确的提取和存入
  2. 确认转账结果(转账是否被允许或拒绝等)

  基础结构层-发送通告服务

  1. 由应用选择通告方法,发送电子邮件、信件或者通过其他通信途径

  通过这个事例,我们可以很清晰的分辨出这三个服务所对应的职责:应用服务管流程;领域服务管业务;基础服务管后勤。在这个转账事例中,我们在设计领域服务的时候就可以设计为:FundsTransferService,代表的是资金转账服务,转账就是一个动词。可以想一想,如果按照我们之前的那种设计思想,肯定会在资金实体中,定义一个转账方法,用来表示资金对象可以转自己?还真是蛮可笑的。在设计领域服务的时候可以这样考虑,当一种业务逻辑,在实体中所不能表述的时候,或者表述的所不合理的时候,就要考虑一下领域服务设计的必要了。

  回到我们的消息领域模型,来看一下充血设计前后解决方案图:

                                        

  可以看到在设计之前,我对文件名的命名就有问题,而且 MessageState 是值对象,应该和实体是区分开来的,设计后,这三者组成才可以称之为领域模型,具体的实现代码如下。

  消息实体:

  View Code

  发送消息领域服务:

  View Code

  消息应用服务:

  View Code

  这边再简单描述下发送消息这个业务流程实现,其实看下应用层的代码就清楚了,首先,UI 发送一个发消息的请求给应用层(相当于系统),参数为:标题、内容、发送人登录名、收件人显示名,应用层服务接到请求之后,先根据发送人和收件人的名称去仓储中查找相对应的用户,如果用户不存在,直接越过下面的发送操作,如果用户存在,则创建一个消息对象,在消息实体的构造函数中去验证这些参数的规则(比如参数不为空、字符串长度限制等等),如果验证成功则创建消息对象成功,首先这这一方面的改进之处就是,把收件人的赋值操作放在这边了,发送消息这个业务逻辑的体现其实并不是简单的赋值操作,其实这种实现更符合实际生活,比如我写一封信给女朋友,写好标题、内容收件人和发件人之后,我并没有寄出,但是这封信已经存在了(符合信存在的标准),但是没有寄出,也就是说这个消息对象已经存在,只是现在这个对象的状态是未寄出,关于这一点,其实是和之前的设计是完全不同的,具体不同我也就不说了。

  换个行,要不然看着太费劲。我们接着说,消息对象创建成功之后(状态是未发),调用发送消息领域服务,进行业务规则验证(比如发送人不能和收件人相同,发送人一天之内不能发送超过100个的短消息等等),其实这才是真正的发送消息业务逻辑,正如领域服务所定义的那样,参数和返回值都是领域对象,也就是消息实体,发送验证成功后进入持久化或者基础服务发送邮箱,整个发送消息的工作流程就是这样。

  在上述发送消息工作流程描述中,需要注意的最重要的一点,就是消息状态的体现,也就是消息对象的未发状态和已发状态,这两个状态确定的前提这个消息对象是存在的,也就是创建成功的。在以前的设计中,如何体现这个发送状态的确定?答案就是收件人的赋值,之前认为,只有填写了收件人,那这个消息的状态就是已发送,其实这种逻辑有点天马星空。我现在个人感觉,消息对象的发送状态不能由它自身确定,也就是说不能由它自己的某一个属性确定,它应该是一个动态的过程,也就是在验证发送业务规则成功后,retrun 之后的那个消息对象,表示这个消息对象的状态是已发送的,因为它是符合发送消息业务规则并验证通过,那它的状态就是已发送。




本文转自田园里的蟋蟀博客园博客,原文链接:http://www.cnblogs.com/xishuai/p/3827216.html,如需转载请自行联系原作者

相关文章
|
4月前
|
存储 API 数据库
深入探索DDD与事件溯源:使用Entity Framework Core构建高效且可维护的领域驱动设计应用——从理论到实践的全方位指南,附带代码示例与最佳实践分享
【8月更文挑战第31天】本文通过实例介绍如何结合领域驱动设计(DDD)与事件溯源(Event Sourcing)及 Entity Framework Core(EF Core),构建高效且可维护的应用程序。DDD 强调业务逻辑与软件设计的紧密融合,而事件溯源则通过记录所有变更事件来重建状态。文章详细展示了创建基于 EF Core 的项目、配置数据库上下文、定义领域模型与事件,并存储和提交事件的具体步骤。通过这些技术,实现了复杂业务逻辑的持久化与重构,提高了应用程序的灵活性和扩展性。
115 0
|
5月前
业务系统架构实践问题之如何在biz层复用domain层的服务如何解决
业务系统架构实践问题之如何在biz层复用domain层的服务如何解决
|
5月前
|
项目管理
项目管理问题之Entity和BaseEntity在DDD的model层中起什么作用
项目管理问题之Entity和BaseEntity在DDD的model层中起什么作用
|
5月前
业务系统架构实践问题之代码应该主要放在biz层还是domain层
业务系统架构实践问题之代码应该主要放在biz层还是domain层
|
5月前
|
存储
业务系统架构实践问题之为什么按模型划域可能是比按领域划分更好的选择
业务系统架构实践问题之为什么按模型划域可能是比按领域划分更好的选择
|
5月前
|
存储 运维 数据库
业务系统架构实践问题之业务模型和存储模型解耦的重要性问题如何解决
业务系统架构实践问题之业务模型和存储模型解耦的重要性问题如何解决
|
消息中间件 前端开发 小程序
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
DDD实战之五:战略设计之上下文映射和系统分层架构(下)
|
设计模式 搜索推荐 领域建模
DDD领域驱动设计实战(一)-领域模型、子域、核心域、通用域和支撑域等基本概念
DDD领域驱动设计实战(一)-领域模型、子域、核心域、通用域和支撑域等基本概念
1045 0
|
前端开发 小程序 机器人
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
DDD实战之五:战略设计之上下文映射和系统分层架构(上)
|
JSON 关系型数据库 MySQL