一次面向领域的游历

2019-05-06  本文已影响0人  xuyaozuo

DDD 的目标 -- 设计高质量的软件模型


当前的软件开发的问题

case1.png

业务专家的思维方式:领域专家更专注于功能点的实现效果,产品级别的体验,缺少于对于内在业务逻辑关系的梳理,或者是这种业务关系梳理的表达。

技术专家的思维方式:技术专家更专注于技术实现的细节,这个表结构怎么设计,需不需要加索引,sql怎么写性能高,但是容易忽略业务所蕴含的本质逻辑。

case2.png

结果:修改业务功能越来月困难。

23142654.png

设计问题概括:

DDD 如何解决这些问题

DDD希望将领域专家和技术专家整合至同一个交付团队,领域专家可以“准确地传达业务规则”. 同时领域专家是可以参与到业务代码的设计中来。同时将技术专家从繁杂的技术实现中回归到业务实现中,不是面向数据编程面向业务编程,面向领域逻辑编程。DDD通过提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。

业务/产品的迭代需要战略设计

整个交付团队在开始工作之前需要进行战略设计, 战略设计流程如下。

case4.png

1 划分领域,子域,界限上下文
领域即是业务。表示组织所从事的一切业务。 将领域划分成子域即是划分不同的系统和业务的关注点。

那为什么在设计之初需要划分领域?
往往设计人员会将实现集中在一些‘实体’、‘对象’ 或者 特定的业务流程上面,甚至使用UML模型,但是全领域通用模型的建立是不可行的。随着各个业务点的需求不断迭代,各个领域模型需要有独自演化的能力。大而全的统一模型代表了各个领域模型在全局的高度耦合。

可行的方式是将整个业务领域划分成各个关注点不同的子域。各个子域对应是不同的界限上下文,并且子域会自发地依照子域需求的变化进行演化。

如何划分子域:将子域分为三类

领域划分的原则:

 同时领域的划分应该尽量是内聚的,领域内所包含的概念是紧密相连或者有直接关系的,通常是为同一个共同业务目标服务的相关功能以及概念,同时同一个领域应该尽量被包括在同一个界限上线文中。

什么是界限上下文的定义
 界限上下文是一个语义的边界,同一个概念命名在同一个界限上下文中应该是唯一的,而在不同的界限上下文中可以是区别的。
例如订单这个概念在订单系统中表示一次购物行为的详细信息。
而在库存预测系统中,订单的概念可能只需要有产品,价格,数量并不会包含用户的具体地址,或者订单的收货具体时间点等信息。

对于一个电商系统,假想的领域划分如下:


case5.png

2 确定通用语言

通用语言是一种团队的公用语言,由业务专家和技术专家共同确定。确定通用语言的方式方法包括但不局限于:

使用思维导图创建概念:
下图是一个简单的电商购物领域的通用语言设计,将领域功能按照“流程” “用例” “行为细则”的概念进行分解


case6.png

建立术语表:

在通用语言建立之后,技术专家可以依据通用语言进行软件建模设计,业务专家可以根据通用语言设计产品页面、交互等产品逻辑。

根据团队确认的通用语言,业务专家可以开始作出产品详细的设计,技术专家可以数据建模,软件设计等。大家共同运用专业能力确保最后的交付。

DDD的战术 -- 如何解耦技术实现和业务逻辑代码


架构风格

分层架构

DDD 可以使用传统的分层架构来实现:


image.png

分层模块的缺点是层与层相互依赖,使用依赖倒置的原则可以解耦各个模块的实现。

依赖倒置原则:高层模块不应该依赖于底层模块,抽象不应该依赖于细节,细节依赖于抽象。

例子中的代码是实现一个简单的用户子域,简单实现了用户获取和注册的功能

项目目录结构

/app
    /config
        config.go // 各种配置文件中entry 的结构体
/domain
  /league // 模块名称
    model.go // 领域模型,包括entity aggregate 
    repository.go // 领域相关仓储的interface的定义
    service.go // 领域服务的相关代码
/usecase // 服务用例相关的代码,或者叫做应用服务相关,这一层的抽象模型可以直接使用protoc 定义的对象
  usecase.go // 具体用例的实现
  monitor.go
  common.go
/interface // 各种接口适配器的实现,既对应的是**用户接口层**
  monitor.go // 监控的具体实现
  grpc.go // grpc server 的实现
  message.go //         
/infrastructure // 基础设施
  /repository // repository 接口的实现
  /message // 领域事件 push / publish 的具体实现 
/registry
  registry.go // 类似spring 中的application context,主要用于获取依赖注入的接口实例 
/proto // gpc protocal files
/cmd // 命令行启动配置
main.go  
六边形架构

DDD通常也使用六边形架构来表示,目的是显著区分各个模块的边界


image.png

后记

本文并未涵盖过多的技术实现细节。如果希望深入DDD技术实现细节中需要关注的是

在微服务中使用DDD
+ 如何实现上下文集成并且映射。
+ 如何实现领域事件的异步通讯,以及一致性保证,消息队列如何设计。
+ 如何做服务自治,减少服务之间启动的依赖。
+ 并发情况下以聚合对象为单位的版本控制。
+ 数据缓存应该如何实现。
+ 如何实现长时间,长流程的聚合。

上一篇下一篇

猜你喜欢

热点阅读