作者|林宁是什么领域?词域的理解是DDD入门的第一个难点。我们有时会被客户问到,这个领域到底是什么?首先,我们必须清楚地知道域是什么,然后才能划分核心域、支撑域和通用域。换句话说,构成域的元素是什么?领域是一个非常抽象的词汇,我们需要先将其形象化。在英语语境中,“Domain”其实就是business,指的是现实生活中的各种事务。处理税收、簿记、销售记录等,这些都是领域。因此,我们给领域下一个定义:领域(Domain)是业务相关知识的集合。通俗地说,领域就是业务知识。业务有一些内在的规则,这些规则是专门的,比如金融、CRM、OA、电子商务等领域都有不同的业务规则。计算机只是业务规则的自动化。更具体地说,构成域的元素是特定的业务场景。通过划分业务场景,然后分类,它们就是我们的子域。核心领域:那些对业务极其重要的场景,内容社区应用,即提问、看帖、回复。支持域:那些支持重要业务的场景,比如登录、找回密码等场景。通用域:已经相对独立的配套业务场景,如实名认证、人机认证(以前支持,现在可以通用)。域和上下文之间的关系是什么?如果域的构成元素是场景,上下文的构成元素是模型,则域和上下文之间不存在包含和包含关系。一个字段是否对应多个上下文也没有关系。它们构成:上下文支持领域的关系,领域衍生出上下文的关系。DDD软件建模是业务问题和解决方案之间的桥梁。领域就是问题,设计的模型是解决方案的一部分。因此,问题和解决方案的形式为x和f(x),其中f=软件建模过程。例如,电子商务网站有多个渠道,业务涉及零售、批发、企业采购等多个场景。这是他们的领域。对于研发工程师来说,他们最终会设计订单、商品等模型上下文来支持这些领域。聚合如何持久化?聚合被赋予两个职责:负责业务一致性。负责数据的整体存储。而它的持久性是一个长期存在的问题。关于业务一致性,EricDDD为我们描述了一个理想的场景。只要从数据库中统一获取一组业务一致的模型,对其进行业务修改,然后持久化回来,就可以避免业??务一致性。业务一致性可以这样理解。我们有订单和订单项,订单的总价是根据订单项计算的。如果不美观的程序员直接修改订单项而不更新订单,会带来bug。然而,不幸的是我们的内存不是无限的,断电后数据就会丢失。我们必须从磁盘读取数据,而磁盘访问速度很慢。数据在磁盘中的组织形式是以集合+关联的方式存储的,这是为了减少数据冗余,方便查询,我们不得不这样做。这就是关系模型和对象模型的区别,不得不采用一些技术方法转换(ORM)。数据的整体存储使得聚合持久化困难,性能低下。一个简单的道理,我们只需要一个橙子,但我们总想把橙树挪一挪,虽然摘橙子需要穿过橙树。为什么拥塞模型不符合编程习惯?充血模型已经是很多DDD从业者的潜在认知。简单来说,就是将业务行为放入模型中。这种方式看似满足了面向对象的实践,但在实际工作中,并不方便,甚至有些别扭。培训的时候,有同学来找我们说,学了DDD以后,就不会再写代码了,甚至忘记了以前的代码怎么写。极端的例子,有人会调用聚合根中的存储来实现聚合存储。这个时候他们发现矛盾的地方在于JPA的存储需要用到实体的类型信息,这时候他们也束手无策了。在辩证唯物主义的认识论中,动作的要素是:主语+动词+宾语。在英语学习中,主谓宾结构的主语是主语,宾语是宾语。即使是主表结构也满足这个原则。主语是主语,谓语是主语的定语,也是宾语。“太阳是圆的”。也就是说,太阳的形状是圆的。太阳是主语,“是”作为逻辑谓语可以认为是动词,“圆”是太阳的表象定语。合适的充血模型是“主体”充血和客体贫血。特殊情况是,当模型对其属性进行操作时,它也可以是主题。因此,赋予领域模型的操作能力应该仅限于操作自身的属性。领域模型的构建、业务处理、持久化等,应该交给主题。一个有趣的悖论是,不合适的充血模型就像让桌子自己移动到楼上,这是一种难以描述的行为。找个搬运工搬桌子不是更好吗?这种行为的主体是搬运工,客体是餐桌。如何层次分明?分层有两个原则:分层有明确的目的,没有目的的分层会带来额外的问题。分层需要考虑框架和库的实现,否则容易带来“千层蛋糕架构”。分层的目的是隔离差异,没有差异的分层是一种浪费。由于差异的出现,每一层对应的对象都发生了变化。访问层:处理访问协议。此时域信息还未知,对象是不同形式的数据包。应用层:处理业务场景,如用户注册、添加用户、导入用户等。对象是一些用例对象。领域层:处理通用的领域能力,比如创建用户,主要对象是领域模型。技术设施层:在不知道领域层信息的情况下,为上层提供技术实现。比如JPA是一个持久化实现,需要从领域层输入对象类型信息和数据信息,对象是泛型对象。如何处理多对多关系?多对多是对象的二义性,中间模型丢失。一个订单可以有多个项目,实际上一个订单可以有多个行项目。一个用户可以加入多个文档进行协同编辑,实际上一个用户可以成为多个文档的参与者。一个用户可以加入多个用户组,实际上一个用户可以是多个用户组的成员。识别对象可以让代码清晰、简洁、解耦。现实中,一个老板可以拥有多个公司,一个公司也可以被多个老板投资。它们之间的多对多关系由对象“股东”承载。在有限责任公司中,股东的身份与老板(自然人)的个人身份相互独立,并得到司法机关的支持。
