DDD微服务中台设计-理论篇

借用当下最流行的段子做个开场白。
“设计原则千万条,高内聚低耦合第一条,架构设计不规范,开发运维两行泪!”。
在分布式架构下,单体应用被拆分为多个微服务,为了保证微服务的单一职责和合理拆分,“高内聚、松耦合”是最宝贵的设计原则。
通俗点讲,高内聚就是把相关的行为聚集在一起,把不相关的行为放在别处,如果你要修改某个服务的行为,最好只在一处修改。如果做到了服务之间的松耦合,那么修改一个服务就不需要修改另一服务,一个松耦合的服务应该尽可能少的知道与之协作的那些服务的信息。
从集中式架构向分布式架构的技术转型,正如从盖砖瓦房向盖高楼大厦转变一样,必然要有组织、文化、理念和设计方法的同步更新,其中最不可或缺的能力就是架构设计能力。
如何做到“高内聚、低耦合”们先来学习几种典型的微服务架构模型。

一、微服务架构模型

1.整洁架构(又名洋葱架构)

在整洁架构里,同心圆代表应用软件的不同部分,从里到外依次是领域模型、领域服务、应用服务、最外围是容易变化的内容,如界面和基础设施(如数据存储等)。整洁架构是以领域模型为中心,不是以数据为中心。

DDD微服务中台设计-理论篇
六边形架构将系统分为内部和外部两层六边形,内部六边形代表了应用的核心业务逻辑,外部六边形代表外部应用、驱动和基础资源等。内部通过端口和适配器与外部通信,对应用以 API 主动适配的方式提供服务,对资源通过依赖反转被动适配资源的形式呈现。一个端口可能对应多个外部系统,不同的外部系统使用不同的适配器,适配器负责对协议进行转换。这就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动。
六边形架构各层的依赖关系与整洁架构类似。
3. CQRS(命令与查询职责分离)

CQRS 就是读写分离,读写分离的主要目的是为了提高查询性能,同时达到读、写解耦。而 DDD 和 CQRS 结合,可以分别对读和写建模。

DDD微服务中台设计-理论篇
DDD 分层架构各层定义与职能:
展现层:它负责向用户显示信息和解释用户命令,完成前端界面逻辑。这里的用户不一定是使用用户界面的人,也可以是另一个计算机系统。
应用层:它是很薄的一层,负责展现层与领域层之间的协调,也是与其它系统应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,不保留业务对象的状态,只保留有应用任务的进度状态,更注重流程性的东西。它只为领域层中的领域对象协调任务,分配工作,使它们互相协作。
领域层:它是业务软件的核心所在,包含了业务所涉及的领域对象(实体、值对象)、领域服务以及它们之间的关系,负责表达业务概念、业务状态信息以及业务规则,具体表现形式就是领域模型。领域驱动设计提倡富领域模型,即尽量将业务逻辑归属到领域对象上,实在无法归属的部分则以领域服务的形式进行定义。
基础设施层:它向其他层提供通用的技术能力,为应用层传递消息(API 网关等),为领域层提供持久化机制(如数据库资源)等。
5.架构模型对比和分析

虽然整洁架构、六边形架构以及 DDD 分层架构三种架构模型展现方式以及解决问题的出发点不一样,但其架构思想与微服务架构高内聚低耦合的设计原则高度一致。

DDD微服务中台设计-理论篇
中台、领域以及微服务属于不同层面的内容,稍作分解我们理清他们之间的关系。
以保险领域为例,业务中台大致可分为两类:第一类是提供保险核心业务服务的专属业务中台(如承保、理赔等业务);第二类是支撑核心业务流程完成保险全流程的通用中台(如主数据、客户、用户以及电子保单等)。
专属业务中台是保险企业的核心竞争力,对应 DDD 的核心子域。通用中台对应 DDD 支撑子域和通用子域。不同领域可根据领域大小进一步细分多个子域,多个子域可对应到一个业务中台,一个业务中台也可能会分解成多个子域。
DDD微服务中台设计-理论篇

六、DDD 分层架构中的服务

前面我们谈到了 DDD 的分层架构,分层架构主要包括:展现层、应用层、领域层和基础层(参考图:DDD(领域驱动设计)分层架构),各层都有不同的服务,但由于各层职责不一样,服务目的和实现方式也存在差异。

1、应用层服务
应用层是很瘦的一层,其服务主要用来表述应用和用户行为。它主要负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装,拼装完领域服务后以粗粒度的服务通过 API 网关向前台应用发布。通过这样一种方式,隐藏了领域层的复杂性及其内部实现机制。 应用层除了定义应用服务之外,在这层还可以进行安全认证,权限校验,持久化事务控制或向其他系统发送基于事件的消息通知。

2、领域层服务
领域层是较“胖”的一层,它实现了全部业务逻辑并且通过各种校验手段保证业务正确性。业务逻辑包括:业务流程、业务策略、业务规则、完整性约束等。 当领域中的某个操作过程或转换过程不是实体或值对象的职责时,便将该操作放在一个单独的服务接口中,这就是领域服务,领域服务是无状态的。

3、基础设施层服务
基础设施层服务位于基础设施层,根据依赖倒置原则,封装基础资源服务,实现资源层与应用层和领域层的调用依赖反转,为应用层和领域层提供基础资源服务(如数据库、缓存等基础资源),实现各层的解耦,降低外部资源的变化对核心业务逻辑的影响。

4、总结
应用层服务是展现层和领域层的桥梁,通过调用领域对象和领域层服务来表达用例和用户故事。领域对象负责单一操作, 领域层服务用于协调多个领域对象共同完成某个业务操作。 应用服务原则上不处理业务逻辑,领域服务处理业务逻辑。

七、微服务的边界设计(逻辑边界与物理边界)

在领域模型设计时,我们通常会根据限界上下文将领域分解成不同的子域,划分业务领域的逻辑边界。在限界上下文内不同的实体和值对象可以组合成不同的聚合,从而形成聚合与聚合之间的逻辑边界。一般来说,限界上下文可以作为微服务拆分的依据,而限界上下文内的聚合由于其业务逻辑的高度内聚,也可以根据需要将同一领域内的聚合业务逻辑代码拆分为微服务,聚合是领域中可以拆分为微服务的最小单元。
限界上下文与限界上下文之间以及聚合与聚合之间的边界是逻辑边界,微服务与微服务的边界是物理边界。逻辑边界强调业务领域逻辑或代码分层的隔离,物理边界强调部署和运行的隔离。
微服务设计时是否一定要做到逻辑边界与物理边界一致br> 逻辑边界的划分是否可以细于物理边界br> 过度的微服务拆分会导致服务、安全和运维管理更复杂,领域之间的服务协同或应用层的处理逻辑更复杂,总之一句话就是:需要更高的研发技能要求和软件维护成本。因此领域和代码分层的逻辑边界的细分是必要的,但是物理边界不宜过细,也就是说在不违反微服务拆分原则的情况下,不宜过度拆分微服务。
为什么要细分业务和代码逻辑边界br> 在从单体向微服务演进后,随着新需求的出现,新的微服务会开始慢慢的膨胀起来,有一天你会发现膨胀的微服务有一部分业务能力需要拆分出去时,如果没有提前进行逻辑边界的细分,微服务内代码的过度耦合将会让你无从下手,你是否还需要再做一次从单体向微服务的拆分br> 如果你在微服务设计时已经根据业务领域边界提前进行了领域代码的分层和逻辑隔离,在微服务再次拆分时,分别对逻辑分离的领域代码打包,同步进行数据库拆分,就可以快速完成微服务的拆分,而不需要重复从单体应用向微服务痛苦的演进过程。
当然,在同一个微服务内逻辑隔离的代码,在内部领域服务之间调用以及数据访问设计上需要有合理的松耦合的设计和开发规范,否则也不能很快的完成微服务再次拆分。
总之,我们需要内外部逻辑边界清晰的微服务,而不是从一个大单体重构为多个小单体。

八、要做微服务而不是小单体

很多时候大家对微服务设计的理解都以为只要最后确定拆分出多少个微服务就可以了,其实拆成多少个微服务并不是微服务架构的要点。如何设计或拆分才能避免拆分出来的微服务不是小单体才是所有微服务架构团队需要关注和解决的问题,这也是 DDD 的价值所在。

DDD微服务中台设计-理论篇
对代码进行逻辑隔离和分层的主要意义在于:
1、避免各层代码的交叉,保持领域代码的纯洁,保证中台领域层业务逻辑的稳定。
2、业务和代码模型的逻辑保持一致,有利于微服务的拆分和组合。

十、微服务的设计和拆分(微服务拆分方法)

绞杀者模式
绞杀者模式类似建筑拆迁,在新建筑分阶段建设完成入住后,分步拆除旧建筑物。
“绞杀者模式”是在遗留系统外围,将新功能用新的方式构建为新的服务 。通过在新的应?中实现新特性,保持和现有系统的松耦合,随着时间的推移,新的服务逐渐“绞杀”老的系统。以此逐步地替换原有系统。 对于那些老旧庞大难以更改的遗留系统,推荐采用绞杀者模式。
修缮者模式
修缮者模式类似文物修复,将存在问题的部分建筑重建或者修复后,重新加入到原有的建筑中,保持建筑原貌。
“修缮者模式”是在既有系统的基础上,通过剥离新业务和功能,逐步“释放”现有系统耦合度,解决遗留系统质量不稳定和 Bug 多的问题。就如修房或修路一样,将老旧待修缮的部分进行隔离,用新的方式对其进行单独修复。 修复的同时,需保证与其他部分仍能协同功能。 修缮模式适用于需求变更频率不高的存量系统。

十一、微服务拆分原则

微服务拆分过程中需严格遵守高内聚、低耦合原则,同时结合项目的实际情况,综合考虑业务领域、功能稳定性、应用性能、团队以及技术等因素。
1、基于业务领域拆分,在领域模型设计时需对齐限界上下?,围绕业务领域按职责单一性、功能完整性进行拆分,避免过度拆分造成跨微服务的频繁调用。
2、基于业务变化频率和业务关联拆分,识别系统中的业务需求变动较频繁的功能,考虑业务变更频率与相关度,并对其进行拆分,降低敏态业务功能对稳态业务功能的影响。
3、基于应用性能拆分,考虑系统?功能性需求,识别系统中性能压力较大的模块,并优先对其进行拆分,提升整体性能,缩小潜在性能瓶颈模块的影响范围。
4、基于组织架构和团队规模,提高团队沟通效率。
5、基于软件包大小,软件包过大,不利用微服务的弹性伸缩。
6、基于不同功能的技术和架构异构以及系统复杂度。

十二、分布式架构设计的关注点

企业一旦采用分布式架构和微服务技术体系,在设计时需要关注商业模式、业务边界、数据体系、微服务设计、前台交互以及多活容灾等多领域的协同。

1、数据是本难念的经
分布式架构下数据面临的问题远比集中式架构复杂。诸如:分布式数据库的选型、数据的分库和分表、数据的同步与异步、跨库和联表查询、数据的分布与集中、在线业务数据与统计分析数据的协同、集中式数据库向分布式数据库的迁移以及面向场景的集中数据复制等。

2.分布式数据库的选择
从集中式架构向分布式架构转型,第一步就需要考虑选择什么样的分布式数据库。
为解决交易型分布式数据库的横向计算能力,目前主要有三种类型的分布式数据库:一体化交易型分布式数据库方案(如阿里 OceanBase 和华为高斯数据库,多采用 Paxos 协议实现多副本数据一致)、单机交易数据库加数据库中间件方案(如腾讯 TDSQL 和 TBase 等,多采用数据同步实现多副本数据一致)和单机交易数据库加分库基础类库(如 ShardingSphere 等,主要实现数据路由和归集)方案。三者的使用场景基本相同,都是通过对大表数据作水平切分,业务请求动态路由到指定节点,以此达到计算能力的线性扩展。一体化方案是以数据库和中间件一体化产品的形式解决线性扩展问题,支持多副本,高可用,提供统一的运维界面。 数据库中间件方案是以独立数据库中间件结合集中式数据库的方式来解决线性扩展问题,高可用功能由中间件和数据库自身功能分别保证。分库基础类库方案是一种类似中间件的轻量级解决方案,适合简单快速的交易操作,在强一致性和聚合分析查询方面较弱。

3.数据的分库和分库主键
选择完分布式数据库后,第二步就需要考虑如何按照领域模型和微服务进行数据库的分库设计,选择合适的分库主键将是一个关键技术点。
对于与客户接触的业务领域,个人认为可以以客户维度作为数据分库主键,以客户为实体,确保所有与本客户接触和服务的数据都在一个单元内,通过集中共享的中台服务,为所有渠道的客户提供一致性体验。如果后序管理流程需要基于区域管理要求,也可以考虑在后序业务环节的数据库中以区域维度作为数据库分库主键,满足业务基于区域的管理要求。
如何将客户维度的数据传输到以区域为维度的数据库中们可以考虑基于消息队列的事件驱动模型。
系统如果做不到“以客户为中心”,又如何能实现“以客户为中心”的业务需求呢/p>

4.高频热点数据的缓存
对于像产品基础数据、主数据之类的热点高频访问数据,在进行系统设计时需考虑将这些数据加载至缓存中,降低数据库的压力,对外提供高性能的数据访问能力。
缓存技术的使用就像调味料一样,投入小见效快,用户体验提升快。

5.数据副本与跨库联表查询
采用分布式技术后,数据将碎片化,为了减轻由于跨库以及联表查询给分布式数据库的压力,需要建立多维的全局数据视图(如客户统一视图、业务统计数据视图等)和面向具体场景的预处理好的数据聚合副本,提供复杂场景的数据查询服务,减轻交易型数据库的压力。
全局数据视图其数据来源于各业务条线的分布式数据库,从源端分布式数据库通过准实时的方式汇集(可以基于数据库日志捕获技术加消息队列)。全局视图的数据库也可以是分布式数据库,根据业务要求选择合适的分库主键进行数据重分布。
对于分布式数据库跨库关联查询性能低的问题,有两种解决方案,根据具体场景采用合适的方案:
1)面向场景的数据副本查询库。将这些需要关联查询的数据副本集中存放在一个分布式数据库中。在进行数据汇集时,提前做好数据关联处理(如多表数据合并成一个宽表),通过查询微服务,专职提供关联查询服务。
2)小表广播模式。有些业务场景中少量表(如用户、机构表等)需要跟业务数据进行关联查询,这种场景可以考虑在业务数据库中新建一张复制表(无需全部字段,取必要字段即可),在主表发生变化时,可以通过发布订阅的消息队列模式刷新复制表的数据,保证数据的一致性。

6.合理的数据冗余
完成领域模型和微服务设计后,集中式数据库的数据将被分散到不同微服务的分布式数据库中。数据实体的依赖关系将被打破,如果需要调用前序或后序微服务的数据实体(如:投保微服务生成的投保单、保单管理微服务的保单需要关联投保单,理赔的报案需要关联保单等,或电商业务中:销售过程中的商品、运输过程中的货物需要关联商品信息),这时候就会跨库或者跨微服务调用了,必然影响系统性能。
如何处理这些跨微服务的关键实体数据br> 最好的方式就是数据冗余,将前序或后序环节的关键数据以数据清单复制表(只需必要的关键数据,不需要所有明细数据)的方式冗余存储。冗余的好处是,前台页面可以一次性获取本领域实体数据和关联实体清单数据,同时也可以在本库对关联清单数据进行查询。只有在需要获取关联实体数据明细时,才调用前序或后续微服务获取全量数据。
合理的数据冗余可以减少跨库查询,提升系统性能。

7.如何数据迁移br> 从集中式数据库向分布式数据库切换时,数据迁移的复杂度将大大增加。需要考虑如何进行数据迁移有技术条件下,是不是不做数据迁移也可以无缝切换br> 传统集中式架构数据多集中在一个集中式数据库中,数据关联度高。
分布式架构下,数据会随着微服务而同步拆分,数据将变得碎片化,存在复制表,数据重分布,数据关联被打破,甚至还可能需要重建数据关联。另外,分布式架构的容灾和多中心多活要求,数据迁移时还需要考虑数据的多副本和多中心的数据复制。分布式架构下数据迁移的复杂度大增。
互联网公司大多采用演进式架构模式,有计划分阶段的进行技术体系的升级,很多时候用户无感知就完成了架构的升级。而传统企业在做技术升级时如采用绞杀者重构模式,是否必须要做数据迁移果不做数据迁移是否也可以顺利切换否通过数据路由加全量数据视图的方案就可以不做数据迁移,实现新旧并存,无缝切换据切换方案需要详细设计和慎重考虑(尚在考虑中,且听下回分解)。

8.数据的异步和同步
分布式架构下事件驱动设计模式是常用的方法,通过基于消息队列的发布订阅模式,可以很好的实现业务异步化。非实时业务场景可以采用事件驱动的模式实现异步化,减轻数据库压力。
也可以通过异步模式实现准实时的数据读写分离,提高数据库性能。

9、中台和微服务要处理好边界
条条道路通罗马,不管走哪条路,凭感觉或拍脑袋也可以设计出微服务,拆分结果可能与按照 DDD 方法出来的结果类似。但是如果有好的理论和方法指导,不但做事情有矩可循的,而且可以避免走弯路。由于 DDD 在设计的时候已经做好了逻辑的边界划分,在微服务需要组合和重新拆分时也会变得容易得多。
还是有必要提一下:中台和微服务设计可以借鉴 DDD 的设计原则和理念,不过战术设计部分由于过于复杂和学习成本过高,可以参考使用。

10、前、中台协同和前台数据的按需加载
前台应用未来可能多采用单页面(SPA)的微前端(对应于微服务的前端展现,一个微服务对应一个微前端)方式,通过前端集成框架(类似门户)实现多页面组合,提供统一的用户体验,在微服务和数据库设计时也需要协同考虑前端页面逻辑。
为减轻跨微服务的访问,前端页面展示时应以清单数据方式按需加载,后端数据设计时也应同步考虑如何组合前端数据展示。如需要展示明细数据,通过调用 API 服务的方式获取全量数据,减少不必要的跨微服务调用。
另外,符合条件的应用也可考虑页面的动静分离和路由接入,将静态页面通过 CDN 的技术,部署在靠近用户的机房,降低交互次数,减少跨广域网访问带来的网络延迟。
前端知识有限,就写这么多了,哈哈。

11、容灾和多活的全局考虑
分布式架构的高可用是在应用、数据和基础设施的分布式技术升级后,通过多数据中心协同来实现的。
为了容灾和多活,在设计方面需要考虑:1)合适的分布式数据库。2)合理的数据分库主键设计,数据的多副本和同步技术。3)单元化架构设计,处理好通用中台和专属中台的部署和依赖关系,实现业务的自包含,减少跨数据中心调用。4)访问层的接入,对外部访问进行路由、限流以及灰度发布。5)统一的全局配置数据,每个数据中心都有实时同步的全量配置数据,实现容灾和多活的一键切换。

12、避免过度拆分和硬件依赖
过度过细的微服务拆分带来更多的软件维护成本和运维压力,过多的分布式事务也会带来性能和数据一致性的压力。在进行设计时,要在保证逻辑边界清晰的情况下,严控微服务的过度拆分和采用过多的分布式事务。
分布式架构的自动的弹性伸缩大多是通过软件的方式去实现的,为保证应用的弹性伸缩能力,在设计中应实现去硬件的无中心化(如可采用软负载,就不用 F5 之类的硬负载),尽量通过软件实现弹性伸缩。因为一旦绑定硬件设备,在硬件遇到瓶颈需要自动弹性伸缩的时候,就需要人工干预,无法自动弹性伸缩。

写在最后

正如老马说的采用微服务的企业需具备一定的高度,如文化、组织和技术,DDD 同样也需要站一定的高度。如果高度不够,我们是否可以站在巨人的肩上呢br> 在领域模型和微服务设计时,守住领域模型和边界,各司其职,才能长治久安!
谨记:边界!边界!边界!

文章知识点与官方知识档案匹配,可进一步学习相关知识云原生入门技能树服务网格(istio)ServiceMesh介绍8727 人正在系统学习中

来源:紫罗兰盛开

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年1月20日
下一篇 2020年1月20日

相关推荐