微服务与领域驱动设计,架构实践总结

怎样的架构才能配得上造到飞起的变化?

一、软件复杂性

1、复杂原因

如果软件系统存在持续的迭代周期,那么其中业务、技术、架构的复杂性都会直线拉升,其相应的开发难度也会提高,可以用一句话总结其根本原因:唯一不变的就是变化;

微服务与领域驱动设计,架构实践总结
  • 业务变化:导致复杂性的根本原因,在多端多版本适配的过程中代码快速膨胀;
  • 数据变化:数据随着业务的变化和发展,不断沉淀积累,需要做横向与纵向的管理;
  • 技术升级:技术组件可能因为漏洞,或者更好的解决问题,不间断升级版本;
  • 人员变动:模块的开发人员一旦出现流动,换人接手会给代码带来风格上的差异;
  • 心态起伏:持续应对复杂问题,但平稳的心态很难持续,也是人员流动的一个因素;

应对复杂的变化一直都是软件工程的核心难点问题,如何用较小的架构变化应对较大的业务变化,就是设计中常说的:高内聚、低耦合;还需要补充很重要的一点:单从技术层面是无法持续解决复杂问题的,还需要从管理角度去定义流程标准,规范各种解决方案,是整个部门要持续面对的事项。

2、应对复杂

不管是常说的设计模式、原则、面向对象,还是架构中常用的集群、微服务、领域驱动等,都是在寻求更合理的方案来应对业务的变化;但是没有一劳永逸的解决方法,既要做一定前瞻性的设计去预期业务,同时还要避免过度的设计影响业务进度;这就需要研发团队具备一定的业务高度和技术深度:

微服务与领域驱动设计,架构实践总结

在系统落地的过程中,需要对业务深入的分析和理解,不断优化技术层面的解决方案;比如微服务的思想是通过拆分的手段实现业务块之间的低耦合,领域驱动设计则实现各个业务逻辑的高内聚;下面围绕两种方式的实践去详细分析。

二、微服务架构

1、架构设计

系统的架构设计是一件极度复杂的事情,在工作的这几年大致经历过如下几个阶段:单服务、多服务集群、微服务、持续集成;在近2年比较稳定的选型是微服务+自动化集成的模式:

微服务与领域驱动设计,架构实践总结

思考其本质的变化逻辑,即为了应对更复杂的业务体系;不管是业务拆分还是模型设计,都是在不断实现 高内聚低耦合的原则;降低业务之间的关联影响,分离业务和技术的高度耦合。

2、业务场景

这里先来看一个经典的业务场景:电商交易;基于微服务架构的电商交易场景中,通常至少会涉及如下几个核心服务:交易、账户、订单、商品、仓储、物流;

微服务与领域驱动设计,架构实践总结

站在业务角度,进行模块化拆分和管理,结合持续集成的组件,通常可以轻松的应对各种复杂的业务场景,但是不存在真正意义上一劳永逸的手段,业务变化带来的各种问题总会无脑推动开发去寻找更合理的解决方案;

微服务与领域驱动设计,架构实践总结

在一次完整的电商交易场景中,实际上真正涉及到的微服务远不止图中的几个,在Trade服务中交织关联多个其他服务,在MVC的分层管理下,初期并不会存在较大风险,但是业务一旦经过多版升级改造之后,并且还存在版本兼容的要求,会给人一种极度混乱和不踏实的感觉;

如果团队成员的综合能力较高,并且版本有充足的时间去设计和优化,这种问题是可以妥善解决的,如果出现时间紧任务重的情况,随之而来的 压力会持续在开发和测试之间来回横跳

解决过相关业务场景的研发都知道,重构加持续集成能力,结合严谨的测试,可以应对业务的不断变化;但是在版本兼容的过程中,依然会导致工程中的代码膨胀到飞起,特别是出现中场换人的情况,都会让接手的人员在被埋和离开中,产生一次剧烈的心态挣扎。

3、问题分析

在MVC的架构模式中,工程通常会进行如下的分层管理:控制层、服务层、持久层、存储层;服务层在特定复杂的场景中会做细化拆分,比如第三方对接、常用中间件的二次封装:

微服务与领域驱动设计,架构实践总结

对于在复杂业务线上争渡的选手来说,对Mvc分层模式的缺陷是深有体会的,Service层聚焦大量复杂的逻辑,通常核心业务块中总会存在几个代码过千行的实现逻辑,不管用什么思路和模式去拆分封装,都很难解决该层不断扩展带来的膨胀问题。

4、面向过程

在MVC分层中,过程式的代码极其明显,通常以数据库表和关系为基础,映射构建相关实体对象,这些实体对象并没有具体的行为和逻辑,只是作为数据和结构的载体:

微服务与领域驱动设计,架构实践总结

从面向对象中类的定义去看:属性和行为;而在MVC模式中,绝大多数实体都只是作为数据的入参出参的结构定义,可以理解为数据容器,在MVC的各层之间不断搬运和加工。

三、领域驱动设计

相比MVC的分层设计,领域驱动设计(Domain-Driven-Design简称DDD)对于复杂业务系统的实现,提出了更加合理的解决方案,DDD模式中涉及大量专业术语和抽象概念,可以参考 EricEvans的相关书籍,本文只描述实践中的核心概念。

1、分离模式

DDD模型在分层设计上,划分出核心的四层:接入层、应用层、领域层、基础设施层;注意这里只是单纯站在服务端的常规架构角度去看,很明显分离MVC模式中的服务实现层的逻辑:

微服务与领域驱动设计,架构实践总结

其中领域层是关键所在,用来封装复杂的业务,对应用层提供业务管理的核心支撑;整个模型也更具备纵向思维,有效的缓解单层复杂度过高的现象;单从模型设计上看,在工程中基于该分层去管理代码包,也可以使每层的设计更加清晰和独立。

2、设计思想

领域驱动设计并不是简单的分层管理模型,涉及诸多抽象逻辑与专业术语,例如:领域、界限上下文、实体、聚合、值对象等等;

2.1 领域

领域可以理解为业务场景中需要解决的问题合集,是具有范围和边界的约束;领域可以拆分多个子域,通常描述为:核心域、支撑域、通用域:

微服务与领域驱动设计,架构实践总结

关于子域的划分也是参考业务属性,可以把核心域理解为最关键的业务场景,并且需要资源倾斜以应对其不断的发展;支撑域可以理解为相对稳定的业务;通用域偏向系统架构层面的公共能力;通过对领域的拆分实现业务分治,这与微服务的拆分思想相符合,两种模式在业务角度是比较统一的;

2.2 界限上下文

DDD中最晦涩难懂的一个抽象概念,特定模型的限界应用,不过可以借用原文的比喻会意一下:细胞之所以能够存在,是因为细胞膜限定了什么在细胞内,什么在细胞外, 并且确定了什么物质可以通过细胞膜:

微服务与领域驱动设计,架构实践总结

界限上下文的定义涉及粒度的思想,即每个粒度要具备独立性;如上图仓储业务,可以将服务部署与仓储子域、仓储上下文做成一一对应的关系,或者在仓储子域中分别定义:仓库和货架两个上下文;这里具有极大的灵活性,没有真正意义上的标准可以参考。

2.3 映射关系

做好界限上下文的划分,理清各个上下文之间的关系,明确业务场景中的依赖顺序,这样可以更好的推动开发流程的落地;对于上下文的关系描述也远不止图中的这些,还有共享内核、合作等等:

微服务与领域驱动设计,架构实践总结
  • 上下游(U-上游,D下游):描述上下文调用时的关系,服务调用方为D,服务提供方为U;
  • 防腐层(Anticorruption-Layer,简写ACL):上下文交互时封装的一层,提供对动作的校验、适配、转换等;
  • 开放主机服务,发布语言(Open-Host-Service简写OHS,Published-Language简写PL):定义访问协议;

在上下文交互时,防腐层可以维护上下文的隔离和独立,确保调用方不直接依赖服务提供方,从而实现不同上下文之间的依赖解耦;同时这也会带来大量的对象转换动作;

2.4 建模设计

子域和界线上线文完成对业务的拆分切块,从而进行分治;基于防腐层降低各个界限上下文的耦合程度;聚合思想保证了业务问题的解决方案内聚;严格的分层模型实现服务支撑能力的分散;

微服务与领域驱动设计,架构实践总结
  • 防腐层(Anticorruption-Layer):上下文交互时封装的一层;
  • 领域层(Domain-Layer):在分层架构中负责领域逻辑的设计和实现;
  • 领域服务(Domain-Service):行为无法识别归属的实体时,封装到领域服务;
  • 聚合(Aggregate):相关对象的集合,描述核心领域,通常把聚合作为数据修改的单元;
  • 实体(Entity):通过标识来定义的对象,而不是基于属性,比如Uid标识用户实体;
  • 值对象(Value-Object):描述特征或属性但没有标识的对象;
  • 工厂(Factory):封装对象复杂的创建逻辑与类型;
  • 存储库(Repository):把存储、缓存、搜索等资源封装的机制,对应领域模型;

领域模型的核心追求目标:高内聚、低耦合;更加抽象的、复杂的设计思想,也同样意味着落地实现的难度更高,但不可否认领域模型作为复杂业务的解决方案,逻辑上的确更加合理。

3、工程实践

领域模型在代码工程的实践中,可以将不同的子域集成到各自的服务中,也可以在一个服务中,通过多个模块(Module)进行隔离维护,即一个模块对应一个界限上下文;

微服务与领域驱动设计,架构实践总结

将业务问题进行分模块分层分包的方式进行隔离,是代码工程中的基本手段,这里只是对组织方式进行描述,在实际的开发中,要根据依赖顺序进行类库拆包管理;

在程序的执行过程中,并不是所有的交互命令都需要经过领域层,实际上大部分业务中的查询命令都是超过增删改命令的,所以在纯读取数据的请求中,应用层可以绕开领域层直接访问基础设施层,减少一层数据处理逻辑。

四、实践总结

最后来讨论一些架构实践的经验,随着技术的不断发展和更新换代,为解决业务问题提供了极大的便利,不管是单服务中各种成熟的组件,又或者分布式中的微服务体系,或者聚焦业务管理的领域模型;每种架构选型都有其适用的场景,不同的选型意味着不一样的实现成本;

实际上在做架构选型时,成熟有经验的主导者,都极其擅长做折中处理,也就是常说的退一步海阔天空;通常需要考虑团队的综合水平与业务需求和产品设计,当然在实际的协作流程中多方都是需要相对让步的,但是对质量的要求以及核心业务的实现逻辑上是不能打折的。

五、参考源码

编程文档:
https://gitee.com/cicadasmile/butte-java-note

应用仓库:
https://gitee.com/cicadasmile/butte-flyer-parent

Original: https://www.cnblogs.com/cicada-smile/p/16188648.html
Author: 知了一笑
Title: 微服务与领域驱动设计,架构实践总结

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/610801/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 文件批量改名(有规律)

    1.如你的文件放在桌面名字为file的文件内,我要把这些文件批量名称改为page1.jpg,page2.jpg,page3.jpg………. 2….

    Linux 2023年6月13日
    090
  • 手把手教你搭建一个SpringBoot工程

    手把手教你搭建一个SpringBoot工程 首先进入Spring官网,下载Demo工程 像这样设置好 点击下载 将工程导入Idea并打开 在 application.propert…

    Linux 2023年6月14日
    0123
  • Linux(进阶篇)

    一、进程 1 进程和内存管理 1.1 进程和线程的区别 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路…

    Linux 2023年6月7日
    093
  • 银河麒麟KYLIN安装wireshark进行抓包

    银河麒麟KYLIN安装wireshark进行抓包(前提是网络连通):sudo apt-get updatesudo apt-get install wireshark -y弹出框选…

    Linux 2023年6月13日
    0125
  • 安卓开发封装处理Retrofit协程请求中的异常

    上篇文章讲解了怎么使用 Kotlin的协程配合 Retrofit发起网络请求,使用也是非常方便,但是在处理请求异常还不是很人性化。这篇文章,我们将处理异常的代码进行封装,以便对异常…

    Linux 2023年6月8日
    0108
  • 相关powerLink教程、配置方法等

    openPowerLink的开发小组早已经解散,所以有些资料都可以在官网上下载到; 这也是最后一次更新了。其中相关powerlink的教程均放在百度网盘里,链接:https://p…

    Linux 2023年6月14日
    0110
  • 我懂得了什么

    站在24岁的这个档口,没有学业又没有工作的时刻,前不着村后不着店。我觉得应该要写一些总结,虽然年仅24的我相比老人家总结不出更深刻的道理,但是现在是互联网的时代,获取知识的途径已经…

    Linux 2023年6月6日
    0104
  • 国产化之Arm64 CPU+银河麒麟系统安装.NetCore

    背景 某个项目需要实现基础软件全部国产化,其中操作系统指定银河麒麟,银河麒麟就是一个Linux发行版,数据库使用达梦V8,这个数据库很多概念和Oracle相似,CPU平台的范围:龙…

    Linux 2023年5月27日
    086
  • Python中的对象引用、浅拷贝与深拷贝

    最近项目中遇到一个Python浅拷贝机制引起的bug,由于对于Python中对象引用、赋值、浅拷贝/深拷贝机制没有足够的认识,导致调试了很久才发现问题,这里简单记录一下相关概念。 …

    Linux 2023年6月6日
    085
  • 容器编排与Kubernates

    1 基本概念 1.1 K8S优势 容器调度、容器管理、容器编排、容器集群管理工具。Google开源,自动化部署。支持弹性收缩、负载均衡。 1.2 K8S在Devops中的角色 ; …

    Linux 2023年6月13日
    0107
  • redis编译安装

    redis是一个强大的NoSQL数据库,相对于memcached,他提供了更丰富的数据类型,有string、hash、list、set、sorted set这几种类型;还支持数据持…

    Linux 2023年5月28日
    0103
  • Redis连接出现Error: Connection reset by peer的问题是由于使用Redis的安全模式

    现在网上一查出现安全模式的连接,基本都是要关闭服务端的操作,其实这种方式是不正确的,最有效的解决方式是使用stunnel进行安全模式的连接。 我碰到的问题是微软云(其实我不想用!)…

    Linux 2023年5月28日
    0100
  • 2-第一个Django程序

    第一个Django程序 从本章节开始将通过实现一个投票应用程序,来让用户逐步的了解Django。这个程序由两步分组成: 公共站点,允许用户访问进行投票,和查看投票。 站点管理,允许…

    Linux 2023年6月7日
    0114
  • 使用github action发布hexo博客到云服务器

    > node -v v16.15.0 > npm -v 8.5.5 安装Hexo CLI 在你的&am…

    Linux 2023年6月7日
    0113
  • WPF 调试依赖属性变更方法

    本文告诉大家如何调试 WPF 的某个依赖属性被变更的方法 在 WPF 里面,所有的依赖属性都有带通知的功能,通过带通知的功能,可以在通知里加上断点,通过调用堆栈了解是哪个模块调用的…

    Linux 2023年6月6日
    095
  • 总结:弹性伸缩的五个条件与六个教训

    前言弹性伸缩是云计算时代给我们带来的一项核心技术红利,但是 IT 的世界中,没有一个系统功能可以不假思索的应用到所有的场景中。这篇文章,我们将应用企业级分布式应用服务-EDAS 的…

    Linux 2023年6月8日
    0102
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球