【领域驱动设计】系统架构的思考集合
一、什么是软件架构
软件架构(software architecture),是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。软件架构是一个系统的草图。
构架不仅是结构。软件系统的构架是指系统重要构件的组织形式。再确定构建边界的前提下,对这些构建间的关系,进行组织的一种高层次表达。
五种视图确定一个软件的架构
- 逻辑架构:(领域划分)产品架构,应用架构。逻辑架构关注的是功能,包含用户直接可见的功能,还有系统中隐含的功能。或者更加通俗来描述,逻辑架构更偏向我们日常所理解的“分层”
- 开发架构:(技术选型)开发架构则更关注程序包,不仅仅是我们自己写的程序,还包括应用程序依赖的SDK、第三方类库、中间件等。
- 运行架构:(执行原理)更关注的是应用程序运行中可能出现的一些问题。例如并发带来的问题,比较常见的“线程同步”问题、死锁问题、对象创建和销毁(生命周期管理)问题等等。开发架构,更关注的是程序运行前的一些准备工作,在静止状态下就能规划好做好的,而运行架构,更多考虑的是程序运行后可能发生的一些问题。
- 物理架构:(部署架构)物理架构,更关注的系统、网络、服务器等基础设施。例如:如何通过服务器部署和配置网络环境,来实现应用程序的“可伸缩性、高可用性”。或者举一个实际的例子,如何通过设计基础设施的架构,来保障网站能支持同时10W人在线、7*24小时提供服务,当超过10W人或者低于10W人在线时,可以很方便的调整部署架构来支撑。
- 数据架构:(存储层解决方案)数据架构,更关注的是数据持久化和存储层面的问题,也可能会包括数据的分布、复制、同步等问题。更贴切来讲,如何选择需要的关系型数据库、流行的NOSQL,如何保障数据存储层面的性能、高可用性、灾备等等
二、软件架构的价值
1、在合理的时间成本约束下,保证软件性能可接受,系统健壮性,系统可维护,系统可扩展,系统可理解。
2、解决研发团队的协作,以及研发工作效率。
三、软件架构原则
1、架构并不由系统的功能决定,而是由系统的非功能属性决定
这句话直白的解释就是:假如不考虑性能、健壮性、可移植性、可修改性、开发成本、时间约束等因素,用任何的架构、任何的方法,系统的功能总是可以实现的,项目总是能开发完成的,只是开发时间、以后的维护成本、功能扩展的容易程度不同罢了。
当然现实绝非如此。我们总是希望系统在可理解、可维护、可扩展等方面表现良好,从而多快好省的达成系统背后的业务目标。
2、软件架构合理性的3个启发式规则
-
可考核(Accountable):好的软件架构让每个团队都有自己负责的业务目标
-
可自主(Autonomous):好的软件架构让每个团队都一定的自主性可以独立往前跑,而不总是被其他团队阻塞
-
可复用(Amortized):好的软件架构鼓励对未来投资,使得基础设施的成本可以被摊销
重要程度:可考核>>可自主>可复用。
然后SOA和DDD又来告诉我们“可自主”才是最重要的。但是我发现实践中,无论是“可自主”还是“可复用”都很模棱两可。很难用这两个原则去说服其他人,用X的方式来分解问题会比用Y的方式来分解问题更好。但是如果你说,这么分解可以让每个团队更可考核,就显得特别理所当然。
具体的度量标准
3、架构合理性的核心指标-高内聚和低耦合
耦合的定义和分类,以及解释
内容摘自百度百科:耦合
系统耦合始源于物理学,在物理学上耦合是指两个实体相互依赖于对方的一个量度,分为以下几种:
-
非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
-
数据耦合:一个模块访问另一个模块时,彼此之间是通过简单数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的。
-
标记耦合:一组模块通过参数表传递记录信息。这个记录是某一数据结构的子结构,而不是简单变量。
-
控制耦合:如果一个模块通过传送开关、标志、名字等控制信息,明显地控制选择另一模块的功能,就是控制耦合。
-
外部耦合:一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
-
公共耦合:若一组模块都访问同一个公共数据环境,则它们之间的耦合就称为公共耦合。公共的数据环境可以是全局数据结构共享的通信区、内存的公共覆盖区等。
-
内容耦合:如果发生下列情形,两个模块之间就发生了内容耦合:①一个模块直接访问另一个模块的内部数据 ;②一个模块不通过正常入口转到另一模块内部 ;③两个模块有一部分程序代码重叠(只可能出现于汇编语言中) ;④一个模块有多个入口
耦合度量有底到高的:非直接耦合<数据耦合<标记耦合<控制耦合<外部耦合<公共耦合<内容耦合
4、顶层框架的设计思想
从框架思想上来说,它更像是一个通信规范的制定者,它已经明确了一个通信过程应该是什么样的。并且它就像是设计模式中的模板方法模式一样,定义好了流程,并且预定义了一些组件,帮你实现这百分之八十的需求。它天生就是为可扩展而设计的,给使用者预留了自定义的接口。
四、软件架构的一种套路-价值链分析
4.1、Step1:战略分析模型
https://www.cnblogs.com/shangxiaofei/articles/16066930.html
4.2、Step2: 业务价值链分析
https://www.cnblogs.com/shangxiaofei/articles/16071327.html
业务领域设计(业务模型) = 业务流程模型(横轴价值链)+ 企业级数据模型(数据关系和分类)
【概念】数据模型和流程模型的组合,可以清楚的描述出,什么样的事件或条件可以触发一组业务活动,业务活动需要的输入有哪些,经过业务流程的处理,输出又有哪些。如果将该业务系统化,就成为实现业务活动的计算机程序是在什么样的场景(事件)下开始执行,程序需要读取哪些数据(实体),依据什么样的顺序(活动)、规则(任务)由谁(组织、角色)执行,执行之后产生哪些数据(实体)
【原则】数据模型的粒度原则:数据模型都有主题域这个层级,就是将关系较近的数据实体聚合成一个分类,这种关系我们可以给出一个主题名称。
在软件设计上,是可以考虑将关系较近的数据实体聚在一起,按照行为接近数据的原则,再将相应的功能聚合成一个组件。结合业务模型,就可以将与主题域中与实体相关的任务聚在一起构成业务组件。聚类过程中要注意:
- 数据主题域中的数据实体可能存在引用其他主题域数据实体的情况,这种情况下,在进行任务聚类时不会考虑此类数据实体,因为它们应当由其所在的数据主题域相关的组件创建,以保证在企业级业务系统中,数据生成职责的唯一性,这是应用企业级数据模型时非常重要的一点。
- 与数据实体相关的任务主要指对数据实体进行新增、修改、删除的任务,对同一数据实体进行新增、修改、删除操作的任务应当归属同一组件。只有这些任务具有数据的写权限,其他任务只具有读权限,这也是保证企业级数据一致性的重要措施。
【过程】流程模型与数据模型之间的配合检查是一个反复锤炼的过程。尽管标准化问题很重要、很困难,不幸的是,并没有什么很好的方法能够帮助大家快速解决问题,这就又回到了之前说的,模型质量严重依赖建模者的经验,除了经验之外,还要依靠高质量的建模输入,既包括完善的业务资料,更需要有丰富经验的业务人员
数据模型的标准化的过程,基于之前的竖井式开发。各个不同业务领域对于相同的数据实体都会有一套自己的业务流程,此时关于数据实体的领域划分就要做出决策。
在对应过程中,经常会遇到多个不同的任务都可能要对同一个数据实体在不同时间进行写操作的情况,比如,个人客户初次到一个银行存钱,申请银行账户时,银行要建立客户的信息,会包括姓名、证件类型、证件号码等基本信息,也会包括电话等联系信息,或者邮寄地址等地址信息,这时的整体业务场景是存款。而客户过了一段时间再次来办理业务时,联系信息可能会有变化,这需要更新客户信息,但是此时的场景有可能发生变化,不再是来存款,可能是来购买实物黄金,从产品的角度,这就是两个不同的业务领域了。
那么,在进行企业级标准化以前,对客户信息的建立和修改完全有可能在存款和实物黄金的领域各有一套流程,可能是任务级别的重复,也能是在不同的任务中各自的内容有重复,实际上,以前做竖井式开发的时候,这是很常见的现象,每个业务系统都是独立的、完整的,都各自有一套客户信息,不仅重复,最重要的是经常会不一致。
当我们通过企业级数据模型去除重复的数据概念之后,通过任务与实体之间的写操作对应关系,会清晰地发现重复的操作。这时我们就需要做出建模的决策,是分开建模还是将所有对客户信息进行写操作的部分集中到一起建模。在 FSDM 提供的数据模型上,参与人这个分类中可以容纳与客户信息相关的所有数据,建模上可以把此类实体聚集在一个主题域下,比如叫做客户主题域,那么从企业级的角度也就可以将各业务领域中与之相关的任务或者涉及到该操作的任务中的客户信息部分全部抽离出来,集中到起来组成一个组件,而其他领域的任务经过调整后,不再包含此类内容,这就完成了一个标准化过程。
【数据标准化案例二】当多领域包含类似内容,做出“整合”抽象出一个主题域冲动,是否整合的两个度量标准
- 业务:业务上自然是要重新审视、理清业务流程,搞清楚具体差异。
- 数据:重新检视数据实体划分的颗粒度是否正确,是否包含的属性太多而导致内聚性不够。数据实体的颗粒度太小,会放大业务差异,而颗粒度太大,则会抹杀业务差异,二者都会导致不合理的标准化结果。
4.2、Step3: 由业务模型推导出架构方案,业务架构师和技术保持沟通,确保架构落地
五、软件架构的一种套路-业务架构,数据架构,产品架构,应用架构,技术架构
5.1、Step1:以产品目标为导向,进行领域问题枚举(现在和未来),保证产品后续迭代的可扩展性
【第一步】:业务域的分解,首先是从系统需求入手,在需求初期可能你就得到的只是一句比较模糊的需求描述,这些需求可能来自于老板、运营或者用户(比如下图的场景)。直接把这句话作为核心产品功能是不恰当的,合理的做法是先把这个产品的所有问题域列清楚。
问题域,是指自己的产品能够解决的所有问题的空间集合。从核心需求出发,将所有当前需要解决、未来可能需要解决的问题放入产品框架的范围,能够帮助你的产品架构拥有更高的可拓展性,在后续具备迭代和优化空间。
-
找到收到的需求中,跟产品形态、产品目标相关的语句,去列出“xx 的流程会是什么样”、“xx 该如何达成”之类的问题,直到如果这些问题解决,能够实现核心需求的方向和业务目标。
-
去逐次寻找这些问题需求被解决的过程中,是否有其他要先解决掉的问题、或者其他跟业务相关的问题能够被解决。
-
按照层级去罗列所有的问题,并附上自己的初步回答,从而形成一个初步的、自己的产品能够解决的“问题域”。
【第二步】:确定产品方向
经过问题域的罗列后,能够得到一个模糊的产品方向和功能范围。问题域的环节非常发散,这一步需要回归基础,把模糊的需求补充、拓展和翻译成一个能够在商业模式和用户体验能够形成闭环的产品需求。
在确定产品方向这个环节的详细操作步骤:
-
产品用户:需要明确产品定位的用户,解决核心用户的核心需求。
-
核心目标:思考清楚自己产品的核心目标是什么。如果以一个 KPI 指标衡量产品的价值,那这个 KPI 应该是什么。
-
依赖系统:自己的系统需要与哪些外部系统存在交互和关联关系,我们的系统在这个大的生态下,是什么定位。
【第三步】:绘制业务流程
业务流程是业务架构中比较常见的图,这一步需要根据产品需求和问题域,按照业务域的流程进行绘制。
5.2、step2:核心流程示例推导功能矩阵,功能矩阵分层,水平拆分,垂直拆分,产出业务架构和产品架构
【第四步】:业务功能矩阵
通过对业务流程进行分析,根据功能职责,进行垂直分解,识别出业务功能和业务服务,将他们归类到相应的流程节点中去。将业务流程和业务功能点组合成一张业务功能矩阵。这张矩阵图是业务架构中为后续的数据架构、产品架构、技术架构作为重要的输入。
【第五步】:功能架构分层
产品框架脱胎于业务流程,但相比业务流程,更加注重产品功能的枚举、功能模块之间的分界。以业务架构的业务功能矩阵图作为输入,将流程图转换成按节点进行分层,节点的功能点存放在同一层中。
在此基础上将明显是同一个产品范围、同一组产品功能的模块放在同一层级,得到一个基础的产品框架。
【第六步】:明确功能边界
一个具备前后台关系的产品架构图至少分三层:
-
用户感知层(在何种场景下通过何种方式触达用户)
-
功能模块层(通过哪些功能模块实现产品的核心功能、和哪些外部平台功能有信息交互)
-
数据层(产品的数据从哪里来、产品的数据沉淀到哪里去)。
在上一步进行简单分层后,我们已经得到一个初步框架,但是难免会有分层不明确的问题。此时需要按照两种维度来处理架构图的层级:不同信息层级的边界、同一层级内模块和模块的边界。
处理不同信息层级的边界
架构图的层级表达其实是信息之间的流转关系,不同信息层级之间一定是有逻辑关系的.
处理同一层级内子模块的边界
各层次之间虽有相关,但同一层次内的子模块之间一定是相互独立、界限分明的。将解决不同问题的功能拆分成两个子模块,做到一个问题只在同一层解决,避免牵一发而动全身的情况出现。
产品架构在业务架构的基础上,按照解决的业务问题域,划分出不同的功能模块,再根据功能模块间的关系,组合成子系统。
应用架构在产品架构的基础上考虑两个事情:
-
第一、考虑的是子系统间的关系。
-
第二、考虑将可复用的组件或模块进行下沉,沉淀到平台层,为业务组件提供统一的支撑。
应用架构的标准
-
清晰的应用边界。
-
应用之间的调用关系明确。
-
有入必有出,有输入系统、必有输出系统。
-
清晰的呈现应用的全局关系。
应用架构的划分方式-水平划分和垂直划分
水平划分原则:按照同一产品范围的模块放在同一层级的原则,得到水平层面的应用系统划分。
垂直划分原则:应用内按业务进行切分,保证子应用是相互独立。
当应用内存在几个相对独立的模块,每个模块的业务逻辑差别比较大,且内部的组成较为复杂和庞大时,还需要进一步对应用内进行子系统的切分。
比如风控系统的案例中,在风控引擎这个应用中,存在实时、离线的校验场景。每种场景都是相对独立。这时候将这三个模块按照子系统进行切分成:实时风控引擎子系统、离线风控引擎子系统。
5.3、step3:基于业务架构推导数据实体,产出ER图
5.4、step4:基于产品架构确定应用架构,基于应用架构确定技术架构
【技术架构】技术选型的战略原则和战术原则
战略原则=>合适原则、简单原则、演化原则
- 【合适原则】-适合优于业界领先:没有那么多人,却想干那么多活。(新技术的学习成本,维护成本,潜在未知隐患成本)
- 【简单原则】-避免结构复杂,逻辑复杂,简单原则就是大道至简。结构上的复杂性存在的第一个问题是,组件越多,就越有可能其中某个组件出现故障,从而导致系统故障。假设组件的故障概率是 1%(有 1%的时间不可用),那么 2 个组件的系统可用性是 99%*99%=98%,5 个组件的系统可用性是 99%*99%*99%*99%*99%=95%,两者相差 3%。说明组件越多,系统稳定性就越差。
- 【演化原则】-演化原则就是演化优于一步到位
战术原则=>高并发,高可用,业务设计原则
- 【高并发】-无状态:水平扩展能力;拆分:系统维度,功能维度,读写维度;服务化:微服务;消息队列
- 【高可用】-降级,限流,熔断,缓存,可回滚
- 【业务设计原则】-防重设计;幂等设计;流程定义;状态与状态机;后台系统操作可反馈;后台系统审批化;文档注释;备份