微服务架构设计基础之领域驱动设计

背景

微服务如今能够说是软件研发领域无人不提的话题,然而业界流行的对比多数都是所谓的Monolithic(单体应用),而大量的系统在十几年前都已是以SOA(面向服务架构)为基础的分布式系统了,那么微服务做为新的架构标准与SOA有什么差别点呢?其本质区别在于设计原理,微服务是去中心化设计,SOA是「集成」造成中心设计;前端

另外,笔者认为如下几点并非微服务和SOA的区别点:程序员

  • CI/CD:持续集成、持续部署自己与敏捷、DevOps是交织在一块儿的,CI\CD更倾向于软件工程的领域,与微服务无关;
  • 基于容器仍是虚拟机:Docker、虚拟机、物理机等是物理介质的一种实现方式,与微服务无关;
  • 微服务周边生态:好比日志平台、调用链系统?更多的是研发自己对于效率提升的自驱力,而与使用何种架构方式无关;
  • 通信协议:微服务的推荐通信协议是RESTful,而传统的SOA是SOAP。不过基于轻量级的RPC框架Dubbo、Thrift、gRPC来实现微服务也不少;在Spring Cloud中也有Feign框架将标准RESTful转为代码的API这种仿RPC的行为,这些通信协议不是区分微服务架构和SOA架构的核心差异;

固然,软件工程(DevOps)、基础设施(容器化)、软件开发模式(敏捷开发)的变革有利的推动了微服务架构的大行其道。而微服务架构是一种架构风格、架构理念,其中的「微」更体现了它的精髓在切分。在实际微服务的落地过程当中证实,若是切分是错误的,你得不到微服务承诺的「低耦合、自治、易维护」之类的优点,而且还会比单体架构拥有更多的麻烦。那么如何切分呢?其实并非一些新的方法论,而是都提出不少年的架构设计方法,也称它们为微服务设计基础或架构模型:领域驱动设计和立方体模型数据库

领域驱动设计

2004年,Eric Evans 发表了Domain Driven Design(领域驱动设计,DDD)。领域驱动设计已经问世十几年,从Eric Evans出版的著做「领域驱动设计」一书中对领域驱动作了开创性的理论阐述,在软件设计领域中,DDD能够称得上是步入暮年时期了。遗憾的是,国外软件圈享有盛誉并行之有效的设计方法学,国内大多数的技术人员却并不了解,也不曾运用到项目实践中。直到行业内吹起微服务的热风,人们彷佛才从新发现了领域驱动设计的价值,并非微服务拯救了领域驱动设计,是由于领域驱动设计一直在顽强的成长,其设计开放的设计方法体系,虽然历来未曾在国内大行其道,但却发挥着巨大的价值。表面上看确实是由于微服务,领域驱动设计才又开始出如今大众视野里。编程

领域驱动设计的意义

固然,领域驱动设计并不是「银弹」,不是能解决全部疑难杂症的「灵丹妙药」,学习并应用它的意义在于:设计模式

  • 一套完整的模型驱动的软件设计方法,用于简化软件项目的复杂度,它能带给你从战略设计到战术设计的规范过程,使得你的设计思路可以更加清晰,设计过程更加规范;
  • 一种思惟方式和概念,能够应用在处理复杂业务的软件项目中,加快项目的交付速度;
  • 一组提炼出来的原则和模式,能够帮助开发者开发优雅的软件系统、促进开发者对架构与模型的精心打磨,尤为善于处理系统架构的演进设计、有助于提升团队成员的面向对象设计能力与架构设计能力;
  • 领域驱动设计与微服务架构天生匹配,不管是在新项目中设计微服务架构,仍是将系统从单体架构演进到微服务设计,均可以遵循领域驱动设计的架构原则。

固然,领域驱动能给咱们带来不少收获,但若是你是属于如下几种状况的某种,那么你确实不须要学习领域驱动设计了:bash

  • 若是你是独当一面的架构师,并能设计出优雅的软件架构
  • 若是你是高效编码的程序员,并只想踏踏实实的写代码
  • 若是你是前端的设计人员,并奉行「用户体验至上」的理念
  • 若是你负责的软件系统并不复杂,二三人即可轻松维护

DDD的关键概念

一个软件系统的诞生,必定是为了解决咱们遇到的某个问题。好比一家企业的一直采用线下销售产品,耗费大量的财力和物力,但愿能够在线上销售本身的产品,用于实如今线销售销售产品的目的,那么就诞生了一个电商系统。一般最初设立的目标或要解决的问题就是一个软件项目的出发点,明确咱们要作什么。好比一个电商、一个论坛、一个支付平台等。微信

下文将从领域、问题域、领域模型、设计、驱动这几个词语的含义和联系的角度去阐述DDD是如何融入到软件开发的。要理解什么是领域驱动设计,首先要理解什么是领域,什么是设计,什么是驱动,什么驱动什么。数据结构

什么是领域/子领域(Domain/Subdomain)

领域是与某个特定问题相关的知识和行为。好比支付平台就属于特定的领域,只要是这个领域,都会有帐户、会记、收款、付款、风控等核心环节。因此,同一个领域的系统都具备相同的核心业务,他们要解决的问题的本质是一致的。一个领域本质上能够理解为就是一个问题域,只要是同一个领域,那问题域就相同。因此,只要咱们肯定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题、问题的范围边界就基本肯定了。架构

在平常开发中,咱们一般会将一个大型的软件系统拆分红若干个子系统。这种划分有多是基于架构方面的考虑,也有多是基于基础设施的。在DDD中,咱们对系统的划分是基于领域(基于业务)的。好比上文提到支付平台是一个领域,而帐户、会记、收款、付款等则为子领域。一个领域由众多子领域汇集而造成。框架

固然,问题随之而来:

  • 哪些概念应该建模在哪些子系统里面?
    • 有时可能会发现一个领域概念建模在子系统A中是能够的,而建模在子系统B中也合情合理。
  • 各个子系统之间的应该如何集成?
    • 有人可能会说,这不简单得就像客户端调用服务端那么简单吗?问题在于,两个系统之间的集成涉及到基础设施和不一样领域概念在两个系统之间的翻译,稍不注意,这些概念就会对咱们精心建立好的领域模型形成污染。

DDD中,有标准方法解决上述问题,就是**限界上下文(Bounded Context)和上下文映射图。**在一个领域/子域中,咱们会建立一个概念上的领域边界,在这个边界中,任何领域对象都只表示特定于该边界内部的确切含义。这样的边界便称为限界上下文。限界上下文和领域具备一对一的关系。从物理层面讲,一个限界上下文最终能够是一个Jar/War文件,甚至能够是一个Package中的全部对象。可是,技术自己并非用来界分限界上下文。

上图引自《实现领域驱动设计》。一般状况下,一个领域有且只有一个核心问题,咱们称之为该领域的「核心域」。在核心域、通用子域、支撑子域梳理的同时,会定义出子域中的「限界上下文」及其关系,用它来阐述子域之间的关系。界限上下文能够简单理解成一个子系统或组件模块。

什么是设计(Design)

DDD中的设计主要指领域模型的设计。DDD是一种基于模型驱动开发的软件开发思想,强调领域模型是整个系统的核心,领域模型也是整个平台的核心价值。每个领域都有一个对应的领域模型,领域模型可以很好的解决负责的业务问题。因此领域模型的设计和架构设计同等重要。

什么是驱动(Driven)

DDD中,老是以领域为边界,分析领域中的核心问题(核心关注点)。而后设计对应的领域模型,经过领域模型驱动代码的实现。而数据库设计、持久化技术这些都不是DDD的核心,属于外围的东西。与数据库驱动开发的思路造成对比,驱动中须要记住两个原则:

  • 领域驱动领域模型设计
  • 领域模型驱动代码实现

领域驱动设计的最大价值是让咱们告别从面向过程式的思想(天马星空,想到哪写到哪)转化为基于系统化的模型驱动思惟。咱们脑补一下软件开发中的常规心路历程:

  • 一、设计表结构
  • 二、写代码(代码写的很冗余,不够抽象)
  • 三、维护代码(适应业务变化)
  • 四、遇到困难(数据结构设计不合理、代码处处冗余、改BUG引入新BUG、新人看代码和无字天书通常)
  • 五、愈发难以维护,开始重构(理论上在老基础上改的技术债务堪比从新开发)
  • 六、重构完成,新系统上线(兼容历史数据、数据迁移、新老系统并行,等等出发点考虑,其实本质上只是作了代码重构)
  • 七、重复执行3-6步......

DDD的分层架构

四层架构

Eric Evans在《领域驱动设计-软件核心复杂性应对之道》这本书中提出了传统的四层架构模式,在后来演进过程当中出现了五层架构和六层架构,,以下图所示:

  • User Interface:用户界面层/展现层,负责与用户交互。包含显示信息、解释用户命令等;
  • Application:应用层,用来协调用户与各应用以及各应用之间的交互。不包含业务逻辑、不保存业务对象的状态;
  • Domain:领域层/模型层,负责表达业务概念,业务状态信息以及业务规则。包含领域模型、领域信息、业务对象的状态。领域层是业务软件的核心
  • Infrastructure:基础设施层,为其余各层提供技术能力。包括为应用层传递消息、为领域层提供持久化机制、为用户界面层绘制屏幕组件等等。基础设施层还可以经过架构框架来支持四个层次间的交互模式。

六边形架构

随着后续的演进,出现了一种改进分层架构的方法,即Robert C. Martin提出的依赖倒置原则(Dependency Inversion Principle,DIP)。它经过改变不一样层之间的依赖关系达到改进目的。

  • 高层模块不该该依赖于底层模块,二者都应该依赖于抽象
  • 抽象不该该依赖于细节,细节应该依赖于抽象

根据该原则的定义,DDD分层架构中的低层组件应该依赖于高层组件提供的接口,即不管高层仍是低层都依赖于抽象,整个分层架构好像被推平了,再向其中加入了一些对称性,就出现了一种具备对称性特征的六边形架构风格。六边形架构是Alistair Cockburn在2005年提出的,其本质是倡导不一样的客户经过「平等」的方式与系统交互,经过不断的扩展适配器转化成系统API所理解的参数来达到每种特定的输出,而每种特定的输出都有适配器完成相应的转化功能。

  • 聚合:
    • 一组具备内聚关系的相关对象的集合;
    • – 是一个修改数据的最小原子单元;
    • – 聚合一般使用id访问;
  • 实体(Entity):表示具备生命周期而且会在其生命周期中发生改变的东西。含有VO、具备identity的特性,一般具备生命周期的概念 JPA tag @Entity
  • 值对象(Value Object):表示起描述性做用的而且能够相互替换的概念。相似于pojo,不可变immutable,可在不一样模型中传递,Spring tag @value
  • 领域事件(Domain Event):全部的领域对象的跨聚合变动须要以事件方式进行通知和记录,聚合内的酌情考虑;
  • 工厂(Factory):负责全部对象的生成和组装;
  • 领域服务(Domain Service):纯技术层面的服务,例如日志,或者是跨聚合的编排服务,一般是Spring Component;
  • 资源层(Repository):相似于DAO层,Spring JPA, Hibernate之类 @CRUDRepository;
  • 防腐层:并不是是系统间的消息传递机制,它的职责更具体的是指将某个模型或者契约中的概念对象及其行为转换到另外一个模型或者契约中;

贫血模型VS充血模型

读完上面的两种分层架构方式,可能不少人会有疑问,这些是什么?为何我以前一直都没听到过这种分法?确实是这样,DDD和面向对象、设计模式等等理论有千丝万缕的联系,若是不熟悉OOA、OOD,那么DDD可能也会理解不了。由于咱们大部分从开发生涯开始之初接触的都是「Action层、Service层、Dao层、DB层」这样的MVC分层理论。而且在21中设计模式中,「行为型」的设计模式,咱们几乎没有什么机会使用,致使这些问题的缘由是J2EE经典分层的开发方式是「贫血模型」。

Martin Fowler(对,就是提出微服务的那位大牛)曾经提出了两种开发方式,即:

  • 以「贫血模型」为基础的「事务脚本」的开发方式
  • 以「充血模型」为基础的「领域驱动」的开发方式

贫血模型

贫血模型是指对象只用于在各层之间传输数据使用,只有数据字段和Get/Set方法,没有逻辑在对象中。而「事务脚本」能够理解为业务是由一条条增删改查的SQL组织而成,是面向过程的编程。

充血模型是面向对象设计的本质,一个对象是拥有状态和行为的。将大多数业务逻辑和持久化放在领域对象中,业务逻辑只是完成对业务逻辑的封装、事务、权限、校验等的处理。

举例,用户管理模块大概是这样的两种实现:

// 贫血模型下的实现
public class User{
	private Integer id;
	private String name;
	...
	// 省略get/set方法

}

public class UserManager{
	public void save(User user){
		
		// 持久化操做....
	
	}
}

// 保存用户的操做多是这样
userManager.save(user);

复制代码
// 充血模型下的实现
public class User{
	private Integer id;
	private String name;
	...
	// 省略get/set方法
	
	public void save(){
	
		// 持久化操做....
	
	}

}

// 保存用户的操做多是这样
user.save();

复制代码

Martin Fowler定义的「贫血模型」是反模式,面对简单的小系统用事务脚本方式开发没问题;稍微大一些的系统使用事务脚本方式会扩大维护成本,业务逻辑、各类状态散布在大量的函数中,哪怕就是要用户对象中增长一个字段,可能都会涉及到几个类的调整......

但愿领域对象可以准确地表达出业务意图,可是多数时候,咱们所看到的倒是充满getter和setter的领域对象,此时的领域对象已经不是领域对象了,反模式的贫血对象了。其实在贫血模型和充血模型模型以外,还有失血模型和胀血模型,但后者两个基本是实际开发中不会去使用,由于走的是两个极端。

总结

本文宏观角度介绍了领域驱动设计,那么微服务和DDD是什么关系呢?其实在2015年的一次演讲中,DDD的提出者Eric Evans表达了对微服务技术的热爱与支持,认为微服务是让DDD落地的好工具。由于DDD和微服务其本质是下降软件项目的复杂性,而DDD是一种设计理念/设计方法,DDD须要有强制性的原则作保障,不然不一样的领域对象终究会混在一块儿。而微服务自己的一些限制,以及你们都能理解微服务的实施前提和首要条件,会在实现上给DDD增长了一些原则限制。DDD和微服务的不必定要同时使用落地,可是若是将DDD和微服务(两个相差十岁的软件设计方法)结合一块儿,那么Martin Fowler和Eric Evans两位布道师是会很赞同的。

做者简介

本分类文章,与「随行付研究院」微信号文章同步,第一时间接收公众号推送,请关注「随行付研究院」公众号。
复制代码

相关文章
相关标签/搜索