昨天公司开经营会,晚上又一起聚餐,晚上回到家已经快12点,到家了就睡觉,但是昨天的博客忘记日更。当然还有一个原因就是由于最近还要更新头条文章,当两者都要去兼顾的时候也很容易分心。原来一般都是一早就更新新浪博客,因此很少忘记,而现在一般是早上先更新头条文章,而晚上才同步更新新浪博客,在这种安排下晚上稍微有点事情就很容易忘记。
今天准备再详细讲解下业务系统软件架构设计方面的内容,我在前面的文章专门写过一篇软件架构师应该走出技术狂热误区,锻炼核心架构思维能力的文章。
在这篇文章里面我对架构思维做了一个简单总结。
其核心仍然是我们常说的分解,集成,抽象,复用,分层等思维模型。感兴趣的可以先阅读这篇文章的一些内容。 软件架构师应走出技术狂热误区,锻炼核心全局思维能力
在这篇文章里面更多的是篇架构思维和企业架构方面的内容,因此今天这篇准备从单个业务系统的架构设计来谈下软件架构设计的核心逻辑和内容。
对于软件架构设计,相信很多人都看过温昱写的一本《软件架构设计》书籍,而对于我这篇文章重点会参考企业架构,RUP架构设计和这本书的一些思想做一些展开。
架构设计包括了 功能性架构和技术架构 设计两个部分的内容,功能性架构解决业务流程和功能问题,而技术架构解决非功能性需求等问题。两种架构都包括了动态和静态两个方面的内容,对于功能性架构中动态部分为业务流程驱动全局用例,用例驱动的用例实现等;对于技术架构中动态部分为架构运行机制,而静态部分为框架,分层等方面的内容。
功能性架构包括了全局用例设计,这个本身是用例分析和设计的一个延续,而全局用例分析建议的思路仍然是业务流程,业务用例建模到系统用例建模的过程。
全局用例分析清楚后可以开始考虑子系统和模块的划分,形成系统的功能架构图,当然在划分过程中一定要考虑到 通过CRUD矩阵等分析方法来分析模块如何划分合理 ,如何保证模块本身高内聚和松耦合。
即我们说的从 00-用例视图 如何过渡到 01逻辑视图 架构。
在全局用例分析完成后涉及到数据模型的设计,数据建模仍然从业务驱动,从最初的业务对象和单据入手,到最终的数据概念模型和逻辑模型等。
架构设计中全局数据模型不一定覆盖所有的数据对象和数据表;但是核心的主数据,核心业务单据数据一定要覆盖到,模型到的层次到逻辑模型即可。如果用面向对象的分析方法,这里需要出的是UML建模中的概念模型和逻辑模型, 体现核心对象和类,核心对象和类之间的关系 。
将全局用例分析和数据模型建立融合在一起,可以看到这两者结合起来会形成一个系统完成的领域模型层。一直认为领域模型思路应该引入架构设计, 只有领域模型才是真正关注功能性架构,而不用马上关注到具体的技术分层和技术实现。
前面两者做完后可以看到一个大系统被分解为了多个子系统或模块,那么接着要考虑的就是模块间的集成架构, 分析完集成架构模块间的接口基本就出来了 。接口设计应该是架构设计的另外一个核心内容。要明白架构设计一个重要作用就是架构设计完成后各个模块可以并行开始概要设计,详细设计和开发工作。只要大家都遵循架构设计约定的接口规则即可以了。
以上正好是架构视图中的 01逻辑视图 的关键内容。
集成架构考虑完另外一个核心内容就是公共 可复用组件的抽取和识别 ,包括了功能组件和技术组件,需要识别出来哪些是可复用的,如何进行复用。对于复用层次本身又包括了数据层复用,逻辑层组件复用,界面层UI组件的复用等。复用是架构价值体现的的另外一个关键点。
这些都做完后,接着一个步骤应该在架构设计阶段做的就是对架构输出成功进行模拟验证,前面完成了分解动作, 必须通过模拟验证来看看后续分解内容能否很好的集成和组装 。很多时候我们做架构设计的时候往往不做这块内容,导致架构设计一些内容变成空中楼阁,无法落地。
再回来看技术架构设计,首先谈下静态部分的内容。这里面就包括了 软件开发的分层架构,开发框架等内容,包括开发规范约定,技术平台和语言的选择,使用的规约 等都需要考虑。这些可以看到刚好是4+1架构视图里面的 02开发视图 的内容。很多时候我们看到谈架构的时候说到的三层或多层架构,仅仅是完整架构设计里面很小的一部分内容。
除了分层架构外,接着考虑的就是各种非功能性需要,我们在架构上需要如何设计。
这里面包括了 事务,缓存,异常,日志,安全,性能,可用性,容错能力 等。这些逐个点都要在架构设计中说清楚如何考虑,由于这些本身就属于一个应用系统中技术平台要考虑的内容,因此应该设计为较为公用的技术组件供上层的业务组件使用。要明白很多时候为何谈到AOP或可插拔架构,只有这样去考虑问题,才会考虑真正的功能性架构设计和功能实现和非功能性技术架构这块充分解耦,实现进一步的灵活装配。而这个对应架构视图里面的 03进程视图 。
再回到架构设计视图层面,还需要考虑的就是整个应用系统的部署架构,部署架构本身也包括了逻辑视图和物理视图,应用最终开发出来了如何进行部署,这涉及到了IT基础架构方面的细化,也需要考虑清楚。即架构视图里面的 04部署视图 。
一说到软件架构设计包括的内容,我们比较容易想到的就是传统RUP和面向对象分析和设计里面经常谈到的4+1架构视图,如下:
从这个图也更好的解释了软件架构应该包括 功能性架构和非功能性架构 两方面内容。
逻辑视图即是从功能需求视角,逻辑视图更多的就是组件划分,组件间的依赖关系,当然你可以进一步细化到核心的类和类关系图。但是一般来讲搞清组件和组件间关系最重要。
对于非功能性需求实际有两个方面的考虑。
当然RUP更加强调的是用例驱动,即最中心的是用例视图,用例视图是展开后续架构分析设计的基础。如果熟悉RUP方法论也可以看到,对于用例实现的分析才能够识别出关键的实体类,控制类和边界类,在类识别出来后才能够进行后续的组件划分和接口集成分析等。
在我写这篇文章的时候,到网上搜索关于RUP架构设计的资料,发现最近几年基本没有更新的技术资料,难道是当前敏捷开发和微服务不需要完整的架构设计了?实际上我们看到当前很多微服务项目建设后续出现大量紧耦合,低性能等问题,完全是由于前期架构设计不充分导致。错误的将采用类似SpringCLoud框架作为我们完成架构设计是当前最大的问题点。
当我重新翻阅了下多年前写过的软件架构设计文档,基本即是参考RUP架构设计展开。
但是在进行架构设计的时候,实际我们已经有一些重要的输入,一个是来自于业务系统本身,一个是来源于组织级技术资产积累。
这些一方面是业务系统进行架构设计的输入,同时也是架构设计的关于约束。 架构设计一方面是要实现业务目标和需求,一方面又必须满足关键约束 。这些约束可以技术框架要求,开发语言和数据库要求,多中心化的要求等等,这些在架构设计的时候都必须考虑到。
基于以上思考和近期的项目实践,对于软件架构设计文档我们给出一个新的参考,但是其核心仍然是围绕业务,数据,应用,技术几个维度展开。
即一个完整的软件架构设计应该包括如下内容:
基于该图,有几个关键说明如下:
对于架构设计文档可以拆分为软件架构设计,数据库设计,部署架构设计三个独立文档。而实际我们项目建设和实践三个文档基本也进行拆分。
其次对于软件架构设计文档应该从架构目标约束出发,基于业务需求和用例输入,进行功能性架构设计和非功能性架构设计,同时在这里将技术开发框架和分层架构设计单独出独立章节,即不管是MVC还是SSH,都是标准分层框架模型,你只需要讲清楚架构运行机制即可,不需要和实际业务功能相关。
在关键架构设计内容梳理清楚后,我们再讲解下架构设计文档的核心内容逻辑。
如果熟悉RUP方法论的可能都清楚,在RUP统一过程方法论里面有三个核心。 其一是用例驱动,其二是架构为核心,其三是增量迭代方法论。
在用例分析和设计中,基本梳理清楚了核心的业务功能点,核心的业务对象,同时在详细的用例需求文档里面还有详细的业务基本流,扩展流和业务规则描述。
在传统的用例分析里面我们没有太去强调业务域的划分和业务架构概念,但是这个实际仍然是需求阶段一个重要的内容。即 通过用例分析后我们需要对用例点进行聚合,形成一个业务架构模型图 。
这个业务架构图形成了基本的业务模块或业务组件,如下:
在我们原来的整个软件生命周期管理里面,除了软件需求外,还会有一个独立的产品需求文档,即在产品需求文档里面会进行产品业务模块-》业务单元-》业务功能的分层定义。
也就是说在需求阶段完成了业务建模,并完成了业务组件划分。
有了产品需求和业务架构,那么架构设计阶段就有了重要的参考。但是如果你没有完整的业务建模,那么在架构设计阶段,需要首先做的就是业务组件划分。
对于组件的划分,我在前面一篇文章中给出了一些指导原则可参考:
中台规划中微服务粒度究竟应该如何划分?你可以从以下几点考虑
那么划分的核心点究竟在哪里?
我们通过组件划分要达到高内聚和松耦合,但是这个是划分后的目标而不是方法。真正划分的方法仍然是 围绕业务功能,业务数据,业务流程三者之间的各种CRUD功能和数据交互分析 。
通过交互分析你才能够发现真正的哪些业务功能或业务数据应该聚合在一起。
逻辑视图首先要解决组件划分
对于逻辑视图,对原来的方法做一定的修正。
即传统架构视图里面的逻辑视图更多的是核心类和类关系视图,而个人理解在架构设计阶段并不一定要细化到这个层次,更加重要的是组件划分和组件关系集成视图。
即在逻辑视图设计的时候,需要首先进行组件划分,组件划分的方法可以参考业务建模,如果没有业务建模可参考那么就需要重新进行业务,数据,流程的CRUD分析来划分组件单元。
而对于组件划分的方法,我们可以理解为最传统的纵向划分思路和当前结合了领域建模和SOA分层架构思想的分层组件划分思路。
我们先看一个传统的组件划分的例子,如下:
上图就是一种典型的 纵向划分思路,即业务组件+开发分层 模型形成组件划分和组件架构图。而这种组件划分和设计思路,是很难真正体现我们常说的领域模型设计逻辑的。
基于SOA和领域模型设计的思路,更多的我们应该是首先找到共性的领域层服务能力,然后再基于领域模型能力来构建上层的业务功能和流程功能。
即从 纵向组件划分思路=>SOA和领域分层模型构建思路。
横向分层有点类似你首先去考虑构建领域模型,再来考虑上层业务功能,上层的业务功能模块和领域模型层的组件不再是简单的纵向1对1关系。这个和当前我们做中台架构规划设计很类似,即先考虑如何构建中台共性业务能力,再来考虑前台业务功能如何划分组件。
我基于PLM系统常见的文档管理,ITEM管理,产品结构和变更管理等业务功能组件模块,基于分层构建的思路重新对组件划分和组件模型进行重构,参考如下:
在这里基于个人理解再强调一点,在我们进行架构设计中的逻辑视图设计的时候, 一开始务必不要陷入到技术开发框架的分层模型中去。而是应该找寻最核心的领域模型。
为什么这样讲?
领域模型更多的体现了核心的领域实现逻辑, 这个领域模型本身是和你采用的技术开发框架无关的,你可能是三层MVC框架,也可能是其它的前后端四层框架等等,但是这些本质上都不影响到你领域模型的设计。 这是领域模型的技术无关性。
也就是在RUP的逻辑视图不应该过早陷入到架构分层模型中。
对于技术框架和架构分层,本身是一个完全技术概念,和逻辑模型相反的地方是其本身和业务无关,但是任何的业务功能具体实现,都必须遵循分层架构和层级业务和数据调用流转机制。
当我们理解到这点后,就更加容易理解逻辑视图和开发视图的关系,即如下:
注意组件划分不是一成不变,你开始的组件划分可能并不合理,这个可以在后续用例实现的过程分析中对个别功能点的归属进行调整。
这也是我们说的架构分析设计过程也是一个不断迭代和求精的过程。
当然在需求阶段,我们就应该形成一个全局用例分析图。
在这里我们做两个关键说明,即如下
对于第一点大家容易理解,一个架构设计不可能对所有的用例进行分析。那么什么是关键用例?比如你当前有一个文档管理模块,那么对于文档的增删改查功能实现是否是关键用例?
那么我的答案是这种不是关键用例,我们对关键用例给出一个新的定义,即 关键用例往往是跨了多个组件协同才能够完成的用例 。如果一个用例实现在一个组件内部就能够搞定,那么我们可以把这部分工作放到概要设计或详细设计去做,而不是在架构设计阶段完成。
当我们对关键用例完成分析后,你会发现组件之间的集成和协同关系也就出来了。而且这个时候你很清楚为何组件之间存在这么一个集成点。
将所有的集成点都找到后,组件间的集成关系图也就基本清楚了。
那么再下一步就是基于集成点梳理接口清单。
对识别出来的接口进行分析,看哪些接口需要拆分,哪些接口需要合并,接口服务如何保证我们常说的粗粒度特性。在这个步骤完成后才能够形成完整的接口清单。
到了这里我们基本完成了逻辑视图最核心的内容,即具体应该分为哪些组件,这些组件如何协同来完成关键用例实现。组件之间的接口集成关系是如何的,接口如何抽象和复用。
当然你可以看到逻辑视图可以再进一步细化,比如对于组件关系图你可以进一步细化到类关系图,对于关键用例实现可以细化到类之间的详细方法定义和交互。即逻辑架构设计再朝下面细化了一步,但是整体细化仍然和我们前面谈到的逻辑架构分析设计思路一致。
开发架构关注软件开发环境下实际模块的组织。软件打包成小的程序块(程序库或子系统),它们可以由一位或几位开发人员来开发。子系统可以组织成分层结构,每个层为上一层提供良好定义的接口。系统的开发架构用模块和子系统图来表达,显示了"输出"和"输入"关系。完整的开发架构只有当所有软件元素被识别后才能加以描述。但是,可以列出控制开发架构的规则:分块、分组和可见性。
再次强调下如果你自己没有完成过一个全新系统的架构设计,那么你很难真正了解到架构设计的完整内容。作为一个架构师必须既了解业务,又了解技术,同时做好业务到技术的抽象转换,做好业务和技术之间的桥梁和承接。
你掌握SSH三层框架,或者说你使用SpringCloud微服务框架,完成框架搭建,搞清楚里面的每一个技术组件的作用可能一周时间足够。或者类似你基于Hadoop整个技术体系搭建一个完整的大数据平台,了解数据存储,处理,分析等关键技术组件估计1到2个月也足够。但是你学会后不能叫架构师,核心原因还是架构不能脱离业务,架构是需要业务驱动去解决业务场景和问题的。
脱离业务实际上就没有架构设计,最多只能够算的上架构设计中的技术框架选型。但是我们看到,如果你不清楚具体的业务需求和目标,你连究竟技术框架如何选型也不清楚。
一个好的架构一定不是用最先进的技术,而是用最适用的技术,是真正的业务目标驱动你要用哪些技术,而不是当前流行什么我必须采用什么技术。
在前面有篇文章里面我就谈到过,当前有些技术人员一味的使用新技术,新框架来搭建企业技术平台和开发框架,而不是从企业实际业务需求和架构约束出发。在这个过程中自己能力提升了,技术框架不适用自己可以一走了事,但是企业却很受伤。这不再是简单的技术人员技术狂热问题,而已经是一种个人自私自利行为表现。
那么对于一个开发视图究竟应该包含哪些内容,我们参考网上的一个说法:
简单来说开发架构就是需要 技术框架选型,开发的分层架构,开发标准规范体系,开发技术选型 ,这些属于标准的和业务功能模块无关的内容。
其次就是我们说的基于已有的逻辑视图和功能,你在开发的时候应该如何划分项目,如何分包,整个项目源代码的结构是如何的,各层和各层之间的关系是如何的。
也就是我前面谈到的 逻辑视图+技术框架分层=完整的开发视图
在逻辑视图中的要给组件实现,如果在开发视图中往往可能会体现到多个分层的Package包,比如我们常说的数据层,逻辑层,展现层等。
同时我们考虑到开发的需要,一个大的业务系统我们可能一开发就拆分多个独立的开发项目进行管理,每个项目都可以进行独立的构建和编译。那么整体的开发视图分层应该为:
开发项目(子系统)=》开发包(分层)=》开发功能(具体类文件)
一个完整的开发架构除了选择一种标准的技术开发框架外,更加重要的就是你需要定义清楚具体的开发项目划分,项目里面的目录和分层结构,类文件的命名规范,接口的定义和调用规范等。
这些规范和开发要求将指导我们进行每一个业务功能点的开发。即使最简单的一个业务功能你也可以看到需要具体在每层写哪些类文件,哪些接口和接口实现文件,哪些配置文件等。
在开发架构下,对于逻辑架构里面的组件架构图会得到进一步的细化。
即原来的 组件粒度-》变化为分层构件粒度 。
其次,在开发架构中,我们还需要考虑会引用哪些外包的开源组件,第三方的SDK包等。这些内容属于通用技术框架内容,在逻辑架构设计阶段我们一般不会关心。
我们还是用前面的例子进一步说,一个PLM系统包括文档,产品,变更三个大模块。如果整体我们就一个开发项目,在开发项目内部进行分包,我们开发架构类似如下:
在开发架构构建过程中,我们将文档管理,产品管理和变更管理三个模块建立三个独立的项目,同时当变化为三个独立项目后,我们要求原来包内组件间的依赖,只能够存在业务逻辑层相互依赖和调用,不能再出现跨模块和跨层的依赖调用以减少拆包后的耦合关系。
基于以上场景,我们整个开发架构变化为:
再次,当我们进一步进行前后端分层,同时将数据库访问层进行统一,即数据访问层建立一个统一的开发项目提供对外的数据访问接口。而对于前端开发合并为一个项目进行。而在领域服务层,我们进一步将原来领域逻辑归类到文档服务和产品服务两大领域服务,不再有具体的变更领域服务的概念。
对于文档和产品两大领域服务可以看做是中台的两个大微服务模块,也采用分项目独立开发构建的模式进行。那么在这种变化下,我们的开发架构视图如下:
通过前面几种场景的比较,主要是想说明开发架构更多的是要说明我们的组件如何分包和分层,组件的开发如何与我们当前的技术框架选型,我们的开发模式相互结合。
而这些属于开发态的内容,需要在开发态才能够看得更加清楚。
那么开发视图和运行视图之间的差别又在哪里呢?
到了进程或部署视图,我们就只能够看到具体的独立部署单位,比如上图,只能够看到数据层一个独立的JAR包,逻辑层两个独立的JAR包文件,前端一个独立的JAR包文件。但是对于JAR包文件里面具体的包划分,分层划分在运行态或运行视图我们是看不到的。
我们先摘录一段网上关于进程视图的描述,如下:
进程架构考虑一些非功能性的需求,如性能和可用性。它解决并发性、分布性、系统完整性、容错性的问题,以及逻辑视图的主要抽象如何与进程结构相配合在一起-即在哪个控制线程上,对象的操作被实际执行。
进程架构可以在几种层次的抽象上进行描述,每个层次针对不同的问题。在最高的层次上,进程架构可以视为一组独立执行的通信程序(叫作"processes")的逻辑网络,它们分布在整个一组硬件资源上,这些资源通过 LAN 或者 WAN 连接起来。多个逻辑网络可能同时并存,共享相同的物理资源。
进程是构成可执行单元任务的分组。进程代表了可以进行策略控制过程架构的层次(即:开始、恢复、重新配置及关闭)。另外,进程可以就处理负载的分布式增强或可用性的提高而不断地被重复。软件被划分为一系列单独的任务。任务是独立的控制线程,可以在处理节点上单独地被调度。
大家看了上面的进程视图描述啥感觉?
实际上我们看到在业务系统的架构设计里面往往并不涉及到这么复杂的进程和任务处理逻辑。如果你是设计一个微服务底层架构或消息中间件,我们感觉会出现类似上面的进程视图描述。
比如我们看Dubbo的技术实现框架,就类似于一种进程视图的表现方式。
但是对于大部分业务系统来说,往往并没有复杂的进程视图需要描述。
但是进程视图更多的是运行架构和运行态的软件质量属性总结。
即我们常说的运行态的安全,性能,扩展性,可靠性,吞吐量等都需要在运行视图和运行架构中进行描述。而这些恰好是我们前面提到的非功能性架构设计的基础。
当然对于非功能性架构设计,运行视图是主要内容,而对于开发架构,部署架构仍然需要提供对非功能性架构能力的支撑。比如我们常说的高并发和性能问题,那么你在开发框架和技术选择上就需要考虑,其次在部署架构本身的扩展性上也需要考虑。
对于高可用,高性能和高扩展的非功能性架构设计,我准备单独再写一篇文章来说明,不在这篇文章进行展开。