关于ddd:04期领域驱动设计与微服务

39次阅读

共计 3185 个字符,预计需要花费 8 分钟才能阅读完成。

这里记录的是学习分享内容,文章保护在 Github:studeyang/leanrning-share。

如何了解畛域驱动设计?

随着微服务的衰亡,你肯定据说过畛域驱动设计 DDD(domain-driven design),然而如果把它当成一个术语来看,仿佛有点形象。这到底是个什么玩意?

别急,你必定还据说过测试驱动开发(TDD, Test-driven development)吧?

这是个什么概念呢?就是说开发的过程中要测试后行,提倡先写测试程序,而后编码实现。开发是目标,测试是辅助,所以叫做测试 - 驱动 - 开发,咱们应该把它拆成 3 个术语来了解。

所以,对于畛域驱动设计,设计是目标,畛域才是辅助。想要设计一个软件,然而因为业务太过简单,设计过程难以进行。这时,应用畛域的思维来辅助设计。

微服务应该拆多小?

如果你是业务架构师,你在设计过程中会遇到哪些难题呢?我想你面临的第一个问题就是:微服务到底应该拆多小?

有人说:“微服务嘛,就是要越小越好!”

这时运维可能要跳进去打你了,微服务如果拆分适度,导致我的项目简单度过高,不仅运维保护这些服务消耗人力,太小的微服务也占用了资源。

那是否有适合的实践或设计办法来领导微服务设计呢?

答案就是 DDD。

DDD 是一种解决简单畛域的设计思维,包含两局部,策略设计和战术设计。策略设计就是辅助建设业务畛域模型,划分畛域边界,建设限界上下文(DDD 的专业术语,下文会解释)。

战术设计则从技术视角登程,侧重于畛域模型的技术实现,实现软件开发和落地,包含微服务代码架构模型的设计和实现。

DDD 思维是如何领导微服务拆分的呢?能够分为三步:

第一步,列举业务场景,找出畛域实体对象。

第二步,依据畛域实体间的业务关联,将相干的实体组合造成聚合。它们属于同一个微服务。

第三步,依据语义边界,将多个聚合划定在一个限界上下文内,造成畛域模型。这一层边界就是微服务的边界。

DDD 畛域的思维

在钻研简单畛域问题时,DDD 会按肯定的规定将业务畛域进行细分,这跟自然科学的钻研办法相似。

当人们在自然科学钻研中遇到简单问题时,通常的做法就是将问题按肯定的规定进行细分,再针对细分进去的问题子域一一深入研究,当所有问题子域实现钻研时,咱们就建设了全副畛域的残缺常识体系了。

举个例子:如果咱们要钻研一颗桃树。依照器官的不同分为营养器官和生殖器官,对营养器官进一步细分,分为叶,茎、根,对生殖器官进一步分为花、果实、种子。

对器官进一步细分,将器官分为组织。对组织进一步细分,将组织细分为细胞。细胞就是咱们要钻研的最小单元。细胞之间的细胞壁确定了单元的边界,也确定了钻研的最小边界。

子域

将桃树细分成了六个子域:根、茎、叶,花、果实、种子。子域再依照重要水平进行划分,分为外围域、通用域、撑持域。

决定产品和公司外围竞争力的子域是外围域;没有太多个性化的诉求,同时被多个子域应用的是通用域;既不蕴含决定产品和公司外围竞争力的性能,也不蕴含通用性能的子域,它就是撑持域。

须要留神的是,外围域要依据公司的倒退策略及业务的理论状况来确定。

举例来说,如果这颗桃树的客人是一名园丁,那他关注的就是桃花盛开,春色满园,所以花就是外围域。如果这颗桃树的客人是一名果农,那他关注的就是桃子品质、产量,所以果实就是外围域。

限界上下文

咱们晓得语言都有它的语义环境,为了防止同样的概念或语义在不同的上下文环境中产生歧义,DDD 在策略设计上提出了“限界上下文”这个概念,用来确定语义所在的畛域边界。

举个例子:下图中的两个账户,光凭名字咱们根本无法辨别,只有通过它们所在的限界上下文咱们能力看出它们之间的区别。

再比方,电商畛域的商品在不同的阶段有不同的术语,在销售阶段是商品,而在运输阶段则变成了货物。同样的一个货色,因为业务畛域的不同,赋予了这些术语不同的涵义和职责边界。

一个限界上下文就能够拆分为一个微服务,这个边界使得一个概念在这个边界内没有二义性。

实体

总结来说有四种状态。

第一,实体的业务状态:在策略设计时,畛域模型中的实体是多个属性、操作或行为的载体。

第二,实体的代码状态:在代码模型中,实体的表现形式是实体类,这个类蕴含了实体的属性和办法,以及外围业务逻辑。

DDD 强调“设计即代码”。对于“注射流感疫苗”这个业务用例,当团队探讨到业务模型时,他们会说:“护士给病人注射规范剂量的流感疫苗。”

传统代码的表现形式是这样的:

public void shot() {patient.setShotType(ShotTypes.TYPE_FLU);
    patient.setDose(dose);
    patient.setNurse(nurse);
}

DDD 思维的代码表现形式是:

public void shot() {Vaccine vaccine = vaccines.standardAdultFluDose();
    nurse.administerFluVaccine(patient, vaccine);
}

很显著,第二类代码更容易了解的多。

第三,实体的运行状态:实体以 DO(畛域对象)的模式存在,每个实体对象都有惟一的 ID。咱们能够对一个实体对象进行屡次批改,批改后的数据和原来的数据可能会大不相同。然而,因为它们领有雷同的 ID,它们仍然是同一个实体。

第四,实体的数据库状态:在畛域模型映射到数据模型时,大多数状况下实体与长久化对象是一对一。

值对象

值对象是 DDD 畛域模型中的一个根底对象,它跟实体一样,都蕴含了若干个属性,它与实体一起形成聚合。

  1. 值对象的业务状态。

实质上,实体是看失去、摸得着的实实在在的业务对象,实体具备业务属性、业务行为和业务逻辑。而值对象只是若干个属性的汇合。

  1. 值对象的代码状态。
public class Person {
    private Integer id;
    private String name;
    private Address address;
}

private class Address {
    private String province;
    private String city;
    private String county;
}

咱们看一下下面这段代码,Person 这个实体有若干个繁多属性的值对象,比方 id、name 等属性;同时它也蕴含多个属性的值对象,比方地址 address。

  1. 值对象的运行状态。

实体实例化后的 DO 对象的业务属性和业务行为十分丰盛,但值对象实例化的对象则绝对简略。

  1. 值对象的数据库状态。

在领域建模时,咱们能够将局部对象设计为值对象,保留对象的业务涵义,同时又缩小了实体的数量;在数据建模时,咱们能够将值对象嵌入实体,缩小实体表的数量,简化数据库设计。

有些场景中,地址会被某一实体援用,它只承当形容实体的作用,并且它的值只能整体替换,这时候你就能够将地址设计为值对象,比方收货地址。而在某些业务场景中,地址会被常常批改,地址是作为一个独立对象存在的,这时候它应该设计为实体,比方行政区划中的地址信息保护。

聚合和聚合根

举个例子。社会是由一个个的个体组成的,咱们每一个人就是一个个体。随着社会的倒退,缓缓呈现了社团、机构、部门等组织,咱们也从集体变成了组织的一员,在组织内,大家协同工作,朝着更大的指标,施展出更大的力量。

畛域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些畛域对象在实现独特的业务逻辑时,能保证数据的一致性。

如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者。

在聚合之间,通过聚合根 ID 关联援用,如果须要拜访其它聚合的实体,就要先拜访聚合根,再导航到聚合外部实体,内部对象不能间接拜访聚合内实体。

最初,我用下图来总结一下畛域、限界上下文、实体、值对象、聚合、聚合根。

封面

相干文章

兴许你对上面文章也感兴趣。

  • 学习分享(第 3 期):你所了解的架构是什么?

正文完
 0