DDD 在京东 DevOps 项目协做领域的落地实战

image.png

DDD 即领域驱动设计(Domain-Driven Design)。它来源于著名建模专家 Eric Evans 发表的很是具备影响力的书籍:《domain-driven design –tackling complexity in the heart of software》(中文:领域驱动设计—软件核心复杂性应对之道)。其最先普遍应用于传统软件架构设计中。前端

今年4月,InfoQ 发布了软件架构与设计的趋势报告。在报告中能够看出,微服务、领域驱动设计等已成为目前软件开发行业的主流趋势,笔者也了解到,DDD 已成为大部分企业微服务落地、中台建设的指导思想。web

image.png

(图片来源:infoQ)spring

隔离变化和沉淀复用数据库

在互联网时代,业务系统构建初期,场景一般简单清晰,基于数据库表设计+CRUD 就能支撑业务的敏捷迭代。随着时间的推移,场景和业务逻辑变得复杂、定制,模块之间彼此耦合,系统变得愈来愈臃肿,每每牵一发而动全身。即便加一个字段这样看似简单的逻辑,光梳理对其余功能点和模块的影响,就至少须要一个对业务和代码都很熟悉的团队老成员才能稳妥敲定。相信一个实现1年以上的业务系统,其架构和代码就有可能正在经历着以上这些,开始慢慢腐化。后端

一个最经典的例子——电商系统里面诸如如下 product 类的设计(现实中字段数可能几百个),它是不符合高内聚低耦合这个架构设计原则的:restful

  • product 类一应俱全,什么领域的知识都知道;其职责不清晰,在沉淀复用上,团队成员的指望都不一致,实现姿式可能千奇百怪;
  • 采购、仓储、配送业务领域的逻辑变更可能都要修改 product ,一个高内聚的系统设计,是须要尽量隔离变化,使之产生的影响最小且控制在必定边界内。

image.png

DDD 经过划分限界上下文,拆分子领域,实现解耦和复用:网络

image.png

如下图片来自软件开发教父:Martin Fowler。诚然,为应付愈来愈复杂的业务逻辑,以自顶向下设计的领域聚合模型替代数据表模型,才能真正实现以业务实体为核心的灵活拓展:架构

  • 面向过程的脚本,不能很好地被扩展;
  • 面向数据表的设计,过分关注数据忽略行为,不能很好地被复用。

image.png

以上,让经历互联网泡沫存活下来的系统弄潮儿逐渐重燃对 DDD 的热情,但愿借助 DDD 能重拾建造更合理软件架构的信心。但在落地上,荆棘环生,困境各异。下面咱们来介绍一下 DDD 在京东 DevOps 项目协做领域的落地实践。app

将 DDD 应用到组件化
image.png框架

在京东内部,项目协做领域有两个用户群体覆盖特别广(总用户数 8W+)的系统:PMP 和行云。
image.png

PMP是一个更面向项目经理的项目管理软件,包含项目流程管理、需求管理、里程碑管理、任务管理、工时管理、成本管理、OKR 管理、ROI 验证和满意度评分等。在架构设计上,PMP 是一个很是庞大的单体应用,其先后端不分离,全部业务逻辑糅杂在一块儿,项目表单字段数达到了 100+。通过时间和组织的变迁,在可扩展性和可维护性上,简直是场灾难。

行云起初的定位是面向研发的敏捷协同软件,包含跨部门需求管理、敏捷迭代和任务管理等。随着行云的普遍使用,将 PMP 功能融合到行云的呼声愈来愈高。

同时,行云对公司外部提供 ToB 服务。内外部存在着大量差别化的场景和业务,单纯靠以往的不断添加 if else 揉泥球的方式,已经彻底行不通。新的定位和背景下,要求咱们必须将业务逻辑拆分为一个个灵活的组件,作到复用与逻辑沉淀;另外一方面,差别化的逻辑能够灵活扩展。这其中有着巨大的挑战:

上图中间圆弧描述的是咱们在对组件拆分时参考的原则(业务可独立运营、高内聚低耦合、数据完整性、渐进性),以此为基础,在应对业务和技术视角上的挑战时,咱们采起的相关 DDD 战略和战术。

业务的挑战

PMP 和行云中都有需求管理、任务管理。这其中需求、任务是否是一个概念?项目、需求、迭代、任务等之间是什么关系?如何对内和对外沉淀一套清晰可复用的业务组件?

DDD 中概念比较多,包含限界上下文、聚合、聚合根、实体、值对象等,本文先不关注这些细节,直接聚焦如下问题:

一、 划分边界,识别领域对象或者领域类,保证其职责清晰纯粹;

二、 明确领域类之间的关联、依赖,界定调用关系和方式。

经过领域分析建模,造成统一语言,最终将项目协做管理这个总体,拆分为一个个功能完整、业务逻辑明确的业务域,如项目域、任务域、工时域等等。每个业务域对外提供明确的领域服务,一个业务领域能够管理多个业务实体。

image.png
x_lazy=1&wx_co=1)

技术的挑战

在划分完领域后,理想的状况是各领域高度自治,减小依赖,甚至各领域能够由不一样的团队来实现,各团队聚焦沉淀该领域核心能力。

最终,咱们造成了一个组件化方案:根据划分出来的领域,将系统拆分红业务组件(核心子领域)和基础组件(通用和支撑子领域),各组件先后端分离。即会有如下前端应用:需求管理、项目管理、缺陷管理、用例管理等;后端应用相应提供不一样的领域服务,领域服务采用分层架构(interface, application, domain, infrastructure)。权限管理、网关服务、消息服务等基础组件下沉,实现最大化组件复用。

image.png

按照领域拆分红业务组件,对系统来讲,最终提供给终端用户还需是一个总体。但组件化意味着分离,那对于业务组件的先后端应用又面临怎样的协同挑战呢?**

业务组件前端应用的挑战

为了实现各业务组件能并行、独立演进,理想状况下,前端子应用需知足独立编译、打包、部署,并最终集成到一个平台上呈现统一的 UI 风格和一致的交互体验。

以上业界将此归结为微前端架构:实现一种架构风格,能够将众多独立交付的前端应用组合成一个大型总体,对客户表现为一件单一完整的产品。

最终,咱们行云团队落地了一个 Jmodule 微前端组件框架。

前端应用经过微前端架构和前端组件框架注册到平台,系统层实现组件元数据管理、运行时热加载静态资源和组件间通讯,对用户侧提供彻底可插拔可配置的组件。具体原理和实现等将在后续另外一篇章节中展开。

业务组件后端应用的挑战

行云在设计之初,后端服务采用的是微服务的架构(利用spring cloud gateway、Eureka 等实现服务注册、网关调用等),这和 DDD 相得益彰。在核心业务组件内,领域服务按照经典分层架构(interface, application, domain, infrastructure)来组织。

  • interface-用户界面层:Controller、restful接口调用,或者web端UI界面、移动端UI界面、第三方服务等;
  • apllication-应用层:对外为展示层提供各类应用功能,对内调用领域层(领域服务),应用层更像是实现某个特定场景的策略,或者流程上的编排等。其协同多个领域的服务,实现场景和业务的隔离
  • domain-领域层:负责表达业务概念,业务行为,业务状态以及业务规则,领域模型处于这一层,是业务软件的核心。其提供的是一系列原子服务,在这一层提供丰富的OPEN API,是实现组件化相当重要的一步
  • infrastructure-基础层实现业务和技术的隔离层。通常包含:网络通信、数据库持久化、异步消息服务、南向网关服务等。这一层在落地的时候,能够实现多种适配器adaptor 来兼容对内、对外、多云异构中间件环境。

除了界定组件内部分层的边界,还须要明确组件间的调用和依赖关系,进一步明确组件的职责。在 DDD 中相应地定义了限界上下文之间的映射关系:

  • 合做关系:两个上下文紧密合做的关系;
  • 客户方-供应方:上下文之间有组织的上下游依赖;
  • 遵奉者:下游上下文只能盲目依赖上游上下文;
  • 分离方式:两个彻底没有任何联系的上下文;
  • 共享内核:两个上下文依赖部分共享的模型;
  • 防腐层:一个上下文经过一些适配和转换与另外一个上下文交互;
  • 开放主机服务:定义一种协议来让其余上下文来对本上下文进行访问;
  • 事件发布订阅:一般用于定义开放主机的协议。

在组件的调用和依赖关系上,咱们能够借鉴以上映射关系的理念来落地。

举个例子,咱们已经沉淀了项目组件和任务组件。假设须要实现的业务场景:当删除某个项目时,须要检查项目中是否已有进行的任务,无则删之,有则提示不能删。根据 DDD 的映射关系,任务被项目依赖,任务处在上游的位置,理想状况下它不须要知道项目业务领域删除的具体逻辑规则,同时尽可能减小项目业务规则的变化所带来的影响。

因而,咱们在落地的时候,淘汰了下图左侧的实现方式——合做关系(项目和任务网状式相互调用,我中有你,你中有我),而是在下游项目领域加了一层适配层,来隔离业务的变化对上游的影响。同时,也确保任务这层逻辑干净、清晰,以供更多其余组件复用。

总结

总结一下,本文大致给出了 DDD 思潮在如下方面落地上的指导意义:

  • 如何沉淀清晰可复用的业务组件?
  • 如何隔离组件间的变化对其余组件的影响?

敬请期待

以后会在后续章节中分享 DDD 在插件落地的实践,解决以下问题:

如何快速响应五花八门的定制化需求,同时保持自身不腐化?