[微服务与DDD]-DDD落地实践之旅第一篇

897 阅读8分钟

上章回顾:

从战略层面,战术层面分别讲解了如何实践DDD,DDD的核心诉求就是将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。

DDD实践之旅

前面的章节介绍了一大堆的理论知识,接下来我将带大家如何实践DDD,项目来自本人在2014到2019年的一次真实创业经历.

项目背景介绍

本项目是一个互联网金融偏互联网玩法的APP,通过为用户提供一个记录各类理财投资的工具,帮用户管理家庭资产,提供智能投顾服务. 首先投资相对记账来说,本身频次比较低,用户不会天天产生投资.相对记账关注的是收入和支出,投资更加关注受益率风险偏好等.

记账和投资的区别:

  1. 记账不管是收入和支出,都是固定的,投资是动态的.
  2. 记账记录的是流水,关注的是余额,投资记录的是买卖,关注的收益.
  3. 大多数记账主要以货币为主题,投资的主体更加丰富,可以是股票,基金,P2P,定存等等.
  4. 记账模型相对比较简单,投资模型比较复杂,比如基金涉及到分红等场景.

那么在业务场景下,记账和投资有什么区别呢?

  1. 记账主要和人有关,投资除了人,还有标的,账号等关联;
  2. 记账体现收支细节,投资侧重收益回报,风险偏好等;
  3. 记账大部分依赖人工输入,无法校验真实性,投资支持账户模式,能做到真实有效;
  4. 记账的实时要求不高,投资是有实效性
  5. 记账的计算比较简单,精度要求低,投资的计算比较复杂,计算精度要求高;

V1.0领域模型划分

image.png 在早期的MVP版本中,只是针对资产的三要素进行了简单的领域上下文划分 资产所有人 = 用户中心
资产标的 = 平台标的中心
资产金额 = 资产中心\

用户

APP上的传统用户概念,即时APP登录的用户,也是资产的所有人,每个资产只能属于一个创建人,但是可以被多个用户共享;

标的

资产的唯一标示,代表了某个具体的资产ID,定义了资产的单位价值.现实世界中,标的是不能独立存在的,一般都依赖于发行该资产的平台;

资产

此处代表了某个标的的份额,可以通过计算获得资产的总价值,资产总价值=资产份额*资产单位价值.

领域&&子域
领域指某一专业或事物方面范围的涵盖,一个领域是由一个或者多个子域构成的,当领域很复杂,很庞大时,需要讲领域细分为多个子域或者子子域 核心域&&通用域&&支撑域
领域按照功能可以划分为核心域,通用域,和支撑域;
核心域,指的是这个业务的核心功能,核心模块 通用域,指的是一些通用系统功能和模块,比如认证、权限,审计 支撑域,值得是一些不具有通用性的特殊功能,这些功能不属于核心功能,但是又必须存在.

image.png 从资产类图得出,

  1. 资产按照收益属性分为:正向资产(投资)和负向资产(债务)
  2. 资产按照添加方式分为:自动类资产和手动类资产 所以总体上资产会按照2类属性分属于不同的类型,从而在计算总资产余额或者风险偏好的时候,会根据不同种类进行计算.自动类资产主要是通过授权账号方式获取资产,这样的好处是资产一定是真实存在的.手动类资产大多数情况都是用户输入资产类的核心要素,无法对资产金额进行有效性校验.这类资产可能在进行资产风险偏好计算带来一定的误差性;

image.png

核心概念:

资产(Wealth): 表示投资某种有价值的标的金额; 必须满足资产的三要素:人,标的,金额
标的(Resource):表示平台发行的有价值的金融产品,比如某个基金,某个P2P产品;
账号(Account):平台用来识别用户的唯一标示,比如某个P2P账户,某个券商账户等;
平台(Platform): 表示发行某类有价值资产的平台,资产在平台内才有价值.比如P2P平台,发布理财产品的银行等\

其中资产是核心域的核心领域模型, 平台,账号,标的等为子领域模型.

  • 资产领域模型上下文,主要提供资产的管理(CURD)子域,计算子域等;
  • 平台领域上下文,提供平台的管理子域(CURD),风控子域,活动子域等;
  • 账号领域上下文,提供账号的管理子域(CURD),活动子域等;
  • 标的领域上下文,提供标的的管理子域(CURD),爬虫抓取子域等;
  • 资产计算上下文,提供了各类不同资产的余额计算,收益计算等公共支撑域;

上下文映射图

在进行上下文划分之后,我们还需要进一步梳理上下文之间的关系。 那么上下文之间到底存在哪些关系呢???

  • 合作关系(Partnership):两个上下文紧密合作的关系,一荣俱荣,一损俱损。
  • 共享内核(Shared Kernel):两个上下文依赖部分共享的模型。
  • 客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。
  • 遵奉者(Conformist):下游上下文只能盲目依赖上游上下文。
  • 防腐层(Anticorruption Layer):一个上下文通过一些适配和转换与另一个上下文交互。
  • 开放主机服务(Open Host Service):定义一种协议来让其他上下文来对本上下文进行访问。
  • 发布语言(Published Language):通常与OHS一起使用,用于定义开放主机的协议。
  • 大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。
  • 另谋他路(SeparateWay):两个完全没有任何联系的上下文。

比如资产领域和平台,账号,标的之间就是PS关系

image.png

通过上下文映射关系,我们明确的限制了限界上下文的耦合性,即在资产平台中,无论是上下文内部交互(合作关系)还是与外部上下文交互(防腐层),耦合度都限定在数据耦合的层级。

实体

梳理清楚上下文之间的关系后,接下来从战术层面上剖析上下文内部的组织关系。首先看下DDD中的一些定义。

实体 当一个对象由其标识区分时,这种对象称为实体(Entity)。实体类具备自己的属性和行为、状态等
值对象 当一个对象用于对事务进行描述而没有唯一标识时,它被称作值对象(Value Object)。
聚合根 Aggregate(聚合)是一组相关对象的集合,作为一个整体被外界访问,聚合根(Aggregate Root)是这个聚合的根节点。

聚合是一个非常重要的概念,核心领域往往都需要用聚合来表达。其次,聚合在技术上有非常高的价值,可以指导详细设计。聚合由根实体,值对象和实体组成。 如何设计聚合,我觉得有以下参考原则:

  • 边界内的内容具有一致性:在一个事务中只修改一个聚合实例。如果你发现边界内很难接受强一致,不管是出于性能或产品需求的考虑,应该考虑剥离出独立的聚合,采用最终一致的方式。

  • 设计小聚合:大部分的聚合都可以只包含根实体,而无需包含其他实体。即使一定要包含,可以考虑将其创建为值对象。

  • 通过唯一标识来引用其他聚合或实体:当存在对象之间的关联时,建议引用其唯一标识而非引用其整体对象。如果是外部上下文中的实体,引用其唯一标识或将需要的属性构造值对象。

    如果聚合创建复杂,推荐使用工厂方法来屏蔽内部复杂的创建逻辑。相反简单聚合,可以不使用工厂方式创建.

image.png

本章汇总

通过投资管理项目,分析领域的划分规则,如何确定界限上下文实现系统边界的划分,多个界限上下文之间的关联关系,进一步分析在界限上下文内如何确定实体和值对象,以及实体和值对象的聚合与聚合根.