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,如需转载请自行联系原作者

相关文章
|
存储 分布式计算 监控
什么是 Hadoop 集群?
【8月更文挑战第12天】
594 4
|
小程序 前端开发 JavaScript
微信小程序结合PWA技术,提供离线访问、后台运行、桌面图标及原生体验,增强应用性能与用户交互。
微信小程序结合PWA技术,提供离线访问、后台运行、桌面图标及原生体验,增强应用性能与用户交互。开发者运用Service Worker等实现资源缓存与实时推送,利用Web App Manifest添加快捷方式至桌面,通过CSS3和JavaScript打造流畅动画与手势操作,需注意兼容性与性能优化,为用户创造更佳体验。
530 0
|
关系型数据库 Shell 数据库
[postgres]启用归档模式
[postgres]启用归档模式
310 0
|
存储 弹性计算 对象存储
ECS快照原理
云盘快照原理包括全量和增量快照。首次快照为全量,备份所有数据块;后续快照仅备份变化部分。快照存储在OSS中,同城冗余或本地冗余根据地域不同而定。删除快照时,按数据块引用关系释放空间。快照容量基于快照链统计,全量快照加所有增量大小。快照不占用云盘空间,但产生存储费用。
|
监控 安全 前端开发
前端安全:XSS攻击与防御策略
抵御XSS攻击的关键策略包括输入验证、输出编码、设置安全HTTP头如CSP和X-XSS-Protection、谨慎管理存储和会话、使用DOMPurify等库进行数据清理、采用安全编码实践、教育用户和开发人员、实施多层防御、持续测试和更新。其他措施如使用非渲染模板引擎、限制错误信息、使用WAF、加密数据、遵守安全编码标准和进行安全审计也是重要步骤。
842 0
|
缓存 资源调度 算法
yarn的安装和使用(全网最详细)
yarn的安装和使用(全网最详细)
12573 1
|
数据库 测试技术 Java
阿里技术专家详解DDD系列 第二弹 - 应用架构
应用架构,指软件系统中固定不变的代码结构、设计模式、规范和组件间的通信方式。在应用开发中架构之所以是最重要的第一步,因为一个好的架构能让系统安全、稳定、快速迭代。但是今天我们在做业务研发时,更多会关注一些宏观的架构,而忽略了应用内部的架构设计,希望能通过案例分析和重构,推演出一套高质量的DDD架构。
57643 24
阿里技术专家详解DDD系列 第二弹 - 应用架构
|
新零售 人工智能 应用服务中间件
flowable工作流选型对比
flowable工作流选型对比
883 0
flowable工作流选型对比
|
Linux Shell 索引
[ linux ] 文件系统和目录结构详解
昨天,有个小学弟了我一个linux面试题目,和她解答完之后我就想在C站开一个专栏,用于linux和windows的学习 我是这么想的,从linux入手,再写windows,最后总结常见区别 本文主要写了linux的文件结构介绍、linux文件系统的介绍、linux文件系统的基本操作,快捷键,通配符等等 下篇文章会写都文件的具体操作命令
687 0
[ linux ] 文件系统和目录结构详解