时间:2024-04-25
蒙亮 胡俊杨
[摘 要] 十三五期间,随着电网信息化支撑业务领域的广度和深度不断增强,传统IT架构如何适应“互联网+电网”形势下的战略转型,成为电网信息化管理过程中的迫切需求和需要思考的问题。本文基于许多大型电网企业多年来的架构应用实践,详细阐述了如何以领域驱動设计(简称:DDD)为指导思想,对复杂的应用系统进行服务拆分。
[关键词] IT架构;领域驱动;服务拆分
doi : 10 . 3969 / j . issn . 1673 - 0194 . 2018. 21. 033
[中图分类号] F270.7;TP393.09 [文献标识码] A [文章编号] 1673 - 0194(2018)21- 0085- 03
1 前 言
1.1 背景与意义
随着网络安全法的发布实施、大数据、云计算、移动互联网、物联网、人工智能等新技术发展日新月异,供给侧结构性改革、电力体制改革、国资国企改革、“一带一路”、“互联网+”等新政策的不断推进,许多电网企业的发展处于重要战略机遇期、转型升级期、改革深化期。内外部环境的深刻变化,直接对电网企业信息化建设提出了新要求。大多数电网企业需要信息化手段来支撑主营业务的深化改革及竞争型业务的创新发展。
IT架构升级是大型电网企业推进信息化建设过程中必不可少的环节之一,它除了需要充分考虑业务转型与新技术应用的发展外,还涉及基础设施、运行时环境、服务框架、数据访问框架、工作流程、工作台和云管理等基础性软硬件平台和关键技术的整合应用。关于服务如何抽象,应用系统架构如何进行应用化改造,是IT架构升级过程中的关键问题。
1.2 主要工作
本文针对IT服务架构中最为关键的服务抽象设计,引入领域驱动设计技术,实现服务和组件的拆分。主要的研究内容包含以下几个方面。
(1)战略设计模式理论包括:识别领域、划分子域、寻找核心域、支撑域和通用域、划定限界上下文和建立上下文映射图。战略设计模式是系统的业务框架设计,保证了领域模型的完整性。
(2)战术设计模式理论主要包括:实体、值对象、聚合、识别领域事件等。战术模式解决了领域的最小业务单元高内聚[1]。
2 技术发展分析
自Eric Evans提出新的软件开发方法DDD后,在国内外出现许多DDD理论的研究者和实践者。
国外有许多专家对DDD的发展做出了卓越贡献,例如Greg Young对Betrand Meyer的CQS模式进行改造,提出CQRS模式,Vaughn Verno完善了DDD思想,正式提出了领域事件模式以及六边形架构风格实现DDD。
随着国内的互联网公司逐渐深入实体经济,业务日益复杂,领域模型在一个复杂的体系里变得尤为重要。国内的盒马鲜生已经成功将DDD运用到实际的开发中,在互联网架构下完整实施具有阿里特点的领域驱动设计模式。目前已经有许多开发人员尝试着将DDD应用到银行、航空、物流及信息系统等项目中,随着云计算、微服务框架等新技术发展完善,DDD将有更多的应用场景。
3 必要性分析
“十二五”期间,许多电网企业信息化处于大建设期,与应用系统的大规模建设的现实相适应,电网企业选择成熟稳定、为人熟知、IDE友好、便于共享、易于开发、部署和测试的单块架构,采用传统软件开发方法开展企业级应用建设;上述模式在大建设期充分发挥了其自身特点,促进了电网企业应用建设的迅速开展。
随着业务的不断扩大,需求功能的持续增加,单块架构已经很难满足业务快速变化的需要。一方面,代码的可维护性、扩展性、灵活性在降低;而另一方面,系统的测试成本、构建成本以及维护成本却在显著增加。因此,随着项目或者产品规模的不断扩大,单块架构应用的改造与重构势在必行。
4 规划方案
垂直拆分:依据领域驱动理念,收集资料,了解系统背景,识别领域通用语言。识别领域,划分子域,寻找核心域、支撑域和通用域,围绕这些子域划定限界上下文,建立上下文映射图。以战略模型为基石,进行战术建模:识别实体、值对象,划分聚合,识别领域事件。战术的成果帮助概念分离,分析设计原因和领域特征,厘清模型内对象的关系、各种业务规则、数据一致性的设计以及测试用例的设计。
领域服务、仓储和工厂的设计,帮助分离服务。以“低内聚,高耦合”为原则,建立服务与服务之间的关系,实现事务的一致性。聚合内的服务要求事务高度一致,关键聚合根的扭转实现了动态模型的设计。设计是否符合需求的场景要求,通过场景走查检验一切的设计符合度和用户体验度。
其总体设计如下图所示:
4.1 资料收集
明确背景和定位,收集业务需求描述,绘制用例及核心业务流程图。
4.2 识别通用语言
领域驱动设计的一个核心的原则是使用一种基于模型的语言。使用模型作为语言的核心骨架,要求团队在进行所有的交流中都使用一致的语言,在代码中也是这样。在共享知识和推敲模型时,团队会使用演讲、文字和图形。这儿需要确保团队使用的语言在所有的交流形式中看上去都是一致的,这种语言被称为“通用语言”。
4.3 战略建模
战略建模是从宏观的角度来划分和集成限界上下文,它包括领域识别,子域及限界上下文划分(分析出子域、核心域、支撑域)、上下文映射图。
4.3.1 识别领域
一个领域本质上就是一个问题域,只要是同一个领域,那问题域就相同。只有确定系统所属的领域,才能进而了解系统的核心业务,即要解决的关键问题、问题的范围边界。
一个领域内可以包含1个或者多个子域。子域又分核心域、支撑子域、通用子域。按照实际功能将这些交织的模型划分成逻辑上相互分离的子域,从而在一定程度上减少系统的复杂性。
4.3.2 划分子域
一个领域内可以包含1个或者多个子域。理论上一个子域对应一个限界上下文是最优也是最理想的情况,但是有时又要考虑到业务关联度需要做出权衡。子域又分核心域、支撑子域、通用子域。
4.3.3 寻找核心域、支撑域和通用域
所有子域按照子域类型的定义划分成核心域、支撑域和通用域,具体的内容如下:
核心域:它是整个业务领域的一部分,也是业务成果的主要促成因素。在实施DDD时,主要关注的核心。
支撑域:对应着业务的某些重要方面,但却不是核心,那么他便是一个支撑子域。
通用域:如果一个子域被用于整个业务系统,那么这个子域便是通用子域。
4.3.4 划定限界上下文
限界上下文是一个由显式边界限定的特定职责。领域模型存在于边界之内。在边界内,每一个模型概念,包括属性和操作,都具有特殊的含义。
一个业务领域包含多个限界上下文,与一个限界上下文沟通,需要通过显式边界进行通信。系统通过确定的限界上下文解耦,每一个上下文内部紧密组织,职责明确,具有较高的内聚性。
4.3.5 划分聚合
与值对象相反,聚合是实体的合并。聚合是一组相关对象的集合,我们把它作为数据修改的单元。一个聚合包括至少两个实体与一个根, 根包含了聚合内所有实体的引用,并作为聚合的代表供外界访问,外界只能通过根来访问聚合内的实体,而聚合内的实体之间可以随意互相访问。包含在聚合内的实体构成了一个整体,当聚合消亡时,它们也将随之消亡。聚合使得实体间的关系网大大简化了,联系紧密的实体可以被放入聚合内部封装起来,聚合之间的联系则通过根变得清晰而有条理[2]。
4.3.6 识别领域事件
领域事件定义领域专家所关心的事件的对象。当关心的状态由于模型行为而发生改变时,系统将发布领域事件。
4.3.7 模型详细说明
模型详细说明主要描述了设计的原因、模型内对象的关系、各种业务规则、数据一致性规则、测试用例等。
(1)缓存存储。缓存是领域对象在内存中的生存场所,是一种面向业务的存储方式,而同时我们的领域模型也是一种面向业务的模型,有了面向业务的存储以后,我们就可以进行面向业务的运算,而正是这种面向业务的运算使得我们的系统具有更好的伸缩性和扩展性。因为此时的领域对象通过缓存都是跑在中间件中,而在负载增多的时候,通过水平的增加中间件服务器来进行水平伸缩。
(2)数据库设计:①打破外键关系。让一个服务的代码通过另一个服务暴露的API来访问数据,而不是直接访问数据库。这个API调用会成为微服务化的第一步。这时候可能会有性能担忧,可以做一个性能测试,如果这个“慢”在可接受的范围内,就没有问题。②共享静态数据。把一些些共享的静态数据放入代码,比如放在属性文件中,或者简单地放在一个枚举中。③共享数据。共享的可变数据对于分离系统来说通常是一个麻烦。领域概念不是在代码中进行建模,相反是在数据库中隐式地进行建模,这里缺失的领域概念是中间服务。④共享表。两个业务共用一个表,但是每个业务的关注点不同,我们可以采取行动把它们存储在不同的上下文中,从而分离出两张表,分别在对应的两个服务中。各自依据各自的关注点来建表结构,进行表建模。
(3)事务边界的处理。事务控制需要注意的事项:①在事务中,不要运行昂贵而不必要的、与事务无关的操作指令,如日志记录,其磁盘读写代价高,非常消耗资源。②不要在浏览数据的时候打开事务(设置值@TransactionAttribute(NOT_SUPPORTED))。
事务控制的边界位于服务层。事务边界的处理主要有以下几种方式:
①再试一次。可以对某一个业务先做一次操作,操作终止后,再对另一个业务做另一次操作,这时可以把这部分操作放在一个队列或者日志文件中,之后再尝试对其进行触发。对于某些操作来说这是合理的,但要保证重试能够修复这个问题。②终止整个操作。另一个选择是拒绝整个操作。在这种情况下,需要把系统重置到某种一致的状态,要么重试补偿事务,要么使用一些后台任务来清除这些不一致的状态。可以给后台的维护人员提供一个界面来进行该操作,或者将其自动化。③使用分布式事务。手动编配补偿事务非常难以操作,一种替代方案是使用分布式事务。分布式事务会横跨多个事务,然后使用事务管理器的工具来统一编配其他底层系统中运行的事务。就像普通的事务一样,一个分布式的事务会保证整个系统处于一致的状态。唯一不同的是,这里的事务会运行在不同系统的不同进程中,通常它们之间使用网络进行通信。
(4)测试用例。微服务架构中的测试类型主要包括:单元测试、接口测试、集成测试、组件测试和端到端测试。
(5)模块、仓储、工厂、领域服务。①模块:模块为人们提供了两种观察模型的方式,一是可以在模块中查看细节,而不会被整个模型淹没,二是观察模块之间的关系,而不考虑其内部细节。模块之间应该是低耦合的,而在模块内部则是高内聚的。模块并不仅仅是代码的划分,而且也是概念的划分。②仓储:仓储是一组负责领域对象的持久化的服务。从架构角度来看,仓储用于连接领域层和基础结构层,领域层通过仓储访问存储机制,而不用过于关心存储机制的具体细节。按照DDD设计原则,仓储的作用对象的领域模型的聚合根,也就是说每一个聚合都有一个单独的仓储[3]。③工厂:工厂模式(Factory Pattern)是Java中最常用的设计模式之一,在DDD中也被广泛使用,它是一种体现封装思想的设计模式。工厂的作用是将创建对象的细节隐藏起来,当创建一个对象时,如果创建工作很复杂,或者暴露了过多的内部结构,则可以使用工厂进行封装。事实上除了通过工厂来创建对象,大部分情况下领域对象的创建都不会太复杂,所以我们只需要简单地使用构造函数创建对象就可以了。④领域服务:当领域中的某个操作过程或转换过程不是实体或值对象的职责时,便应该将该操作放在一个单独的接口中,即领域服务。如果勉强地把这些重要的领域功能归为实体或值对象的职责,那么不是歪曲了基于模型的对象的定义,就是人为地增加了一些无意义的对象。应确保领域服务和通用语言是一致的,并且保证它是无状态的[4]。
(6)动态模型设计。动态模型设计包括业务流程设计、设计关键聚合根的状态流转图。
(7)场景走查。确定领域模型、领域服务、仓储等完成系统用例以及关键业务流程,确认领域模型是否能满足领域中的业务场景和业务流程。
最常用的就是通过序列图来走查场景,对创建的领域模型进行逐步验证。
(8)实现架构设计。依据前面的所有分析和设计,最终實现架构设计。部署打包方式为:Jenkins自动化打包,实现持续集成与发布。
5 结 语
电网企业业务复杂,应用系统较多,本文提出的应用系统服务化改造方法作为一种指导思想,可以有序推动大型电网企业的IT架构升级,确保IT建设资产与实际业务的符合,有效支撑企业业务运转,完成从企业信息化到信息化企业的跨越,最终打造安全、可靠、绿色、高效的智能电网。
主要参考文献
[1][美]Eric Evans.领域驱动设计: 软件核心复杂性应对之道[M].修订版.赵俐,译. 北京:人民邮电出版社,2016.
[2][美]Vaughn Vernon.实现领域驱动设计[M]. 滕云,译. 北京: 电子工业出版社,2014.
[3]黄文光,金义富. 基于领域驱动设计构建企业级Web平台的应用[J]. 实验室研究与探索,2013,32(8):72-75.
[4]王忠,程磊. 基于领域驱动设计的软件开发[J]. 软件导刊,2008, 7(2):37-39.
我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自各大过期杂志,内容仅供学习参考,不准确地方联系删除处理!