从Axon到DDD

1,266 阅读12分钟

前言

本文不讲DDD里面常见的概念、试图采用一种渐进式的方式把我理解的DDD表达出来,此文做为媒介多交流多思考互相促进。希望可以让大家对DDD的本质有一个了解,DDD是什么?DDD怎么落地?

虽然还没聊DDD是什么,先明确一下DDD不是什么:DDD不是分四层、DDD不是六边形架构。只是落地时大家把DDD与六边形、四层分层一起使用仅此而已。

Axon框架

为什么从Axon开始

axon框架包含DDD思想,对于大家理解DDD起到事半功倍的作用,前提是不要陷入axon框架本身实现,axon框架高度抽象复杂,并且是java8语法跟踪调试难度大,仅需要学习axon提供的应用demo即可。
另外提一本书《微服务架构设计模式》这本书的前七章内容axon框架都进行了完美的落地。学习过axon可以回过头看看《微服务架构设计模式》这本书会发现书很抽象axon很具象。

Axon介绍

Axon官方介绍:Axon框架是一个用于构建进化的、事件驱动的微服务系统的框架,它基于DDD领域驱动设计、命令-查询责任分离(CQRS)、事件溯源的原则。 Axon还包含六边形架构思想,可以让开发人员更关注业务,适应复杂的业务变化。

如果让我总结概述一下Axon的话:Axon尽量屏蔽所有的技术实现与细节、让研发人员专注于需求分析与设计,然后把设计翻译成代码填进Axon框架中

Axon还有一些特性:all in memory (热点数据、与redis缓存有一定不同)、无须开发人员关注并发(所有业务代码按照单线程开发即可)、无需关注本地事务与分布式事务、高性能。这里只提一下,不讲Axon内部实现原理。

Axon中几种思想简介

CQRS

CQRS架构.png CQRS本质是框架层面读写分离,读一万遍也不影响数据状态变化,业务开发过程中我们更关注能引起数据状态变化的增删改(查询性能优化时关注),在DDD中领域模型也更关注引起数据状态变化的增删改,所以Axon框架把CQRS与DDD思想一起使用。
另外一点在Axon中只要内存中增删改执行成功事务就执行成功,增删改的落库可以后续慢慢异步进行。这也是Axon框架可以屏蔽到所有技术实现让业务人员更加关注业务的一种手段。

事件溯源

事件溯源是一种通过过去发生的事件历史来存储应用程序状态的方法。 当前状态将根据事件的完整历史重新构建,其中每个事件表示应用程序中的一个更改或事实。 例如一个人的银行账户当前余额,由之前的此账户所有的收入、开支决定。我们可以通过回放之前每一笔交易,得到当前账户余额。

六边形架构

其实六边形本质就是适配器模式,无论是我们业务开发过程中还是Axon框架本身都会经常用到。举例:项目中我们使用Axon框架,从A服务发送一个消息到B服务,Axon提供了几种MQ供你选择,业务人员无需了解具体使用的是哪一种MQ。
吐槽一下:六边形架构这个词个人认为有问题,不能称之为架构。

Axon中概念

Axon中一些概念,Command、Command Bus、Command Handler、@Aggregate(Domain)、Event、Event Handler、Event Bus、Saga、Query、Query Handler。其中前面加黑的属于CQRS中的C, 后面两个属于Q。Aggregate(Domain)是实现C的基础与核心。

DDD

先学习下面Axon官方提供的例子,在回过头来理解其中的思想、概念。

Axon学习例子

只看上面的思想和概念,其实对理解学习Axon、DDD帮助不大,直接运行一下demo亲自感受一下Axon的运行逻辑。可以迅速的理解上面提到的概念。下载并启动AxonBank项目,访问: http://127.0.0.1:8080/ 此demo 实现创建银行账户、存钱、取钱、转账、查询转账记录四个功能。重点学习一下转账 一定要打断点感受一下axon的运行逻辑,否则很难理解axon

  带着问题运行一下AxonBank demo:
  1、command与event作用域与职责区别。
  2、saga的职责在业务过程中的作用。 
  3Aggregate与表之间的关系
  4、数据什么时候入库的。
  5、思考一下上面提到的axon概念中哪一个对应 四层中的领域层,APP层。

下面我画了一下Axon转账实现

银行转账流程图DDD.png 看了这个图,很多人会说这是一个流程图,其实它有一个更专业的名词在UML中叫活动图。流程图偏向过程,活动图不止流程还体现聚合,只看图仍然无法理解axon再次建议断点调试一下

DDD领域驱动设计

编码的目的

思考一个问题,编码的目的是什么?编码是为了解决现实问题的,要解决现实问题,我们面临的第一个问题就是如何把业务映射到代码。
要把业务映射到代码整个研发过程需要分三大步。首先需要了解业务(需求分析),然后是设计,最后是落地(开发部署运营)这三个步骤。无论是哪一种开发模式方式了解业务与设计都是这三步的之中的重中之重。

领域驱动设计

领域驱动设计重点在领域驱动。 领域与驱动分别对应开发过程三步中的了解业务和设计两步。
什么是领域,有几种驱动设计,领域驱动怎么驱动的?

什么是领域

编码是为了解决现实问题,要做成一件事我们会遇到很多具体的问题,我们习惯把问题归类划分。
比如要做一个电商(行业领域),我们面临的问题是用户、商品、挑选、下单、支付、派送、售后。运营还需要权限,日志,监控
比如要做一个融资平台(行业领域),我们面临的问题是用户、资方、风控、借钱(下单)、放款、还钱。运营还需要权限,日志,监控。
上面的每一个问题,都是一个具体的领域。一般情况下一个领域我们对应一个微服务。网上有很多讲领域(业务域、支撑域、通用域)的文章和其他的概念,大家自行学习。

驱动设计方式

驱动方式有很多种,比较常见的是数据库驱动设计。
数据库驱动设计、领域驱动设计。它们都是把业务映射到代码的落地方式,只是关注点不同。数据库驱动优先考虑如何承接数据,再考虑如何编码。领域驱动设计优先考虑如何编码,最后考虑数据的存储。数据的存储是技术层面的事情,逻辑代码是业务层面的事情,这也是我们一直听到的领域驱动更关注业务的原因。
数据库驱动设计: 拿到需求或者原型、很多人直接按照原型开始设计表,最后开始写代码,开发过程中发现表不合理原型不合理。
领域驱动设计:面对问题首先了解清楚需求进行需求分析,再设计具体类(聚合)、具体方法(入参)、具体逻辑( saga ),最后再考虑数据的落地。 考虑一下如何了解清楚需求、如何设计具体类、saga,用什么工具?

需求分析与设计

下面引用DDD书中的一个例子:下面以飞行控制系统为例说明如何进行需求分析与设计,请看《领域驱动设计精简版》中目录 《构建领域知识》、《创建通用语言》 部分(自行百度下载,这两部分是精华,其他都无须看)。
读完很多人肯定会说,这不就是沟通的过程吗? 是的,就是沟通的过程。如何提高沟通效率,让双方互相理解,避免歧义?# Eric Evans提出:限定上下文、通用语言。限定上下文,作用是在沟通的过程中先限定好场景,比如我说开户,需要先限定一个场景:比如我对银行柜员说开户、对券商柜员说开户意义是不一样的。通用语言,就是沟通方之间互相都理解的语言没有歧义的语言。Eric Evans只是提了这两个概念,特别是通用语言,并没有提出一种工具让我们在需求分析与设计阶段使用。推荐大家使用UML统一建模语言。

我的领域驱动理解过程

五年前学习DDD时候最早接触的是axon框架,感觉axon太强大了,无论多复杂的业务都能处理而且扩展性特别强,当时认为只有Axon(充血模型、面向对象编程)才是DDD正确的落地方式,当时认为三层变成四层不是DDD。
现在认为DDD:微服务 + 业务梳理 + 面向对象思想。于采用什么框架、是不是充血贫血、面向过程还是对象、分层是三层还是四层关系都不大,只设计阶段按照领域方式进行设计抽象类、梳理业务流程、状态扭转等即可。
题外话说一点:虽然Java语言是面向对象语言,大多数公司开发人员还是定义Bean和无状态服务,于C语言开发过程中定义结构体和函数没有任何区别,所以大多数公司的Java代码还是面向过程编码实现。

如何落地DDD

DDD设计步骤

由于DDD:微服务 + 业务梳理 + 面向对象思想
第一步: 按各种域划分微服务,一个服务可以包含一个或多个域。即使没听过DDD,其实大家也都接触过微服务不用多讲
第二步: 业务梳理 + 面向对象,先业务梳理再面向对象抽象类,这是一个反复的过程。在这个过程中我们不太关注读接口。具体操作: 在业务沟通过程中我们记录下名词、动词,名称一般会抽象成聚合(Java类) 动词抽象成方法(存在例外),画活动图(uml)、状态图等,在这个过程中我们可以借助UML工具
第三步: 设计数据表承接聚合、实体、值对象,聚合和表的关系并不是一对一,一个聚合可能对应多个表。

DDD设计落地工具

领域建模工具: UML统一建模语言: 包含类图、用例图,状态图,时序图,协作图,活动图等十种。 按照UML建模的类图是充血模型。状态图描述状态的扭转。用例图需求分析前期用于梳理功能,用椭圆标识一项一项功能。 时序图一般标识从某个类到某个类的执行顺序。活动图一种特殊的流程图(包含对象信息)。

DDD设计落地架构

上面的设计过程于具体技术栈无关,那么我们落地是选择充血模型还是贫血模型?通过哪种技术架构落地DDD那?
答:只要我们在需求与设计阶段是按照DDD的思想进行需求分析、设计落地,那么我们已经在使用DDD思想了。 具体是选择充血模型、贫血模型落地从理论上讲都可以。
充血模型:如果选择充血模型,我们可以使用Axon框架落地。使用Axon框架好处即使大家没有深入学习DDD思想,使用Axon框架也会在不知不觉中把ddd落地。并且可以把DDD设计阶段产出的类图、活动图完美映射到Axon中的聚合根和saga中。 
贫血模型:如果选择贫血模型,我们把DDD设计阶段产出的类图按照面向过程的方式一分为二落地实现,分成贫血的Bean与无状态的Service,UML活动图不在按照saga方式改成硬编码调用一个一个方法。
三层还是四层:我相信按照DDD方式把业务梳理透并设计了合理的类图活动图,无论是axon还是其他框架、无论是三层还是四层都可以落地。 那么为什么很多DDD在强调分层
分层是一种纪律与规范:分层思想早于微服务、DDD等思想。 即使大家都理解了DDD思想,每个人编码风格不统一也是一个问题,所以有三层或四层工程结构,每一层的职责明确便于后期开发维护。 有机会会写一篇分层与具体每层职责的文章。

DDD落地难点

一、惯性,因为之前的训练都是数据库驱动开发,一时之间很难转变成领域驱动开发。
二、时间,做详细需求与设计,最后把设计映射成代码,总体需要的时间也许会更长。