架构师角色的再认识

引言在软件开发领域,架构师的角色至关重要。Martin Fowler曾经指出,提高开发团队的整体能力,比架构师单独做出决策更为关键。这一观点启发我们重新思考架构师的职责和作用。

架构师的两种角色根据业务系统的不同需求,架构师可以被分为两种类型:纵向架构师横向架构师

纵向架构师纵向架构师专注于具体的业务和系统。他们的主要职责包括:- 定义问题和预期结果- 进行系统、模块和代码设计

纵向架构师需要深入理解业务需求,并通过架构设计来实现这些需求。

横向架构师横向架构师则关注团队的整体水平提升和业务边界的确定。他们的职责包括:- 解决跨领域的技术问题- 确定不同团队之间的协作边界- 制定技术规范和统一的沟通语言- 定义架构能力的基础和成长路径

横向架构师的目标是让每个团队成员都能够拥有架构思维,从而提高团队的敏捷性和整体能。

架构设计的核心过程无论是纵向还是横向架构师,他们的核心工作都是围绕如何完成架构设计和如何培养架构思维。这涉及到:- 探讨架构设计的方法和策略- 培养团队成员的架构意识和能力- 通过不断的迭代和优化,提升系统的性能和可维护性

结语架构师不仅仅是技术决策者,更是团队能力提升的推动者。通过培养团队成员的架构思维,架构师可以极大地提升团队的整体实力,实现更高效的软件开发过程。

架构师的定义与职责

架构师是软件开发过程中的关键角色,其主要职责包括:

  1. 需求提炼:架构师需要与需求方沟通,深入理解项目的概念,确保双方有统一的沟通语言。2. 关键质量属性总结:基于利益相关方的期望,架构师应总结软件系统的关键质量属性。3. 架构设计:架构师负责提供满足这些质量属性的高层架构设计,包括软件的模块化、模块间交互、用户界面风格、对外接口方法等。4. 创新设计:架构师应具备创新思维,设计软件的创新特性,以满足不断变化的市场需求。5. 技术沟通:架构师需与不同角色进行有效沟通,包括与客户讨论概念,与经理讨论设计问题,与工程师讨论结构特性,以及与程序员讨论实现技巧和风格。 架构师的角色是多面的,需要在技术深度和广度上都有所涉猎,以确保软件系统的成功落地和持续发展。

架构师与其他角色的互动

  • 与客户:架构师需要理解客户的业务需求和期望,确保软件设计能够满足这些需求。- 与经理:在设计问题上,架构师与经理共同探讨,确保设计方案符合项目目标和预算。- 与工程师:架构师与软件工程师合作,讨论软件的结构特性,确保技术实现的可行性。- 与程序员:架构师指导程序员在实现过程中关注外观、风格和技巧,以保证软件的高质量。 架构师的工作是跨学科的,要求其在技术、管理和创新方面都有深入的理解和实践能力。

架构师的能力模型

架构师在系统设计和开发过程中扮演着至关重要的角色。以下是架构师需要具备的关键能力:

1. 需求挖掘与提炼架构师需要具备深入挖掘和提炼需求的能力。面对客户或产品需求描述的不完整性,架构师应能够:- 理解需求的详细内容和业务目标。- 识别不同利益相关方的动机和关注点。

2. 架构设计架构设计是架构师的核心能力之一。关键点包括:- 结合团队经验和技术特长进行技术选型。- 持续学习,以适应技术的发展和变革。

3. 代码落地架构师应与开发团队紧密合作,确保架构设计能够通过代码实现。

4. 多维能力架构师需要在日常工作中不断提升以下多维能力:- 业务抽象能力。- 代码编写能力。- 解决复杂问题的能力。- 计算机基础知识。- SQL调优技能。- 项目管理和推进能力。

2.2 方法论的重要性

方法论对于架构师的成长至关重要。以下是方法论学习与实践的重要性:

  • 方法论学习:通过学习方法论,架构师可以形成系统化的思考方式。- 实践应用:将方法论应用于实际工作中,以解决具体问题。- 反思与提升:基于方法论对实践进行反思,实现能力的螺旋上升。 架构师的成长是一个不断学习和实践的过程,需要持续地吸收新知识,应用新方法,并在实践中不断反思和提升。

架构师思维与方法论的重要性

在学习和工作中,我们经常会遇到一些基础而重要的公式或原理,例如物理学中的运动学公式。这些公式经过推导和实践,成为我们解决问题时的有力工具。正如物理学中的公式,架构师思维和方法论也是我们解决问题时不可或缺的工具。

1. 理解方法论的价值

以物理学中的运动学公式为例,我们可以通过推导得到公式 Vt^2 - V0^2 = 2as。这个公式在初学时可能看起来并不复杂,但当我们将其内化为自己的知识后,就可以在解决问题时节省推导的精力,从而集中精力解决更具挑战性的问题。这正是方法论的价值所在。

2. 架构师思维的内涵

架构师思维,是指在面对复杂问题时,能够从宏观和系统的角度进行思考和设计的能力。这种思维方式要求我们不仅要关注细节,还要能够把握整体,理解各个部分之间的相互关系和影响。

3. 架构的定义与组成

根据ISO/IEC 42010:20072的定义,架构是系统的基本组织,体现在其组件、组件之间的关系以及环境,以及指导其设计和演化的原则。具体到软件系统架构,我们可以从以下几个方面来理解:

  • 系统模块架构:包括系统分层模块、应用模块和外部服务模块。- 模块间关系:模块依赖关系和数据流转方式。- 设计原则:模块划分方案的指导思想。

4. 架构师的职责与挑战

架构师需要将业务或技术需求转化为具体的软件系统架构。这个过程涉及到多个层面的决策和设计,包括但不限于:

  • 系统模块:确定系统的不同模块及其功能。- 模块关系:明确模块间的依赖和数据流转。- 设计原则:制定指导系统设计和演化的原则。

5. 架构师解决问题的方法论

架构师在解决问题时,需要运用一系列的方法论,这些方法论可能包括:

  • 需求分析:深入理解业务和技术需求。- 系统设计:基于需求设计系统的模块和架构。- 技术选型:选择合适的技术栈和工具。- 风险评估:评估设计中可能存在的风险。 通过这些方法论,架构师能够将复杂的业务和技术需求转化为清晰、可行的软件系统架构。

结语

架构师思维和方法论是我们在面对复杂问题时的重要工具。通过学习和实践,我们可以不断提高自己的架构能力,更好地解决实际问题。

在设计软件系统时,我们首先需要构建一个坚实的基础。这个基础就是业务领域模型。要创建这个模型,我们需要深入理解业务需求,挖掘需求背后的深层次含义,并评估关键利益相关者所关注的核心质量属性和业务目标。以下是实现这一目标的步骤:

  1. 需求分析:首先,我们必须对业务需求进行全面的分析。这包括了解业务运作的方式、识别业务流程以及确定业务目标。
  2. 利益相关者分析:识别所有关键利益相关者,并理解他们的需求和期望。这有助于确保系统设计能够满足所有相关方的需求。
  3. 核心质量属性评估:确定系统设计中需要优先考虑的质量属性,如性能、可靠性、可用性和安全性。
  4. 业务领域模型构建:基于需求分析和利益相关者分析的结果,构建业务领域模型。这个模型应该能够反映业务的实际情况,并为软件系统的设计提供指导。
  5. 数据存储架构设计:根据业务领域模型,设计数据存储架构。这包括选择合适的数据存储技术,如关系数据库、NoSQL数据库等。
  6. 对象架构设计:定义系统中的对象及其关系,确保对象架构能够支持业务流程和数据存储需求。
  7. 系统模块架构设计:将系统分解为模块,每个模块负责特定的功能。确保模块之间有清晰的接口和良好的解耦。
  8. 架构决策文档化:记录所有架构决策,包括选择特定技术或设计模式的原因,以便于团队成员理解和后续维护。
  9. 持续迭代和改进:架构设计是一个持续的过程,需要根据业务发展和技术变化进行不断的迭代和改进。 通过上述步骤,架构师可以确保软件系统的领域模型能够准确反映业务需求,并为系统的开发和维护提供坚实的基础。

从上往下,我们首先需要了解需求,包括利益相关方的业务目标/动机、关键架构需求用例集;其中利益相关方的业务目标和动机会影响质量属性的优先级以及架构的设计选型;关键架构需求一般包含以下四块:

  • **约束:**给定的不可更改的设计决策;
  • **质量属性:**评估系统性能的非功能性需求,是利益相关方判断软件系统是否好用的一切外部可见特性,包括响应耗时、sla、可伸缩性、可用性、可维护性、可测试性等;其表征系统在特定环境下的运行情况,设计决策一般都会提高或者抑制一个质量属性,比如性能和可读性等。这点非常重要,在模式选择的时候,需要基于质量属性进行抉择;
  • **影响较大的功能需求:**需要特别注意的特性和功能;
  • **其他影响因素:**架构师/团队的经验、特长等;

最后是用例集,它是一组相关的成功和失败的场景集合,用来描述参与者如何使用系统来实现目标;优秀的产品的prd文档,应该要无限接近于标准的格式化用例描述集合,它是原始产品方案通过多轮沟通对焦,得出来的标准化语言,一个用例应该要包含 参与者(可以是系统或者用户)+前置条件(在什么场景下)+如何使用系统+经过什么规则+产生什么结果;而这样的用例集合,应该要能够描述出产品系统的功能全貌

当能够从 业务目标、关键架构需求和用例集 将需求描述清晰了之后,就可以开始用业务模型来描述项目产品,这一层设计的是问题空间中的领域模型,它是对客观物理世界中概念、规则、关系的分析和描述,与软件系统无关。引用前辈的总结,“这个层次上的实体我们称之为概念实体,这部分内容是用在需求和业务分析上的,讨论业务概念模型时完全不需要考虑软件的实现,这个过程是一个分析过程,即使不做软件研发,做其他的研发,类似的分析过程也应该是有的”。《实现领域模型驱动》这本书也对问题空间做了定义,“问题空间是顶级域和其他域的组合,以及域之间的关联关系,即使没有软件的存在,这些域还是存在的,域之间的关系也还是存在的,领域模型之间的关系也还是存在的”。最后我们要把问题空间的领域模型映射到解决问题空间,《实现领域模型驱动》中对解决问题空间的定义是“一组特定的软件模型,它通过软件的方式来实现解决方案”,这一组特定的软件模型,也就是我们要产出的软件系统架构(系统模块架构、对象架构、存储架构)。现在有很多讲架构的文章,大多是讲的这一层的故事,甚至于讲的是这一层中系统模块架构的故事。比如mvc架构,六边形架构,洋葱架构,整洁架构等。《领域驱动设计,软件核心复杂性应对之道》 中则花了非常大的篇幅,讲实体、值对象、服务、聚合根、工厂、仓库、界限上下文等,这是一套将问题空间领域模型映射到解决空间对象架构的设计模式。

3.3 怎么解决架构师要解决的问题

3.3.1 了解需求

千里之行,始于足下。只有了解了需求,了解了利益相关方关注点,了解了业务目标,我们才能制定设计策略。

3.3.1.1 利益相关方

首先我们要梳理需求的利益相关方关注点图表:

这里将相关方进行排序,优先级越高的,他的关注点就更能影响后续决策。与利益相关方进行沟通之后,我们要与他们明确业务目标:背景(原因)+ 主体 + 结果。业务目标是架构的主要驱动因素,多个利益相关方冲突时,以业务目标进行排序抉择。

3.3.1.2 关键架构需求

关键架构需求指的是能够显著影响架构中的结构选择的需求,一般有四类:约束、质量属性、影响较大的功能需求、其他影响因素。具体的定义在3.2中有讲到,这里不做赘述;

  • 约束:约束是架构中不能被打破的原则性前提;如果描述的语法类似于“要尽可能地xxx”、“如果能xxx那就更好了”,这种就不属于约束,约束的语法应该是“必须xxx”。由于约束的不可变性,我们要尽可能减少约束性需求。可能是产品链路还没有思考清楚,可能是业务模型还没有理清,水平不高的产品往往并不知道新增约束会带来的后果,由于新增约束通常会带来架构设计上的便利,加速项目功能上线,这些产品在前期会给出很多并不恰当的约束,导致后续技术债难还,这就需要架构师慧眼识珠了。
  • 质量属性:质量属性与功能性需求无关,所以很容易被忽略。但是在模式选择和方案设计的时候,通常需要基于质量属性做设计决策。因此要有一个质量属性优先级的排名,比如说数据一致性、事务性、可测性、数据准确性等,我们可以通过质量属性网格或图表,对利益相关方关注的事项和原始质量属性场景进行提取、分类、完善、排定优先级,可视化头脑风暴和与利益相关方的沟通结果。如下图,是我摘自《List of Quality Attributes for Grid Monitoring Tools》的一个示例。

  • **影响较大的功能需求:**需要特别注意的特性和功能;
  • **其他影响因素:**虽然它叫“其他影响因素”,但是它对最终架构选型至关重要;架构师和团队的经验会大大影响我们对技术框架的使用和对基础存储的选择,因为熟悉的事物对项目的进度风险,对后续的运维成本,都会小很多。所以架构师要不断学习新的知识,了解技术升级和变革。

3.3.1.3 用例集

用例集是一组相关的成功和失败的场景集合,用来描述参与者如何使用系统来实现目标。单独一个用例,应该要能完整地表达一次或一类业务行为。比如 [拥有88vip的消费者][在购物车同时勾选两个同店铺活动商品时],[点击查看明细按钮],[经过同店活动限购1次的优惠计算规则],[得到只享受一个优惠的价格]。其范式为参与者(可以是系统或者用户)+前置条件(在什么场景下)+如何使用系统+经过什么规则+产生什么结果。优秀的产品的prd文档,应该要包含无限接近于标准的格式化用例描述集合,它是原始产品方案通过多轮沟通对焦,得出来的标准化语言。这里的用例集和我们常说的tc(测试用例)集是有差别的;用例相比tc会更强调过程规则,强调对功能的描述;tc则更关注的是输入和输出,强调覆盖场景,强调对业务功能、业务模型、技术模型的功能性/非功能性验证;

3.3.2 架构描述

大多数同学都不喜欢写技术方案、架构文档,因为它会占用写代码的时间,而且由于返工和迭代的必然性,架构文档相比代码往往显得过时。但是优秀的架构描述是有用的资产,在多人协作的场景下,在系统需要被多方了解评估或开发测试时,能有效地促进沟通和协作,将设计决策和思想有效地传递给每一个人,提高软件的开发测试质量。**口口相传的信息传递效率永远都是最低的。**无论是3.2中讲到的业务模型,还是软件系统模型,对其架构的描述,都不可避免地涉及到通过画图来描述全貌。复杂的系统架构,想用单一的模型图来描述,一般只能粗具梗概;所以 ISO/IERC/IEEE 42010:2011 中提出了“视点”的概念,并做了定义:

视点:从不同的视角或者专业领域来看待系统的方法。

常见的视点方法论有以下这些。

  • C4模型
  1. 上下文层次(Context):描述了系统与外部实体(如用户、其他系统、硬件设备等)之间的关系,显示系统如何与其周围的环境交互以及其外部依赖关系。
  2. 容器层次(Container):系统内的软件被分解成多个容器,如应用程序、数据库、文件系统等,容器图描述了这些容器之间的关系及它们如何共同工作以实现系统的功能。
  3. 组件层次(Component):在容器的内部,每个容器被进一步拆分为组件,如类、模块、服务等,组件图描述了组件之间的关系和依赖关系,以及它们如何协同工作。
  4. 代码层次(Code):这是最低层次,描述了每个组件的内部实现细节,通常可以是类图、包图等,用于展示组件内的代码结构。——Simon Brown
  • 4+1视图模型

场景视图:从外部视角,描述系统的参与者(用户)与系统功能用例的关系。反映的是系统的最终用户需求和交互设计。

逻辑视图:从结构化视角,描述该系统对用户提供的所需功能服务所具备的组件结构和数据结构,以及一些边界约束条件,清晰地描述给用户提供的功能需求服务是如何构建的。描述该系统内部所具备了哪些组织结构,以达到实现对外功能。开发视图:从结构化视角和行为视角,去描述实现系统功能的各个组件和模块是如何实现的。处理视图:从行为视角,描述系统各个组件和模块是如何进行通信的。物理视图:从交互视角,描述系统可以部署到哪些物理环境(如服务器、PC端、移动端等)上和软件环境(如虚拟机、容器、进程等)上。——Phillipe Krutchen

本文从下面两小节的问题空间解决方案空间的模型来做架构描述:业务领域模型图:描述需求的分析结果,突出业务领域概念和业务模型关系,统一产品需求方、领域专家、开发人员的概念语言;系统模块图:描述系统内的分层模式和模块/领域依赖关系,描述系统间的依赖关系和数据交互方式;对象模型图:描述核心类职责,类与类继承关系,类与类的依赖关系;数据存储架构图:描述库表结构,分库分表策略,存储选型,以及不同数据表之间的一对多/一对一等依赖关系;这些讲的都是一个视点想要表达的内容,而不是只用有一张图来表达的内容;为了让系统的细节更丰富地呈现,我们可以按需绘制精细视图,来描述某个局部的系统细节;为了表达设计原则,我们可以增加质量属性视图,描述对于某个质量属性的设计,如可用性、数据准确性、一致性等,突出质量属性是这个视图可以表现的故事,因为质量属性通常不那么清晰可见,容易被忽略,我们可以在这里将它化虚为实。对于单个视图的绘制,UML是一个比较完善的标准,它可以很好地表现设计构思,但是并不是所有人都对UML了如指掌,我很赞同Simon Brown的观点,“虽然UML很有用,但我更喜欢用简单的方框和线条表现架构。为避免混淆,我建议尽可能使用简单的、不言自明的符号,并添加必要的图例”,这里表现的就是尊重架构受众的思想,因为设计的本质是社交,我们要让我们的设计以更高的效率传递到其他人那里,包括技术人员和非技术人员。

3.3.3 业务模型

业务模型是问题空间的领域模型,描述业务和产品,与软件系统无关,具体**描述的是客观物理世界的概念、规则、关系。**要对业务领域建模,首先需要对3.3.1中的用例进行分析,引用前辈的做法:

a、从准确的用例中剥离出名词;b、根据名词梳理领域模型和其属性;c、根据名词的修饰梳理出属性值;d、根据名词的定义完善属性值;e、从用例集合中剥离出动词&形容词;f、根据动词&形容词梳理出领域模型之间的关系;

但仅仅是对用例集进行提取,会遗漏一些隐藏概念,比如关于“一个店铺拥有多个子账号,每个子账号对店铺的可操作权限不同”的描述,我们很容易将操作权限作为子账号的一个属性,子账号则挂靠在店铺实体上。

相信做软件开发的大家对这个例子应该很亲切,因为我们一般都会将权限拎出来,作为单独的实体概念,再将权限关联在帐号上,也方便权限的维护的后续的继承:

这里就涉及到架构元模型(元模型定义了模型中使用的概念和使用规则)中隐藏概念的建立,好奇心循环是我们建立元模型的手段, 从提问开始,建立模型、检验模型、分离概念。

关于前面讲的提取的名词(概念和属性),还有很重要的需要注意的点是,这些词语需要与各个相关方统一语言;Michael Keeling和Eric Evans在他们的书中,都不约而同地强调了业务与技术之间统一通用语言的重要性;如果产品需求方、领域专家、开发人员对业务模型的描述语言都局限在自己的圈子内,那么沟通将引入巨大的翻译开销。有过跨团队协作的同学应该能够深刻理解这种翻译带来的误解的风险和成本有多高,并且这种翻译将使得沟通不畅,各方在做领域知识消化的时候,都需要耗费额外的精力。所以在业务模型的设计时,开发人员与业务相关方之间要建立通用语,所有利益相关方都有权理解架构,有权知道系统中的各个模块是如何协同运作。

3.3.4 软件系统模型

3.3.4.1 系统模块架构

系统模块架构核心描述系统内的分层模式和模块/领域依赖关系,描述**系统间的依赖关系和数据交互方式;**关于系统分层模式和模块/领域的依赖关系方法论,《DDD中常提到的应用架构总结(六边形、洋葱、整洁、清晰)》中有一张图画的非常好,一图即可概览主流架构分层思路在ddd方向上的演化,这里引用一番:

最开始的MVC架构,对系统做简单分层,描述了从数据层业务逻辑层数据出口层的关系;由于mvc中,业务逻辑对数据出入口的依赖是确定的,所以这个依赖变化时,改动成本高,无法突出领域模型的独立性;于是六边形架构横空出世,它将领域层独立出来,不依赖任何确定的外部服务,以端口/适配器的方式定义外部服务交互协议,只要能实现这套交互协议,数据出入口的依赖变化,对领域层是没有侵蚀的;这里体现的是领域驱动设计的思想,将领域知识立在最重要的地位,不为任何模块影响。最后是清晰架构,它的最内层是领域层,包含领域模型和领域服务,实现相关的领域知识和概念模型;向外是应用服务,可以依赖领域层,做业务用例的编排实现,比如操作某个领域服务后,再操作某个实体进行某项行为,最后发送某个领域事件等;和应用服务层同级的,还有CQRS和事件/消息处理器,接受不同类型的命令执行类似应用服务的事情;以上这些即应用核心,应用核心与外界的交互分为两类:图中的左半边为主动适配器,做类似于Controller或是HSF服务的系统最外层请求实现;图中的右半边为被动适配器,定义消息出口、数据持久化接口、搜索引擎接口等,由外部具体的基础设施实现。可见,清晰架构属于集前人所长,提供的一份以领域知识为核心的分层架构指南。除了系统内部的分层结构,我们还需要描述系统间的关系,应用间的关系,以及数据流转的方式,如下图:

3.3.4.2 对象架构

按照3.3.3中抽象出来的业务领域模型来编写代码,能够使代码更好地表达设计含义,并且使模型与实际的系统相契合;面向对象编程之所以强大,就是因为它为架构概念提供了实现方式,能描述现实物理世界中的关系(操作、继承、组合)和模型(定义、属性、职责)。《领域驱动设计,软件核心复杂性应对之道》中有大量的篇幅讲解用面向对象的思路对类的类型进行划分,并将业务模型映射到对象模型中的模式:

  • 实体:由标识定义,而不依赖它的所有属性和职责,并在整个生命周期中有连续性。这句话在初看的时候非常晦涩,简单来说,就是一个标识没变的对象,在其他自身属性发生变化后,它依然是它,那么它就是实体;举个例子,一个订单的收货地址,收货人电话,都发生了变化,但是这个订单id没有变,那么这个订单依然是变化前的那个订单,只是它的一些属性发生了变化;通过这种方式来识别实体的目的,是因为领域中的关键对象,通常并不由它们的属性定义,而是由可见的/不可见的标识来定义,且有完整的生命周期,在这个周期内它如何变化,它都依然是它;通过这种方式识别出实体这种领域关键对象,也是领域驱动设计和数据驱动设计最大的差别,数据驱动设计是先识别出我们需要哪些数据表,然后将这些数据表映射为对象模型;而领域驱动设计是先通过业务模型识别出实体,再将实体映射为所需要的数据表。不过前面也提到,实体的标识可以是可见的,也可以是不可见的,因为有很多域内无持久化的系统,在它们的对象模型中,并不存在可见的唯一标识id,所以在我之前的另一篇文章,也提供过不一样的描述实体的思路:

对于更加关注"行为"而非"唯一性"的纯计算型应用,给出划分实体与值对象的另一种思路:
1、实体是会对自身属性做出强解释行为的类型。2、值对象是轻属性解释,重属性设计的类型。理由是,纯计算型应用,业务关注重点是行为,当一个类需要承载复杂的计算逻辑,即对自身属性需要进行强解释行为时,它往往就承载了系统中更重要的职责,能更加凸显领域业务概念。

  • 值对象:用于描述领域的某个方面而本身没有唯一标识的对象。被实例化后用来表示一些设计元素,对于这些设计元素,我们只关心它们是什么,而不关心它们是谁。举个例子,一个订单的收货地址Address对象有省份、城市、街道、门牌号这几个属性,其中的门牌号从111修改成222后,它就已经不再是修改前的那个它了,因为门牌号222并不等于门牌号111的地址。即它是没有生命周期的,它的equals方法由它的属性值决定(实体的equals方法由唯一标识决定);
  • **聚合:**聚合是一组实体和值对象的组合;内部包含一个聚合根,和由聚合根关联起来的实体和值对象;比如说有商品、sku、库存三个实体,那么在商品模型中,商品就是聚合根,其内部通过sku id关联它的sku,通过库存id关联商品/sku的库存;聚合将这组关联关系建立,对外提供统一的操作,比如需要删除某个商品,那么这个聚合的内部可以在一个事务(或分布式事务)中,对库存进行清空,对sku进行删除,最终对商品进行删除。
  • **服务:**有一些对实体/聚合/值对象进行编排操作的概念并不适合被建模为对象,那么它应该被抽象为服务,化作一只上帝之手,做领域对象间流程操作的编排。服务很重要的特征,它的操作应该是无状态的。
  • **工厂:**当创建一个实体对象或聚合的操作很复杂,甚至有很多领域内部的结构需要暴露的时候,就可以用工厂进行封装。一种相对简单粗暴的判断方法是看这个类的构造方法实现是否复杂,并且看着这些逻辑不应该由这个类实现,那么不妨用工厂来构造这个对象吧!
  • 仓库:仓库是可持久化的领域对象和真实物理存储操作之间的媒介,随意的数据库查询会破坏领域对象的封装,所以需要抽象出仓库这种类型,它定义领域对象的获取和持久化方法,具体实现不由领域层感知;至于具体用了什么存储,如何写入和查询,是否使用缓存,这些逻辑统一封装在仓库的实现层,对于后续迁移存储、增删缓存,都可以做到不侵蚀业务领域。
  • 防腐层:防腐层并不是一个特定的对象类型,而是一种领域模型保护的思路;对于领域外界的变化,我们需要持悲观的态度,因为领域外部的模型不受我们控制,它们的变化轨迹难以捉摸,所以在系统与系统直接,上下文与上下文之间,要有一层放腐层进行领域内外的模型转换。

有了对象类型的划分,对象职责如何确定呢?GRASP给出了很好的判断标准:

创建者问题:谁负责产生类的实例解决方案:如果符合下面的一个或者多个条件,则可以将创建类A实例的职责分配给类BB包含AB聚合AB拥有初始化A的数据并在创建类A的实例时将数据传递给类AB记录A的实例B频繁使用A
信息专家定义:如果某个类拥有完成某个职责所需要的所有信息,那么这个职责就应该分配给这个类来实现。这时,这个类就是相对于这个职责的信息专家。解决方案:将职责分配给拥有履行一个职责所必须信息的类(域)。
低耦合问题:怎么样支持低的依赖,减少变更带来的影响,提高重用性?解决方案:在类的划分上,尽量创建松耦合的类,修改一个类不会影响其他类。在类的设计上,尽量降低类中成员和方法的访问权限,尽量将类设计为不变类。在类的引用上,将一个对象对另一个对象的引用降低到最小。
高内聚问题:如何使得复杂性可控?解决方案:功能性紧密的相关职责应该放在同一个类中,并共同完成有限的功能。这样做更加有利于对类的理解和重用,也可以降低类的维护成本。
纯虚构问题:当不想破坏高内聚和低耦合的设计原则时,但是有些职责又没地方放,如何处理解决方案:将一组高内聚的职责分配给一个虚构的或者处理方便的类,它并不是问题域的概念,而是虚构的概念,以达到支持高内聚低耦合和重用的目的。
间接问题:如何分配职责,以避免两个事物之间的直接耦合?解决方案:当我们不知道将职责分配给何种模型的时候,可以看看是否可以将职责分配给中介模型。

3.3.4.3 存储架构

存储架构描述库表结构,分库分表策略,存储选型,以及不同数据表之间的一对多/一对一等依赖关系;常见的手段有E-R图描述表模型关系,用图例来描述分库分表策略以及存储选型策略。

四、写在最后

这篇文章从动笔到写完,断断续续持续了一个多月。对自己也是一次总结和思考的过程。文章中可以看到,创造/成长类的方法论,很多环节的都离不开【循环】。像学习->实践->反思的循环,像架构设计中 分析->设计->返工->反思的循环,像好奇心循环中的 提问->建模->验证->分离概念的循环,以及文中没有提及的也很有名的TDC循环;可见恩格斯所说的螺旋形式上升之精辟,任何事物都是从肯定否定再到否定之否定中发展。对架构师的思考和实践也一样,本文是站在当前的视角和经验进行的总结,大家可以指出其中不妥的部分,让螺旋形式的发展可以一直循环下去。回到文章的开头,有提到横向架构师和纵向架构师的概念,但本文通篇在讲纵向架构师的故事;因为,横向架构师本质也是站在多业务域视角上的纵向架构师,单业务领域对它来说变成了子域罢了,方法论是相通的;另外横向架构师一个很重要的职责就是本文的标题,让人人都成为架构师。

参考: