当前位置: 首页 > 科技观察

如何用DDD指导微服务拆分?

时间:2023-03-18 19:37:10 科技观察

本文转载自微信公众号《架构改进之路》,作者的架构改进之路。转载本文请联系建筑改良之路公众号。软件架构的发展经历软件架构的发展经历了从单体架构、垂直架构、SOA架构到微服务架构和最新的ServiceMesh(网格服务架构)的过程。借用dubbo的网站架构开发图及说明:微服务存在的问题进入微服务后,集中式架构单一应用的很多问题都解决了,但是新的问题也应运而生。微服务应该有多强大?微服务设计呢?如何拆分微服务?微服务的边界在哪里?很久以来人们都没有解决这个问题,就连MartinFowler在提出微服务架构的时候也没有告诉我们如何拆分微服务。甚至长期以来,人们对微服务的拆分存在一些误解。有人认为:“微服务很简单,就是把以前的单体应用拆分成多个部署包,或者把原来的单体应用架构拆分,换成一套支持微服务的技术架构,甚至是微服务。”有人认为微服务应该拆分得越小越好。鉴于上述情况,很多项目由于前期拆分过多,导致项目过于复杂,导致后期难以运维甚至上线。可以得出结论,微服务分裂困境的根本原因是不知道业务或微服务边界在哪里。也就是说,一旦确定了业务边界和应用边界,这个困境就迎刃而解了。DDD和DDD的诞生解决了业务边界的确定问题。可见DDD并不是一种技术架构,而是一种划分业务领域范围的方法论。DDD的兴起,是因为很多熟悉领域驱动建模(DDD)的工程师发现,在设计微服务时,利用DDD思想进行业务排序,可以很好地规划服务边界,可以很好地实现“高层次”的内外部微服务.内聚、低耦合”。所以越来越多的人把DDD作为业务划分的指导思想。DDD是一种拆解业务、划分业务、确定业务边界的方法。是一种高度复杂的领域设计思想。它划分了我们的问题划分区域,试图将技术实现的复杂性进行分离。主要解决的问题是软件难以理解难以演化的问题。DDD不是一种架构,而是一种架构方法论。目的是简化复杂的问题领域和帮助我们设计清晰的领域和边界,可以很好的实现技术架构的演进DDD由两部分组成,战略设计部分和战术设计部分。战略设计主要从业务角度出发,建立业务领域模型,划分领域边界,建立通用语言的有界上下文。限界上下文可以作为微服务设计的参考边界。战术设计从技术角度出发,着眼于领域模型的技术实现,完成软件的开发与实现,包括聚合根、实体、值对象、领域服务、应用服务、资源的代码逻辑设计与实现图书馆。微服务拆分问题开发者在初次尝试实现自己的微服务架构时,往往会有一系列的疑问:微服务应该如何拆分?典型的微服务应该如何微服务?,最后真的会有好处吗?要回答以上问题,首先需要了解微服务设计的逻辑。科学的架构设计应该使用一些输入并逐渐得出结果。架构师应避免凭空设计,“拍脑袋”。服务的划分有一些基本的方法和原则,这些方法可以使微服务的划分更具可操作性。最终微服务实现的时候,也能照着图走,不管是遗留系统的改造,还是新系统的架构,都能游刃有余。微服务拆分的几个阶段在开始拆分微服务之前,架构师需要在脑海中有一个重要的认识:微服务只是手段,不是目的。微服务架构是为了让系统更容易扩展,更有弹性。在将单体应用转变为可靠的微服务架构之前,单体系统的每个模块都应该合理清晰。也就是说,从逻辑上讲,单体系统和微服务没有区别。在某种理想情况下,微服务只是将单体系统的模块分开部署。耦合良好的单体应用程序可能会导致更多麻烦。混沌的微服务VS好的单体领域驱动设计是基于面向对象的思想,从业务出发,通过领域模型体现对系统的抽象,从而获得服务的合理划分。在使用DDD进行业务建模和服务拆分时,可以参考以下几个阶段:使用DDD(领域驱动建模)进行业务建模,从业务中获取抽象模型(如订单、用户),Relationships划定限界上下文。验证模型是否被适当抽象,并能反映系统设计和响应业务变化。从DDD的限界上下文转化为微服务,获得系统架构、API列表、集成方式等输出。如何抽象使用DDD划分微服务的过程?抽象需要找到看似无关的事务之间的内在联系,这对于微服务的设计尤为重要。然而,实际的例子比比皆是。电信或移动营业厅要求用户分两步办理号码卡业务和宽带业务。原语是由不合适的抽象模型造成的,最终影响到微服务的划分。我们可以用概念图来描述一些概念的抽象关系。如果商品概念的概念图没有对领域模型进行抽象,就得不到微服务的正确划分。使用DDD进行业务建模通过使用??DDD从业务的角度分析系统,对系统进行抽象后,得到了一个具有更高内聚性的业务模型集合。在DDD中,一组概念紧密、内聚性强且能找到明确边界的业务模型称为限界上下文(BoundedContext)。限界上下文可以被认为是一种逻辑微服务,或者是单体应用程序中的一个组件。在电子商务领域,是电子商务领域最常见的概念,如订单、商品、支付;在社交领域,就是用户、群组、消息等。在DDD方法论中,你如何找到子系统的边界?其中一种实践称为事件风暴研讨会,它需要业务需求提出者和技术实施者协作完成领域建模。以改变系统状态的事件为关键点,从系统事件的角度出发,提取能够反映系统运行情况的业务模型。进一步识别模型之间的关系,划分限界上下文,可以看作是逻辑微服务。事件是系统数据流中的关键点,类似于电影制作中的关键帧。例如,系统管理员可以登录、创建产品、将产品上架。对应的系统状态变化为用户已登录,商品已创建,商品已上架;对应的客户可以登录、创建订单、支付,对应的系统状态变化为用户。登录,创建订单,支付订单。利用事件窥探业务黑盒,抽象模型。得到模型后,分析模型之间的关系,得到限界上下文。例如,产品属性和产品与用户和用户群体的关系更为密切,通过这些关系做出有界上下文拆分的基本线索。二是识别模型中的歧义,使限界上下文划分更加准确。比如在电子商务领域,另一个设计不当的例子是:订单中的lineitem被当成商品是同一个概念,被划分为商品服务,但订单中的商品实际上并不相同概念作为商品库中的商品。当订单需要修改订单下的商品信息时,需要访问商品服务,这必然造成订单与商品服务的耦合。一个合理的设计应该是:产品服务向订单服务提供产品信息,但订单服务没有理由修改产品信息,而是将订单项作为产品的快照进行访问。订单项应该作为一个独立的概念划分到订单服务中,而不是与商品使用相同的概念,甚至共享同一个数据库表。存在“歧义”陷阱的典型场景一组密切相关的模型构成一个上下文,对歧义的识别可以帮助我们找到上下文的边界。验证和回顾领域模型前面我们提到了限界上下文可以作为逻辑微服务,但这并不意味着我们可以直接将限界上下文变成微服务。限界上下文设计好之后,验证的方法可以基于使用微服务的两个目的:降低耦合,方便扩展,可以作为限界上下文审查原则:原则1:设计的限界上下文之间的相互依赖性应该是越少越好,依赖的上游应该不知道下游的信息。原则2:利用潜在业务进行适应。如果它能在一定程度上响应业务变化,就证明它所引导的微服务能够在相当长的一段时间内支撑应用开发。然而,一个理想的领域模型通常会在抽象、成本和可重用性等因素之间取得平衡。“抽象”的代价可以用一张简单的图表示,而我们的领域模型设计往往在可重用性和代价平衡的中间区域才具有实用价值。几个典型的误区在大量使用DDD指导微服务拆分实践后,我们发现在很多系统设计中存在一些常见的误区,主要分为两类:抽象不成功、抽象过度、抽象错误。1)抽象不成功在实际开发过程中,大家有一个体会,在设计阶段只考虑了一些通用的服务,但是发现项目中有很多可复用的逻辑,应该做成一个单独的服务。我们在做服务拆分的时候,省略服务的结果就是一些业务逻辑被分散到各个服务中去重复。2)抽象度太高抽象度太高最典型的特征之一就是得到的限界上下文极小。过高抽象带来的成本包括:更多微服务部署带来的运维压力、开发调试难度、服务间通信带来的性能开销、跨分布式事务的协调等。服务。所以,抽象并不是越高越好,要根据实际业务需求和成本来考虑。相应地,微服务应该有多小?业界有一句话形容一个微服务应该有多小:“一个微服务应该能够在两周内被重写”。这句话或许只是一句玩笑话。如果真的是微服务应该怎样的标准,那是不可取的。微服务的大小应该取决于划分限界上下文时限界上下文的内聚程度。3)对微服务或DDD的理解不够,错误的抽象。模型是模棱两可的,并且放置在不同的有界上下文中。例如订单中的收货地址、用户配置的常用地址、地址库中的标准地址等。这三个地址的名称虽然相似,但在概念上并不是一回事。如果架构师将“地址”划分到标准地址库中,势必造成用户上下文、系统配置上下文、订单上下文之间不必要的耦合。抽象错误导致的依赖上图右边是正常的依赖关系,左边产生异常依赖,会进一步产生双向依赖。从限界上下文到系统架构通过DDD得到领域模型和限界上下文后,我们理论上就得到了微服务的拆分。但是,从限界上下文到系统架构,还需要做以下事情。1)设计微服务之间的依赖关系一个合理的分布式系统,系统之间的依赖关系应该是非常明确的。依赖性,在软件开发中,指的是一个应用程序或组件需要另一个组件提供必要的功能才能正常工作。因此,依赖组件不知道依赖它的应用程序。也就是说,被调用者不需要知道调用者的信息,否则这不是合理的依赖。在微服务设计中,如果领域服务需要传递一个from参数,根据不同的通道做出不同的行为,这对系统的扩展是致命的。比如用户服务不应该知道访问他的来源;用户服务应为访问者提供无差别的订单、商品、物流等服务。因此,微服务的依赖可以概括为:上游系统不需要知道下游系统信息,否则请重新审视系统架构。2)设计微服务之间的集成方式拆分微服务是为了更好的集成。对于后续的实施,还有一个重要的服务整合阶段。微服务之间的集成方式会受到很多因素的制约。我们在讨论微服务是怎样的时候,提到了集成会带来成本。不同的集成方法可以用于不同的目的。使用RPC(远程调用)进行集成。使用RPC可以让开发者非常容易的切换到分布式系统开发,但是RPC的耦合度还是比较高的,同时需要依赖RPC平台。业界优秀的RPC框架有dubbo、Grpc、thrift等,它们都是以消息的形式集成的。消息用于异步传输数据,服务以发布-订阅的方式进行交互。另一种思路是传递系统事件,于是产生了EventSourcing的集成模式,使得微服务具有天然的弹性。以RESTful方式集成。RESTful是一种最大限度利用HTTP协议的API设计方式,通过HTTPAPI集成服务。这种方式使得耦合度极低,即使是一点点的修改都可以暴露给外部系统。这三种集成方式的耦合度由高到低,适用于不同的场景。需要根据实际情况选择,甚至可能同时存在于系统中。还有其他方法可以集成服务。总的来说,以上三种微服务集成方式可以概括目前大部分常见的系统需求。综上所述,本文主要讨论DDD为什么火起来的原因,它解决了哪些行业问题,DDD的主要思想,DDD的大致实现步骤。逻辑往往比经验更重要。写这篇文章的初衷是想弄明白微服务划分的依据是什么,又该如何回答才能有说服力呢?是具体情况具体分析吗?凭经验?或者说,我分析业务逻辑是通过一套方法来搞定的。当没有足够的经验直接解决问题,或者问题太大无法用经验解决时,唯一能支持你做决定的就是对输入问题的有效分析。使用DDD来指导微服务的划分,可以在一定程度上弥补经验上的不足,做出合理的系统架构设计。