前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DDD 中的几个困难问题

DDD 中的几个困难问题

作者头像
ThoughtWorks
发布2022-07-18 15:43:55
3910
发布2022-07-18 15:43:55
举报
文章被收录于专栏:ThoughtWorks

领域到底是什么?

对领域这个词的理解就是 DDD 入门的第一个难关。我们有时会被客户问到,领域到底是什么?首先要清晰地知道领域是什么,才能划分核心域、支撑域和通用域。换句话说,构成领域的要素是什么呢?

领域是一个非常抽象的词汇,我们需要先对其具象化。在英语的语境中,“Domain” 其实就是业务,指的是现实生活中的各种事务。处理税务、记账、售货记录等,这些都是领域。

于是,我们给领域下了一个定义:

领域(Domain)是业务相关知识的集合

通俗来说,领域就是业务知识。业务有一些内在规则,存在专业性,比如财务、CRM、OA、电商等不同领域的业务规则不同。计算机只是业务规则的自动化。更加具体来说,构成领域的要素就是特定的业务场景。

通过对业务的场景划分,再对其分类,就是我们的子域。

核心域:那些对业务极其重要的场景,内容社区应用,就是提问、看帖、回复。

支撑域:那些对重要业务支持的场景,比如登录、找回密码等场景。

通用域:那些已经成为相对独立的支撑业务场景,比如实名验证、人机校验(以前是支撑,现在可以是通用)。

领域和上下文是什么关系?

如果领域的构成要件是场景,上下文的构成要件是模型,那么领域和上下文之间就没有包含和被包含关系。

也不存在一个领域是否对应多个上下文的关系。

他们构成:上下文支撑领域的关系,领域导出上下文的关系。

DDD 软件建模就是业务问题和解决方案之间的桥梁。领域是问题,设计出来的模型是解的一部分。因此,问题和解形如 x 和 f(x) 的关系,f = 软件建模过程。

举个例子来说,某个电商网站有多个渠道,零售、批发、企业采购等多个场景的业务,这是他们的领域。对于研发工程师来说,他们会最终设计出订单、商品等模型上下文,来支持这些领域。

聚合如何持久化?

聚合被赋予了两个责任:

  1. 负责业务的一致性。
  2. 负责数据的整体存储。

而其持久化是一个老大难问题。

关于业务的一致性,Eric DDD 给我们描述了一种理想情景。只要把业务一致的一组模型从数据库中统一获取到,对其做业务修改,然后再持久化回去,就可以避免业务的一致性被破坏。

业务的一致性可以这样理解。我们有订单和订单项,订单的总价由订单项计算得来。如果不长眼的程序员把订单项直接修改了,而不更新订单,就会带来 bug。

但是,遗憾的是我们的内存不是无限大,而且数据会在断电后丢失。我们必须把数据从磁盘中读取出来,而磁盘的访问速度很慢。数据在磁盘中的组织形式使用了集合+关联的方式存放,这是由于我们为了降低数据冗余和方便查询而不得已为之。这就是关系模型和对象模型的差异,而不得不采用一些技术方法转换(ORM)。

而数据的整体存储,让聚合的持久化变得困难和性能低下。

一个简单的道理是,我们只需要一个橘子,却总想把橘子树搬来搬去,虽然摘橘子需要通过橘子树。

充血模型为什么不符合编程习惯?

充血模型已经是很多 DDD 实践者的潜在认知,简单来说就是把业务行为放到模型中。

这种做法看似满足了面向对象的实践,但是在实际工作中,它并不方便,甚至有些别扭。在培训中,有学员找我们说,学了 DDD 之后不会写代码了,甚至忘记之前的代码该如何编写。

极端一点的例子,还会有人在聚合根中调用仓储来实现聚合的存储。这时,他们发现矛盾在于 JPA 的存储需要使用实体的类型信息,这时候便束手无策了。

在辩证唯物主义认识论中,一个行为构成的要件是:主体 + 动词 + 客体。

在英语学习中,主谓宾结构的主体是主语,客体就是宾语。甚至,主系表结构也满足这个道理。主语是主体,表语是主体的属性,也是客体。

“太阳是圆的”。指的是,太阳的形状是圆的。太阳是主体,“是” 作为逻辑谓词可以认为是动词,“圆的”是太阳的外观属性。

合适的充血模型是给 “主体”充血,给客体贫血。特殊的情况是,当一个模型操作它的属性的时候,它也可以是主体。因此,给领域模型的操作能力,应该仅限于操作自己的属性。而领域模型的构建、业务处理、持久化应该交给主体来做。

一个有意思的悖论就是,不合适的充血模型就像让一张桌子让它自己把自己搬到楼上去,我们难描述这种行为。更好的做法不是去找一个搬运工去搬这个桌子么,这次行为的主体就是搬运工,客体就是这个桌子。

如何清晰的分层?

分层有两个原则:

  1. 分层是有明确目的,没有目的的分层会带来额外的问题。
  2. 分层需要考虑框架、库的实现,否则容易带了 “千层饼架构”。

分层的目的为了隔离差异,没有差异而进行的分层就是浪费。由于差异的出现,每层所对应的客体就发生了变化。

  1. 接入层:处理接入协议,这个时候还不知道领域信息,客体就是数据包的不同形式。
  2. 应用层:处理业务场景,比如用户注册、添加用户、导入用户等,客体就是一些用例对象。
  3. 领域层:处理通用领域能力,比如创建用户,客体主要就是领域模型。
  4. 技术设施层:为上层提供技术实现,并不知道领域层的信息。比如 JPA 是一种持久化实现,需要从领域层输入对象的类型信息和数据信息,客体就是泛型对象。

多对多关系一般怎么处理?

多对多就是客体的含混不清,迷失了中间模型。

一个订单可以有多个商品,实际上是一个订单有多个订单项。

  1. 一个用户可以加入多个文档协作编辑,实际上是一个用户可以成为多个文档的参与人。
  2. 一个用户可以加入多个用户组,实际上是一个用户可以在多个用户组成为成员。

辨明客体,可以让代码变得清晰、简单、解耦。在现实中,一个老板可以有多个公司,一个公司也可以由多个老板投资。他们之间的多对多关系是通过 “股东” 这个客体来承载的。

在有限责任的公司中,股东身份和老板的个人身份(自然人)相互独立,并得到司法支持。

参考资料

  • 《DDD 概念参考》

- 相关阅读 -

遗留系统的服务拆分

Thoughtworks 全球CTO:按需求构建架构,过度工程只会“劳民伤财”

点击【阅读原文】可至洞见网站查看原文&加粗字体部分的相关链接。

本文版权属Thoughtworks公司所有,如需转载请在后台留言联系。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ThoughtWorks洞见 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
访问管理
访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档