如何使用 DDD 指导微服务拆分?|8月更文挑战

我是架构精进之路,大厂架构师,CSDN 博客专家,点击上方“关注”,坚持天天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。程序员

软件架构发展经历

软件架构的发展经历了从单体架构、垂直架构、SOA 架构到微服务架构以及到如今最新的 service mesh(网格服务架构)的过程。借用 dubbo 的网站架构发展图和说明:数据库

微服务存在的问题

进入微服务以后 , 解决了集中式架构的单体应用不少问题, 可是新的问题应运而生 , 微服务的力度应该多大 ?微服务如何设计呢?微服务如何拆分 ?微服务边界在哪里 ?markdown

很长时间人们都没有解决这一问题,就连 Martin Fowler 在提出微服务架构的时候也没有告诉咱们这该如何拆分微服务。架构

甚至在很长的时间里人们对微服务拆分产生了一些误解, 有人认为:"微服务很简单,就是将以前的单体应用拆分红多个部署包, 或者将原来的单体应用架构替换为一套支持微服务的技术架构,就算是微服务了。" 还有人认为微服务应该拆分得越小越好。框架

鉴于上述情形, 不少项目由于前期拆分过分, 致使复杂度太高, 致使后期难以运维甚至难以上线。运维

能够得出一个结论:微服务拆分困境产生的根本缘由就是不知道业务或者微服务的边界到底在什么地方。换句话说,肯定了业务边界和应用边界,这个困境也就迎刃而解了。dom

DDD 的诞生

而 DDD 就是解决了这个肯定业务边界的问题,可见 DDD 并非一种技术架构,而是一种划分业务领域范围的方法论。DDD 的兴起是因为不少熟悉领域驱动建模(DDD)的工程师在进行微服务设计时, 发现用 DDD 的思路进行业务梳理能够很好规划服务边界, 能够很好实现微服务内部和外部的"高内聚、低耦合"。因而愈来愈多的人将 DDD 做为业务划分的指导思想。异步

DDD 是一种拆解业务、划分业务、肯定业务边界的方法, 是一种高度复杂的领域设计思想,将咱们的问题拆分红一个个地域, 试图分离技术实现的复杂性,主要解决的是软件难以理解难以演进的问题,DDD 不是一种架构, 而是一种架构方法论, 目的就是将复杂问题领域简单化, 帮助咱们设计出清晰的领域和边界, 能够很好的实现技术架构的演进。DDD 包括两部分,战略设计部分和战术设计部分。分布式

  • 战略设计主要从业务视角出发,创建业务领域模型,划分领域边界,创建通用语言的限界上下文,限界上下文能够做为微服务设计的参考边界。微服务

  • 战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

微服务拆分难题

开发者在刚开始尝试实现本身的微服务架构时,每每会产生一系列问题 :

  • 微服务到底应该怎么划分?

  • 一个典型的微服务到底应该有多微?

  • 若是作了微服务设计,最后真的会有好处吗?

回答上面的问题须要首先了解微服务设计的逻辑,科学的架构设计应该经过一些输入并逐步推导出结果,架构师要避免凭空设计和“拍脑门”的作法。

服务的划分有一些基本的方法和原则,经过这些方法能让微服务划分更有操做性。最终在微服务落地实施时也能按图索骥,不管是对遗留系统改造仍是全新系统的架构都能游刃有余。

微服务拆分的几个阶段

在开始划分微服务以前,架构师须要在大脑中有一个重要的认识:微服务只是手段,不是目的。

微服务架构是为了让系统变得更容易拓展、更富有弹性。在把单体应用变成靠谱的微服务架构以前,单体系统的各个模块应该是合理、清晰地。

也就是说,从逻辑上单体系统和微服务没有区别,某种理想状况下微服务只是把单体系统的各个模块分开部署了而已

大量的实践教训告诉咱们,混沌的微服务架构,比解耦良好的单体应用会带来更多麻烦。

混乱的微服务VS良好的单体

领域驱动设计立足于面向对象思想,从业务出发,经过领域模型的方式反映系统的抽象,从而获得合理的服务划分。

采用 DDD 来进行业务建模和服务拆分时,能够参考下面几个阶段:

  • 使用 DDD(领域驱动建模) 进行业务建模,从业务中获取抽象的模型(例如订单、用户),根据模型的关系进行划分限界上下文。

  • 检验模型是否获得合适的的抽象,并能反映系统设计和响应业务变化。

  • 从 DDD 的限界上下文往微服务转化,并获得系统架构、API 列表、集成方式等产出。

使用DDD划分微服务的过程

如何抽象?

抽象须要找到看似无关事务的内在联系,对微服务的设计尤其重要。

然而现实的例子比比皆是,电信或移动营业厅还须要用户分两步办理号卡业务、宽带业务。原始是不合适的抽象模型形成的,并最终影响了微服务的划分。

咱们可使用概念图来描述一些概念的抽象关系。

商品这一律念的概念图

若是没有抽象出领域模型,就得不到正确的微服务划分。

使用 DDD 进行业务建模

经过利用 DDD 对系统从业务的角度分析,对系统进行抽象后,获得内聚更高的业务模型集合,在 DDD 中一组概念接近、高度内聚并能找到清晰的边界的业务模型被称做限界上下文(Bounded Context)。

限界上下文能够视为逻辑上的微服务,或者单体应用中的一个组件。

在电商领域就是订单、商品以及支付等几个在电商领域最为常见的概念;在社交领域就是用户、群组、消息等。

DDD 的方法论中是如何找到子系统的边界的呢?

其中一项实践叫作事件风暴工做坊,工做坊要求业务需求提出者和技术实施者协做完成领域建模。把系统状态作出改变的事件做为关键点,从系统事件的角度触发,提取能反应系统运做的业务模型。再进一步识别模型之间的关系,划分出限界上下文,能够看作逻辑上的微服务。

事件是系统数据流中的关键点,相似于电影制做中的关键帧。

例如系统管理员能够登陆、建立商品、上架商品,对应的系统状态的改变是用户已登陆、商品已建立、商品已经上架;相应的顾客能够登陆、建立订单、支付,对应的系统状态改变是用户已登陆、订单已建立、订单已支付。

利用事件刺探业务黑盒并抽象出模型

在获得模型以后,经过分析模型之间的关系得出限界上下文。例如商品属性和商品相对于用户、用户组关系更为密切,经过这些关系做出限界上下文拆分的基本线索。

其次是识别模型中的二义性,让限界上下文划分更为准确。

例如,在电商领域,另一个不恰当设计的例子是:把订单中的订单项当作和商品一样的概念划分到了商品服务

但订单中的商品实际上和商品库中的商品不是同一个概念。当订单须要修改订单下的商品信息时,须要访问商品服务,这势必形成了订单和商品服务的耦合。

合理的设计应该是:商品服务提供商品的信息给订单服务,可是订单服务没有理由修改商品信息,而是访问做为商品快照的订单项。

订单项应该做为一个独立的概念被划分到订单服务中,而不是和商品使用同一个概念,甚至共享同一张数据库表。

典型具备”二义性“陷阱的场景

一组关系密切的模型造成了上下文(context),二义性的识别能帮咱们找到上下文的边界(bounded)。

验证和评审领域模型

前面咱们说到限界上下文能够做为逻辑上的微服务,但并不意味着咱们能够直接把限界上下文变成微服务。

限界上下文被设计出来后,验证它的方法能够从咱们采用微服务的两个目的出发:下降耦合、容易扩展,能够做为限界上下文评审原则:

**原则 1:**设计出来的限界上下文之间的互相依赖应该越少越好,依赖的上游不该该知道下游的信息。

**原则 2:**使用潜在业务进行适配,若是能在必定程度上响应业务变化,则证实用它指导出来的微服务能够在至关一段时间内足以支撑应用开发。

可是理想的领域模型每每抽象程度、成本、复用性这几个因素中获取平衡。

”抽象”的成本

用一个简单的图来表达话,咱们的领域模型设计每每在复用性和成本取得平衡的中间区域才有实用价值。

几个典型的误区

在大量使用 DDD 指导微服务拆分的实践后,咱们发现不少系统设计存在一些常见的误区

主要分为两类:未成功作出抽象、抽象程度太高、错误的抽象。

1)未成功作出抽象

在实际开发过程当中,你们都有一个体会,设计阶段只考虑了一些常见的服务,可是发现项目中有大量能够重用的逻辑,并应该作成单独服务。

当咱们在作服务拆分时,遗漏了服务的结果是有一些业务逻辑被分散到各个服务中,并不断重复。

2)抽象程度太高

抽象程度太高最典型的一个特征是获得的限界上下文极端的微小。

抽象程度太高带来的成本有:更多的微服务部署带来的运维压力、开发调试难度提升、服务间通讯带来的性能开销、跨服务的分布式事务协调等。所以抽象不是越高越好,应根据实际业务须要和成本考虑。

那相应的,微服务到底应该多小呢?

业界流传一句话来形容,微服务应该多小:“一个微服务应该能够在二周内完成重写“。

这句话可能只是一句调侃,若是真的做为微服务应该多微的标准是不可取的。

微服务的大小应该取决于划分限界上下文时各个限界上下文内聚程度。

3)错误抽象

对微服务或 DDD 理解不够。模型具备二义性,被放到不一样的限界上下文。

例如,订单中的收货地址、用户配置的经常使用地址以及地址库中的标准地址。

这三种地址虽然名称相似,可是在概念上彻底不是一回事

假如架构师将”地址“划分到了标准地址库中,势必会形成用户上下文和系统配置上下文、订单上下文存在没必要要的耦合。

抽象错误带来的依赖

上图的右边为正常的依赖关系,左边产生了不正常的依赖,会进一步产生双向依赖。

从限界上下文到系统架构

在经过 DDD 获得领域模型和限界上下文后,理论上咱们已经获得了微服务的拆分。可是,限界上下文到系统架构还须要完成下面几件事。

1)设计微服务之间的依赖关系

一个合理的分布式系统,系统之间的依赖应该是很是清晰地。依赖,在软件开发中指的是一个应用或者组件须要另一个组件提供必要的功能才能正常工做。所以被依赖的组件是不知道依赖它的应用的,换句话说,被调用者不须要知道调用方的信息,不然这不是一个合理的依赖

在微服务设计时,若是 domain service 须要经过一个 from 参数,根据不一样的渠道作出不一样的行为,这对系统的拓展是致命的。例如,用户服务对于访问他的来源不该该知晓;用户服务应该对订单、商品、物流等访问者提供无差异的服务。

所以,微服务的依赖关系能够总结为:上游系统不须要知道下游系统信息,不然请从新审视系统架构。

2)设计微服务间集成方式

拆分微服务是为了更好的集成到一块儿,对于后续落地来讲,还有服务集成这一重要的阶段。

微服务之间的集成方式会受到不少因素的制约,前面在讨论微服务到底有多微的时候就顺便提到了集成会带来成本,处于不一样的目的能够采用不一样的集成方式。

  • 采用 RPC(远程调用) 的方式集成。

使用 RPC 的方式可让开发者很是容易的切换到分布式系统开发中来,可是 RPC 的耦合性依然很高,同时须要对 RPC 平台依赖。业界优秀的 RPC 框架有 dubbo、Grpc、thrift 等

  • 采用消息的方式集成。

使用消息的方式异步传输数据,服务之间使用发布-订阅的方式交互。另一种思想是经过对系统事件传递,所以产生了 Event Sourcing 这种集成模式,让微服务具有自然的弹性。

  • 采用 RESTful 方式集成。

RESTful 是一种最大化利用 HTTP 协议的 API 设计方式,服务之间经过 HTTP API 集成。这种方式让耦合变得极低,甚至稍做修改就能够暴露给外部系统使用。

这三种集成方式耦合程度由高到低,适用于不一样的场景,须要根据实际状况选择,甚至在系统中可能同时存在。

服务间集成的方式还有其余方式,通常来讲,上面三种微服务集成的方式能够归纳目前常见系统大部分需求。

总结

这篇文章主要研讨了 DDD 火起来的缘由, 解决了什么业界难题, 知道 DDD 主要思路 , 以及 DDD 大概的实现步骤等 。

逻辑每每比经验更为重要。写这篇文章的初衷是为了获得微服务划分的依据是什么,我该怎么有说服力的回复?

是具体状况具体分析?By experience?仍是说,我是经过一套方法对业务逻辑进行分析获得的。

  • 当没有足够的经验直接解决问题,或问题庞大到不足以使用经验解决时,能支撑你作出决策就只有对输入问题进行有效的分析。

  • 使用 DDD 指导微服务划分,能在必定程度上弥补经验的不足,作出有理有据的系统架构设计。

我是架构精进之路,大厂架构师,CSDN 博客专家,点击上方“关注”,坚持天天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。

相关文章
相关标签/搜索