Bootstrap

架构之道——人人都是架构师

插: AI时代,程序员或多或少要了解些人工智能,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家(前言 – 人工智能教程 )

坚持不懈,越努力越幸运,大家一起学习鸭~~~

一、前言

对于架构师的职责,最近感慨颇深;很认同Martin Fowler说的:

Improving the development team’s ability gives an architect much greater leverage than being the sole decision maker. —— Martin Fowler

大意就是提高团队整体能力的架构师比只做决策的架构师更牛逼。

因为对于每个业务系统,我们都需要从这个业务的架构视角进行代码开发/优化和迭代,所以每个业务系统的开发者,都应该要有架构师属性,这里我将其定义为纵向架构师;对于目标在提高整个团队水平、确定多业务边界的,则定义为横向架构师。

纵向架构师需要聚焦业务和系统,做问题和结果的定义,做系统、模块、代码设计;

横向架构师需要解决跨域问题,确定跨团队边界,定义规范,统一语言,定义纵向架构能力基础/标准的成长路径和方法论,让人人都成为架构师,让团队更加敏捷;简而言之,其核心过程是探讨如何完成架构设计,如何拥有架构思维。

图片

二、什么是架构师

2.1 什么是架构师

引用wiki的定义:

软件架构师定义和设计软件的模块化,模块之间的交互,用户界面风格,对外接口方法,创新的设计特性,以及高层事物的对象操作、逻辑和流程。软件架构师与客户商谈概念上的事情,与经理商谈广泛的设计问题,与软件工程师商谈创新的结构特性,与程序员商谈实现技巧,外观和风格。

总结一下,架构师需要提炼需求方关于项目的概念并和需求方有统一的沟通语言;需要基于利益相关方总结关键质量属性,给出满足质量属性的软件系统架构设计;需要与开发工程师共同进行软件系统的落地

图片

这里的质量属性指的是评估系统性能的非功能性需求,如可扩展性,可用性,一致性等;wiki中的定义如下:

Within systems engineering, quality attributes are realized non-functional requirements used to evaluate the performance of a system. 

基于wiki对架构师的定义我们可以看出,架构师的能力模型需要包含

  • 挖掘/提炼需求描述的能力:客户/产品的prd往往描述得比较局部,会缺少一些上下文或者产品上认为不重要的部分 ,相信很多同学还经常收到一句话需求,更有甚者就简单地说和XXX产品一样。这时候就需要架构师将需求的迷雾层层拨开,了解需求详情、业务目标,了解不同的利益相关方动机、关注点;
  • 架构设计能力:关键架构需求中,有一个点就是架构师及团队的经验和特长,比如我们用lindorm更多,那么我们在选型中就会更青睐于lindorm,因为这对项目的开发、运维成本以及进度风险会更低;但与此同时,我们也要承担这个选型带来的一些缺点;所以架构设计方法除了本文讲到的方法论,还很依赖于架构师的技术前沿性。就像一位前辈所说的,“每时每刻都在发生技术的升级和变革,只有持续不断的学习,才能对老的架构有新的认识,对于老的问题产生新的解法,要了解业界最近在发生什么变化,这个领域最关键的项目和人在做什么,学习他们的技术,学习他们的论文”;
  • 代码落地能力:架构师要能够和开发工程师一起完成代码编写落地,这块本文不过多赘述;

总而言之,架构师是需要在日常中,多维且持续去坚持锻炼的,包括业务抽象能力、代码能力、复杂问题的解决能力、计算机基础、sql调优,项目管理推进能力等。

2.2 方法论

这里把方法论单独拎出来,一是因为要聊如何成为架构师,就要聊成为架构师的方法论和实践的故事;二是在刚参加工作的时候,我应该和大多数同学一样,认为方法论很虚头巴脑;原因也很简单,因为那个时候没有足够的经验对方法论感同身受,也没有接纳方法论的那份心情。现在回过头来看,学习方法论->实践->基于方法论对实践进行反思 的循环,正正是我们自身能力螺旋上升的典型模式。

图片

再讲一个更亲切地能说明其重要性的例子。我们都知道关于路程/时间/加速度有两条非常容易通过实验得出的公式:s = 1/2 * (V0 + Vt) * t、a = (V0 - Vt) / t;基于这两个公式,我们一顿推导,将时间t消掉,可以得到 Vt^2 - V0^2 = 2as的公式,应用非常的广;经过我们多次的刷题实践和思考,这个公式在我们心里已经是一条基础公式,做题的时候,我们完全不需要再耗费精力去将它推导得出。

Vt^2 - V0^2 = 2as 这条公式,就是一个【方法论】。初看这条公式的时候,觉得也不过如此,静下来推导一番,也能够得出,然后再用它去解决问题。但是推导它是需要耗费精力的,人最宝贵的资源之一就是精力;当你将这个公式作为一个方法论铭刻于心后,在解决关于这个公式的相关问题时,就可以释放出推导它的这部分精力,用来解决更有挑战性的部分。

可见,我们还在读书的时候,已经深刻且频繁地在使用方法论了。

三、纵向架构师思维

这篇文章的标题是人人都是架构师,这里最重要的其实是希望人人都具备架构师思维;忘了之前看的哪篇文章里面有写到,“不一定人人都能够是经济学家,但可以人人都具备经济思维;不一定人人都能够是历史学家,但可以人人都具有鉴往知来的思维”;写得很精辟。

要完成这一章的探讨和编写,需要回答好这么几个问题。架构是什么?架构师要解决的问题有哪些?解决这些问题的方法论是什么?

3.1 架构是什么

在ISO/IEC 42010:20072中对架构有如下定义:

The fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution.

这个定义对架构的描述高屋建瓴;它描述了架构是由组成模块、特定上下文环境下模块间的关系、指导模块和关系发展的原则

就像代码中的基类一样,越顶层的抽象描述事物的范围越广,对于软件系统架构,我们其实可以从一个软件系统最终落地的结果来看,得到软件系统架构关于架构的具体实现:

  • 系统模块架构
  • 模块:系统分层模块,应用模块,外部服务模块;
  • 关系:模块依赖关系,模块数据流转方式;
  • 原则:模块划分方案。
  • 对象架构
  • 模块:类,有哪些核心类,类职责是什么;
  • 关系:类继承关系,类与类之间的依赖关系;
  • 原则:类设计指导思想和关注点;
  • 数据存储架构
  • 模块:库表模块,描述有哪些数据库、有哪些表,如何做分库分表,数据库如何选型;
  • 关系:表关联关系,不同数据表之间的一对多/一对一等依赖关系;
  • 原则:数据存储选型/设计方案和关注点,设计考虑的核心质量属性;

3.2 架构师要解决什么问题

在3.1 中我们讲到软件系统架构有哪些,这些也正是架构师要产出以及要落地的内容,即输出;对于业务系统而言,我们的输入往往是业务需求/技术需求的描述集合。如下图所示,其中的长方形就是架构师要解决的问题:如何将业务/技术 需求描述 加工成 落地的软件系统架构

图片

从3.1中讲到的 数据存储架构/对象架构/系统模块架构 向上推导,我们需要有业务领域模型,才能够设计软件系统的领域模型;而要得到业务领域模型,我们需要对需求充分了解,充分挖掘,并评估各个利益相关方关注的核心质量属性和业务目标。因此,架构师要解决的问题如下图所示:

图片

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

  • 约束:给定的不可更改的设计决策;
  • 质量属性:评估系统性能的非功能性需求,是利益相关方判断软件系统是否好用的一切外部可见特性,包括响应耗时、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实例的职责分配给类B

B包含A

B聚合A

B拥有初始化A的数据并在创建类A的实例时将数据传递给类A

B记录A的实例

B频繁使用A

信息专家

定义:如果某个类拥有完成某个职责所需要的所有信息,那么这个职责就应该分配给这个类来实现。这时,这个类就是相对于这个职责的信息专家。

解决方案:将职责分配给拥有履行一个职责所必须信息的类(域)。

低耦合

问题:怎么样支持低的依赖,减少变更带来的影响,提高重用性?

解决方案:

在类的划分上,尽量创建松耦合的类,修改一个类不会影响其他类。

在类的设计上,尽量降低类中成员和方法的访问权限,尽量将类设计为不变类。

在类的引用上,将一个对象对另一个对象的引用降低到最小。

高内聚

问题:如何使得复杂性可控?

解决方案:功能性紧密的相关职责应该放在同一个类中,并共同完成有限的功能。这样做更加有利于对类的理解和重用,也可以降低类的维护成本。

纯虚构

问题:当不想破坏高内聚和低耦合的设计原则时,但是有些职责又没地方放,如何处理

解决方案:将一组高内聚的职责分配给一个虚构的或者处理方便的类,它并不是问题域的概念,而是虚构的概念,以达到支持高内聚低耦合和重用的目的。

间接

问题:如何分配职责,以避免两个事物之间的直接耦合?

解决方案:当我们不知道将职责分配给何种模型的时候,可以看看是否可以将职责分配给中介模型。

图片

3.3.4.3 存储架构

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

四、写在最后

这篇文章从动笔到写完,断断续续持续了一个多月。对自己也是一次总结和思考的过程。

文章中可以看到,创造/成长类的方法论,很多环节的都离不开【循环】。像学习->实践->反思的循环,像架构设计中 分析->设计->返工->反思的循环,像好奇心循环中的 提问->建模->验证->分离概念的循环,以及文中没有提及的也很有名的TDC循环;可见恩格斯所说的螺旋形式上升之精辟,任何事物都是从肯定否定再到否定之否定中发展。

对架构师的思考和实践也一样,本文是站在当前的视角和经验进行的总结,大家可以指出其中不妥的部分,让螺旋形式的发展可以一直循环下去。

回到文章的开头,有提到横向架构师和纵向架构师的概念,但本文通篇在讲纵向架构师的故事;因为,横向架构师本质也是站在多业务域视角上的纵向架构师,单业务领域对它来说变成了子域罢了,方法论是相通的;另外横向架构师一个很重要的职责就是本文的标题,让人人都成为架构师。

;