昆仑山

注册

 

发新话题 回复该主题

领域驱动编程,代码怎么写 [复制链接]

1#
治白癜风上海哪家医院好 http://www.xxzywj.com/m/

一前言

相较于大家熟练使用的MVC分层架构,领域驱动设计更适用于复杂业务系统和需要持续迭代的软件系统的架构模型。关于领域驱动设计的概念及优势,可以参考的文献非常多,大多数的同学都看过相关的书籍,所以本文不讨论领域驱动概念层面的东西,而是试图从编程实践的层面,对领域驱动开发做一些简单的介绍。

加入阿里健康之后,我所在的团队也在积极推进领域驱动设计的应用,相关同学也曾给出优秀的脚手架代码,但目前看起来落地情况并不太理想,个人浅见,造成这种结果主要有四个原因。

大家更熟悉MVC的编程模式,需要快速实现某个功能的时候,往往倾向于使用较为稳妥、熟悉的方式。大家对领域驱动编程应该怎么编写并没有一个统一的认知(AxonFramework[1]对领域驱动设计实现的非常好,但它太“重”了)。DDD落地本身就比较难,往往需要事件驱动和EventSto来完美实现,而这二者是我们不常用的。领域驱动设计是面向复杂系统的,业务发展初期看上去都比较简单,一上来就搞领域驱动设计有过度设计之嫌。这也是领域驱动设计常常在系统不得不重构的是时候才被拿出来讨论的原因。

笔者曾在研发过程中研究、实践过领域驱动编程,对领域驱动框架AxonFramework也做了深入的了解,(也许是因为业务场景相对简单)当时落地效果还不错。抛却架构师的视角,从一线研发同学的角度来看,基于领域驱动编程的核心优势在于:

实施面向对象的编程模式,进而实现高内聚、低耦合。在复杂业务系统的迭代过程中,保证代码结构不会无限制地变得混乱,因此保证系统可持续维护。

领域驱动开发最重要的当然是正确地进行领域拆解,这个拆解工作可以在理论的指导下,结合设计者对业务的深入分析和充分理解进行。本文假定开发前已经进行了领域划分,侧重于研究编码阶段具体如何实践才能体现领域驱动的优势。

二保险领域知识简介

以保险业务为例来进行编程实践,一个高度抽象的保险领域划分如图所示。通过用例分析,我们把整个业务划分成产品域、承保、核保、理赔等多个领域(Bounded-Context),每个领域又可以根据业务发展情况拆分子域。当然,完备保险业务要比图中展现的复杂太多,这里我们不作为业务知识介绍的篇章,只是为了方便后续的代码实践。

三领域驱动开发的代码结构

1领域驱动的代码分层

可以使用不同的Java项目发布不同的微服务对领域进行隔离,也可以在同一个Java项目中,使用不同module进行领域隔离。这里我们使用module进行领域隔离的实现。但是无论采用何种方式进行领域隔离,领域之间的交互只能使用对方的二方包或者API层提供的HTTP服务,而不能直接引入其他领域的其他服务。

在每个领域内部,相对于MVC对应用三层架构的拆分,领域驱动的设计将应用模块内部分为如图示的四层。

用户接口层

负责直接面向外部用户或者系统,接收外部输入,并返回结果,例如二方包的实现类、SpringMVC中的Controller、特定的数据视图转换器等通常位于该层。在代码层面常常使用的包命名可以是interface,api,facade等。用户接口层的入参、出参类定义采用POJO风格。

用户接口层是轻的一层,不含业务逻辑。安全认证,简单的入参校验(例如使用

Valid注解),访问日志记录,统一的异常处理逻辑,统一返回值封装应当在这层完成。

用户接口层所需要的功能实现是由应用层完成,这里一般不需要进行依赖倒置。编码时,该层可以直接引入应用层中定义的接口,因而该层依赖应用层。需要注意的是,虽然理论上用户接口层可以直接使用领域层和基础设施层的能力,但这里建议大家在对这种用法熟练掌握前,最好采用严格的分层架构,即当前层只依赖其下方相邻的一层。

应用层

应用层具体实现接口层中需要功能,但该层并不实现真正的业务规则,而是根据实际的usecase来协调调用领域层提供的能力。

消息发送、事件监听、事务控制等建议在这一层实现。在代码层面常常使用的包命名可以是application,service,manager等。它用来取代SpringMVC中service层,并把业务逻辑转移到领域层。

领域层

领域层面向对象的,它主要用来体现和实现领域里的对象所具备的固有能力。因此,在领域驱动编程中,领域层的编程实现是不允许依赖其他外部对象的,领域层的编程是在我们对领域内的对象所具备的固有能力和它要在当前业务场景下展现什么样的能力有一定了解后,可以直接编码实现的。

例如我们最开始接触面向对象的编程的时候,常常会遇到的一个例子是鸟会飞、狗会游泳,假设我们的业务域只关心这些对象的运动,我们可以做如下的实现。

publicinterfaceMoveable{voidmove();}publicabstractclassAnimalimplementsMoveable{}publicclassBirdextendsAnimal{publicvoidmove(){//trytoflySystem.out.println("Iamflying");}}publicclassDogextendsAnimal{publicvoidmove(){//trytoswimSystem.out.println("Iamswimming");}}

基于领域驱动的编程需要这样(充血模型)去实现对象的能力,而不是像我们在MVC架构中常常使用贫血模型,把业务逻辑写在service中。

当然,即使采用了这样的编程方式,距离实现领域驱动还差的远,一些看似简单的问题就可能给我们带来巨大的不安感。例如复杂的对象应当如何初始化和持久化?同样一个事物在不同领域都存在,但其

分享 转发
TOP
发新话题 回复该主题