SOA架构和微服务架构以及领域驱动设计 - Go语言中文社区

SOA架构和微服务架构以及领域驱动设计


一,主流架构模型SOA架构和微服务架构

1.1 SOA架构

  SOA 全称(Service Oriented Architecture),中文意思为“面向服务的架构”,他是一种设计方法,其中包含多个服务,服务之间通过相互依赖最终提供一系列的功能。一个服务通常以独立的形式存在与操作系统进程中。各个服务之间通过网络调用。跟 SOA 相提并论的还有一个 ESB(企业服务总线),简单来说 ESB 就是一根管道,用来连接各个服务节点。为了集成不同系统,不同协议的服务,ESB 做了消息的转化解释和路由工作,让不同的服务互联互通。

SOA 所解决的核心问题
1. 系统集成:站在系统的角度,解决企业系统间的通信问题,把原先散乱、无规划的系统间的网状结构,梳理成规整、可治理的系统间星形结构,这一步往往需要引入一些产品,比如 ESB、以及技术规范、服务管理规范;这一步解决的核心问题是【有序】。
2. 系统的服务化:站在功能的角度,把业务逻辑抽象成可复用、可组装的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用
的业务服务,实现业务逻辑的快速复用;这一步解决的核心问题是【复用】。
3. 业务的服务化:站在企业的角度,把企业职能抽象成可复用、可组装的服务;把原先职能化的企业架构转变为服务化的企业架构,进一步提升企业的对外服务能力;“前面两步都是从技术层面来解决系统调用、系统功能复用的问题”。第三步,则是以业务驱动把一个业务单元封装成一项服务。这一步解决的核心问题是【高效】。

1.2 微服务架构

微服务架构其实和 SOA 架构类似,微服务是在SOA上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。

 组件表示一个可以独立更换和升级的单元,就像PC中的CPU、内存、显卡、硬盘一样,独立且可以更换升级而不影响其他单元。如果我们把PC作为组件以服务的方式构建,那么这台PC只需要维护主板和一些必要的外部设备。CPU、内存、硬盘都是以组件方式提供服务,PC 需要调用 CPU 做计算处理,只需要知道 CPU 这个组件的地址即可。

微服务的特征:
1. 通过服务实现组件化
2. 按业务能力来划分服务和开发团队
3. 去中心化
4. 基础设施自动化(devops、自动化部署)

1.3 SOA架构和微服务架构的差别

  1. 微服务不再强调传统SOA架构里面比较重的 ESB 企业服务总线,同时 SOA 的思想进入到单个业务系统内部实现真正的组件化。
  2. Docker 容器技术的出现,为微服务提供了更便利的条件,比如更小的部署单元,每个服务可以通过类似 Node或者 Spring Boot 等技术跑在自己的进程中。
  3. SOA 注重的是系统集成方面,而微服务关注的是完全分离。

二,领域驱动设计及业务驱动划分

2.1 领域驱动设计的概念

 领域驱动设计(DDD,Domain-Driven Design),软件开发不是一蹴而就的事情,我们不可能在不了解产品(或行业领域)的前提下进行软件开发,在开发前,通常需要进行大量的业务知识梳理,然后才到软件设计的层面,最后才是开发。而在业务知识梳理的过程中,我们必然会形成某个领域知识,根据领域知识来一步步驱动软件设计,就是领域驱动设计的基本概念。

2.2 为什么需要DDD

  业务初期,功能大都非常简单,普通的 CRUD 就能满足,此时系统是清晰的。随着产品不断迭代和演化,业务逻辑变得越来越复杂,我们的系统也越来越冗杂。各个模块之间彼此关联,甚至到后期连作者都很难说清模块的具体功能意图是啥。导致在修改一个功能时,要追溯到这个功能需要的修改点就需要很长时间,更别提修改带来的不可预知的影响面。比如说:
这里写图片描述

  订单服务接口中提供了查询、创建订单相关的接口,也提供了订单评价、支付的接口。同时订单表是个大表,包含了非常多字段。在我们维护代码时,将会导致牵一发而动全身,很可能只是想改下评价相关的功能,却影响到了创建订单的核心路径。虽然我们可以通过测试来保证功能完备性,但当我们在订单领域有大量需求同时并行开发时,改动重叠、恶性循环、疲于奔命修改各种问题。

  绝大部分公司都是这样一个状态,然后一般的解决方案是不断的重构系统,让系统的设计随着业务成长也进行不断的演进。通过重构出一些独立的类来存放某些通用的逻辑解决混乱问题,但是我们很难给它一个业务上的含义,只能以技术纬度进行描述,这个带来的问题就是其他人接手这块代码的时候不知道这个的含义或者可以通过修改这块通用逻辑来达到某些需求。

2.3 领域模型追本溯源

  其实领域模型本身就不是一个陌生的单词,说直白点,在早期领域模型就是数据库设计.我们做传统项目的流程或者说包括现在我们做项目的流程,都是首先讨论需求,然后是数据库建模, 在需求逐步确定的过程不断的去更新数据库的设计。接着我们在项目开发阶段,发现有些关系没有建、有些字段少了、些表结构设计不合理,又在不断的去调整设计。最后上线。在传统项目中,数据库是整个项目的根本,数据模型出来以后后续的开发都是围绕着数据展开;然后形成如下的一个架构:
这里写图片描述

  1. service 很重,所有逻辑处理基本都放在 service 层。
  2. POJO()作为 service 层的非常重要的一个实体,会因为不同场景的需求做不同的变化和组合,就会早成POJO的几种不同模型(失血、贫血、充血),用来形容领域模型太胖或者太瘦。

  随着业务变得复杂以后,包括数据结构的变化,那么各个模块就需要进行修改,原本清晰的系统经过不断的演化变得复杂、冗余、耦合度高。后果就非常严重。我们试想一下如果一个软件产品不依赖数据库存储设备,那我们怎么去设计这个软件呢?如果没有了数据存储,那么我们的领域模型就得基于程序本身来设计。那这个就是 DDD 需要去考虑的问题。

2.4 以抽奖设计为例来DDD

  DDD 理解起来有点抽象, 这个有点像设计模式,感觉很有用,但是不知道怎么应用到自己写的代码里面,或者生搬硬套最后看起来又很别扭,那么接下来以一个简单的转盘抽奖案例来分析一下 DDD 的应用。
1. 针对功能层面划分边界
这个系统可以划分为运营管理平台和用户使用层,运营平台对于抽奖的配置比较复杂但是操作频率会比较低。而用户对抽奖活动页面的使用是高频率的但是对于配置规则来说是误感知的,根据这样的特点,我们把抽奖平台划分针对 C 端抽奖和 M 端抽奖两个子域在确认了 M 端领域和 C 端的限界上下文后,我们再对各
自上下文内部进行限界上下文的划分,接下来以 C 端用户为例来划分界限上下文.

  1. 确认基本需求
      首先我们要来了解该产品的基本需求:
    1. 抽奖资格(什么情况下会有抽奖机会、抽奖次数、抽奖的活动起始时间)
    2. 抽奖的奖品(实物、优惠券、理财金、购物卡…)
    3. 奖品自身的配置,概率、库存、某些奖品在有限的概率下还只能被限制抽到多少次等。
    4. 风控对接, 防止恶意薅羊毛。
  2. 针对产品功能划分边界
    这里写图片描述
    抽奖上下文是整个领域的核心,负责处理用户抽奖的核心业务。
    1. 对于活动的限制,我们定义了活动资格的通用语言,将活动开始/结束时间,活动可参与次数等限制条件都收拢到活动资格子域中。
    2. 由于 C 端存在一些刷单行为,我们根据产品需求定义了风控上下文用于对活动进行风控。
    3. 由于抽奖和发放奖品其实可以认为是两个领域,一个负责根据概率去抽奖、另一个负责将选中的奖品发放出去。所以对于这一块也独立出来一个领域。
  3. 细化上下文
      通过上下文划分以后,我们还需要进一步梳理上下文之间的关系,梳理的好处在于:

    1. 任务更好拆分(一个开发人员可以全身心投入到相关子域的上下文中)。
    2. 方便沟通,明确自身上下文和其他上下文之间的依赖关系,可以实现更好的对接。

        然后是基于上下文的更进一步细化建模,在 DDD 中存在一些名字定义。

      • 实体:当一个对象由其标识(而不是属性)区分时,这种对象称为实体(Entity)。
      • 值对象:当一个对象用于对事物进行描述而没有唯一标识时,它被称作值对象。
      • 聚合根:聚合根属于实体对象,它是领域对象中一个高度内聚的核心对象。(聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识, 值对象没有唯一标识,不存在这个值对象或那个值对象的说法)。
      • 领域服务:一些重要的领域行为或操作,可以归类为领域服务。它实现了全部业务逻辑并且通过各种校验手段保证业务的正确性。
      • 资源库:资源封装了基础设施来提供查询和持久化聚合操作。这样能够让我们始终关注在模型层面,把对象的存储和访问都委托给资源库来完成。他不 是数据库的封装,而是领域层与基础设施之间的桥梁。DDD 关心的是领域内的模型,而不是数据库的操作。
  4. 代码设计
      在实际开发中,我们一般会采用模块来表示一个领域的界限上下文,比如:

    • com.mko.bussiness.lottery.*;//抽奖上下文
    • com.mko.bussiness.riskcontrol.*;// 风控上下文
    • com.mko.bussiness.prize.*;//奖品上下文
    • com.mko.bussiness.qualification.*;// 活动资格上下文
    • com.mko.bussiness.stock.*;//库存上下文
        对于模块内的组织结构,一般情况下我们是按照领域对象、领域服务、领域资源库、防腐层等组织方式定义的。
    • com.mko.bussiness.lottery.domain.valobj.*;//领域对象-值对象
    • com.mko.bussiness.lottery.domain.entity.*;//领域对象-实体
    • com.mko.bussiness.lottery.domain.aggregate.*;//领域对象-聚合根
    • com.mko.bussiness.lottery.service.*;//领域服务
    • com.mko.bussiness.lottery.repo.*;//领域资源库

2.5 领域驱动的好处

  用DDD可以很好的解决领域模型到设计模型的同步、演进最后映射到实际的代码逻辑。总的来说,DDD 有几个好处:
1. DDD 能够让我们知道如何抽象出限界上下文上下文以及如何去分而治之.
- 分而治之:把复杂的大规模软件拆分成若干个子模块,每一个模块都能独立运行和解决相关问题。并且分割后各个部分可以组装成为一个整体。
- 抽象:使用抽象能够精简问题空间,而且问题越小越容易理解,比如说我们要对接支付,我们抽象的纬度应该是支付,而不是具体的微信支付还是支付宝支付。
2. DDD 的限界上下文可以完美匹配微服务的要求在系统复杂之后,我们都需要用分治来拆解问题。一般有两种方式,技术维度和业务维度。技术维度是类似MVC 这样,业务维度则是指按业务领域来划分系统。微服务架构更强调从业务维度去做分治来应对系统复杂度,而DDD也是同样的着重业务视角。

2.6 领域驱动的总结

 领域驱动设计其实我们可以简单认为是一种指导思想,是一种软件开发方法,通过DDD可以将系统解构更加合理,最终满足高内聚低耦合的本质。在我的观点来看,有点类似数据库的三范式,我们开始在学的时候并不太理解,当有足够的设计经验以后慢慢发现三范式带来的好处。同时我们也并不一定需要严格按照这三范式去进行实践,有些情况下是可以灵活调整。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/lj1314ailj/article/details/80919955
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-06-26 17:20:18
  • 阅读 ( 1310 )
  • 分类:架构

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢