Open Tiny-Box opened 3 years ago
这里我们首先讲一个定律:康威定律
在设计系统时,组织交付的方案结构将不可避免地与其沟通结构一致
这个是在讲,我们在将问题分解成各种小型问题时,虽然这种方式可以将问题更容易委派,但是它会引发更多的协调问题。很多时候我们会为了解决这些协调问题准备沟通协作方式或者等级制度,但是这两种方法都是很僵化的。 比如我们按照传统的职能分团队,分出前端,后端,db,运维等团队,一旦涉及到跨领域业务变更时,基本跟惊群似的全都启动。这种情况,按最坏的场景来想,每个团队都会专注于交付各自组件,没人care真正端到端的价值,最终这些组件并没有办法在组织内高效运作,甚至没办法正常运行。
如果说上面这段还是有点抽象的话,我举一个具体的例子:每一个底层架构团队,都会遇到推上游升级的问题(你没遇到说明你写的东西没人用),“为什么上游们不更新呢?”,当这个天问出现了后,就是康威定律在跨越50年后,依旧在这片土地释放着自己曾经的隐喻。
那么这个时候,演进式架构是一种方式,或者说一种思路,去缓解这种低效的、推脱式的合作方式。其实我们在之前听过了很多架构方式,增量式、持续式、敏捷式、响应式等等,但是这些词并没有准确地表达架构的推进方式,演进式架构更关注两点:增量与引导。 持续啊,增量啊,这些词都在表达随着时间变化,这确实是演进式架构关键的特征之一,但是他们都没有说明,架构要怎么样变化,或者说期望架构的目标是什么。引导这个词反映了我们想要的架构。 相比于可适应,我们更希望称架构是演进的,因为我们是希望架构在时间的维度上清晰健壮,而不是经历了修修补补后更难理解。适应这个词更像是短时间的妥协,只要系统能work就行,但是为了能真正的演进,你必须支持真正的变化,而不是权宜的考虑。这里更像是一个生物学的隐喻,演进是一个过程:一个适用于在不断变化的环境中持续运行的系统,系统会有特适性,但架构应该关注整体的演进性。
飞轮这个词我是从字节这里了解到的,我觉得很契合这里,就在这里用了
这个飞轮大概分成这几部分(简单画了一个图)
适应度函数其实是一个遗传算法里面常用的东西,被用来收敛结果。这里讲适应度函数,是指有一个东西你需要用它来衡量整个系统演进方向是否在正轨。 你注意到了,即使上面这句定义,我也没有使用准确定义而是在用侧面描述。一方面是我现在还没有对它有足够的认识,所以我先做阐释。
首先我们在描述中讲了,它是来衡量系统方向的,那么:
他们都算也不全算,举一个简单的例子,当我们有一个功能,他的要求是50ms内响应,那么我们需要一个UT来对这段功能代码做耗时测试,并将它集成在CI/CD中,每一次的上线都会对其运行来确定当前耗时是否符合预期。这样的系统叫适应度函数。
它不是一段代码或是一个组件,而是一个抽象的概念,指你现在的系统在演化,你需要技术的手段来控制是否满足你的预期。所以,不是单纯的UT或脚本就算这些。(当然,我相信绝大多数的系统连UT都没有【笑)
越早越好,你接手一个架构后,最重要的事情就是确认架构系统内是否有足够的适应度函数来支撑你的设计,如果没有,你的活儿就来了。
变更的前提你提前有了足够的适应度函数支撑了,在那之前你如果要做架构性调整,还是想想就好 因为各个工程的历史背景完全不一样,我在这里不详细讲实践,说的更多是论点性的东西,如果你真的关心实践,可以讨论 or 看书里的几个例子(虽然我觉得那几个例子我用不上)
变更的前提你提前有了足够的适应度函数支撑了,在那之前你如果要做架构性调整,还是想想就好
因为各个工程的历史背景完全不一样,我在这里不详细讲实践,说的更多是论点性的东西,如果你真的关心实践,可以讨论 or 看书里的几个例子(虽然我觉得那几个例子我用不上)
这部分的判断标准很简单也没有直接的红线(就像多少行代码应该换行一样),评估的标准是模块和函数的依赖项个数,以及模块的入参与出参数。谨慎地使用extra这样的字段,你根本不知道业务会对它做些什么。
举个简单的例子,两个模块都依赖的consumer,是否我们应该抽象出一个consumer基类来处理这个?不一定!每一个复用的模块都意味着另外的责任,而微服务本身应该做到的是领域内实体的隔离,追求复用可能会对模块的基础易用性造成损害。所以我们提出重复优先于耦合,各自的领域业务去建立自身的特性,虽然我们了解重复的缺点,但是这个操作消除了耦合。
当我们在引入各种组件时,需要去考虑是否有防腐层来隔离它。这么说有点抽象,我举个实际的我刚刚干完的活儿。 下游对notice的发送进行了改造,需要上游业务对notice发送方法进行更新,我这里建立一个notice发送服务来收敛所有业务侧的notice,这个操作看起来非常的无关必要(因为还加了一个中间层),但它其实提供的就是一个防腐的作用,今天有可能使用了nsq来发送,明天提供了消息中台来操作,而我这个服务向上游提供的是notice发送这个语义,而并非notice发送的方法,即使明天又有大的升级,改动和影响都是可控的。 引入与更换的组件,需要架构想清楚抽象出来的语义是什么,而不是暴露方法给业务。
代理模式基本有几大组件构成:
这几种组件的组合可以让你的系统更清晰的了解业务路径,也非常方便的做单点的测试和解耦。但它的问题就是缺失集中化的中介,协调和处理全链路的错误处理,测试会是一大难点,而且,事务的实现上是一个挑战 为了解决这些问题,需要架构对各个事件处理器做合适的业务内聚(方便处理业务异常与合并事务),在链路中使用关联性的ID来传起整个链路,确定测试与调试。
与代理模式相比,中介模式更像是将业务整体做成了一套中介的总线,从演进的角度看,中介作为最大的耦合点,将所有的服务绑在了一起,所有的下发并行处理,统一收口,各项处理器不会直接通信,中介自己定义了重要的工作流。
这个我其实不想说太多,因为大家或多或少都接触过,早期的服务导向型最常见的特性就是服务消息总线的建立 这种系统是完全没有什么演进性(我相信也是很多公司的主体架构设计),大量系统在去做复用和抽象,而业务的快速迭代又需要新的特性,倒逼架构 or 自己做架构,从而让整个系统的架构量子无限扩大,各团队间协调矛盾重重 所以,微服务出现了 微服务很多地方都在说,我这里只提几点,微服务的建模一定是需要基于领域的,而不是基于技术抽象。它不是为了完全解耦而存在的,架构需要做的是界定服务间的上下文,让服务完全无共享,至于服务内的重复和适当的耦合,不是最重要的事情。
《演进式架构》 《领域驱动建模》
其实我很好奇一个问题,这一点我也在思考
对于“演进式”架构这样一种通用的方法论?我们应该从那些方面来 review 一个架构迭代的效果?
前言
这里我们首先讲一个定律:康威定律
这个是在讲,我们在将问题分解成各种小型问题时,虽然这种方式可以将问题更容易委派,但是它会引发更多的协调问题。很多时候我们会为了解决这些协调问题准备沟通协作方式或者等级制度,但是这两种方法都是很僵化的。 比如我们按照传统的职能分团队,分出前端,后端,db,运维等团队,一旦涉及到跨领域业务变更时,基本跟惊群似的全都启动。这种情况,按最坏的场景来想,每个团队都会专注于交付各自组件,没人care真正端到端的价值,最终这些组件并没有办法在组织内高效运作,甚至没办法正常运行。
如果说上面这段还是有点抽象的话,我举一个具体的例子:每一个底层架构团队,都会遇到推上游升级的问题(你没遇到说明你写的东西没人用),“为什么上游们不更新呢?”,当这个天问出现了后,就是康威定律在跨越50年后,依旧在这片土地释放着自己曾经的隐喻。
那么这个时候,演进式架构是一种方式,或者说一种思路,去缓解这种低效的、推脱式的合作方式。其实我们在之前听过了很多架构方式,增量式、持续式、敏捷式、响应式等等,但是这些词并没有准确地表达架构的推进方式,演进式架构更关注两点:增量与引导。 持续啊,增量啊,这些词都在表达随着时间变化,这确实是演进式架构关键的特征之一,但是他们都没有说明,架构要怎么样变化,或者说期望架构的目标是什么。引导这个词反映了我们想要的架构。 相比于可适应,我们更希望称架构是演进的,因为我们是希望架构在时间的维度上清晰健壮,而不是经历了修修补补后更难理解。适应这个词更像是短时间的妥协,只要系统能work就行,但是为了能真正的演进,你必须支持真正的变化,而不是权宜的考虑。这里更像是一个生物学的隐喻,演进是一个过程:一个适用于在不断变化的环境中持续运行的系统,系统会有特适性,但架构应该关注整体的演进性。
架构的飞轮
这个飞轮大概分成这几部分(简单画了一个图)
1. 构建适应度函数
什么是适应度函数?
适应度函数其实是一个遗传算法里面常用的东西,被用来收敛结果。这里讲适应度函数,是指有一个东西你需要用它来衡量整个系统演进方向是否在正轨。 你注意到了,即使上面这句定义,我也没有使用准确定义而是在用侧面描述。一方面是我现在还没有对它有足够的认识,所以我先做阐释。
首先我们在描述中讲了,它是来衡量系统方向的,那么:
他们都算也不全算,举一个简单的例子,当我们有一个功能,他的要求是50ms内响应,那么我们需要一个UT来对这段功能代码做耗时测试,并将它集成在CI/CD中,每一次的上线都会对其运行来确定当前耗时是否符合预期。这样的系统叫适应度函数。
它不是一段代码或是一个组件,而是一个抽象的概念,指你现在的系统在演化,你需要技术的手段来控制是否满足你的预期。所以,不是单纯的UT或脚本就算这些。(当然,我相信绝大多数的系统连UT都没有【笑)
什么时候加入适应度函数?
越早越好,你接手一个架构后,最重要的事情就是确认架构系统内是否有足够的适应度函数来支撑你的设计,如果没有,你的活儿就来了。
2. 进行增量变更
在做抽象的时候,需要评估模块的耦合
这部分的判断标准很简单也没有直接的红线(就像多少行代码应该换行一样),评估的标准是模块和函数的依赖项个数,以及模块的入参与出参数。谨慎地使用extra这样的字段,你根本不知道业务会对它做些什么。
在服务中应该重复优先于耦合
举个简单的例子,两个模块都依赖的consumer,是否我们应该抽象出一个consumer基类来处理这个?不一定!每一个复用的模块都意味着另外的责任,而微服务本身应该做到的是领域内实体的隔离,追求复用可能会对模块的基础易用性造成损害。所以我们提出重复优先于耦合,各自的领域业务去建立自身的特性,虽然我们了解重复的缺点,但是这个操作消除了耦合。
防腐层的构建在业务中至关重要
当我们在引入各种组件时,需要去考虑是否有防腐层来隔离它。这么说有点抽象,我举个实际的我刚刚干完的活儿。 下游对notice的发送进行了改造,需要上游业务对notice发送方法进行更新,我这里建立一个notice发送服务来收敛所有业务侧的notice,这个操作看起来非常的无关必要(因为还加了一个中间层),但它其实提供的就是一个防腐的作用,今天有可能使用了nsq来发送,明天提供了消息中台来操作,而我这个服务向上游提供的是notice发送这个语义,而并非notice发送的方法,即使明天又有大的升级,改动和影响都是可控的。 引入与更换的组件,需要架构想清楚抽象出来的语义是什么,而不是暴露方法给业务。
两个具有演进式架构的设计
事件驱动型
事件驱动型(代理模式)
代理模式基本有几大组件构成:
这几种组件的组合可以让你的系统更清晰的了解业务路径,也非常方便的做单点的测试和解耦。但它的问题就是缺失集中化的中介,协调和处理全链路的错误处理,测试会是一大难点,而且,事务的实现上是一个挑战 为了解决这些问题,需要架构对各个事件处理器做合适的业务内聚(方便处理业务异常与合并事务),在链路中使用关联性的ID来传起整个链路,确定测试与调试。
事件驱动型(中介模式)
与代理模式相比,中介模式更像是将业务整体做成了一套中介的总线,从演进的角度看,中介作为最大的耦合点,将所有的服务绑在了一起,所有的下发并行处理,统一收口,各项处理器不会直接通信,中介自己定义了重要的工作流。
服务导向型
这个我其实不想说太多,因为大家或多或少都接触过,早期的服务导向型最常见的特性就是服务消息总线的建立 这种系统是完全没有什么演进性(我相信也是很多公司的主体架构设计),大量系统在去做复用和抽象,而业务的快速迭代又需要新的特性,倒逼架构 or 自己做架构,从而让整个系统的架构量子无限扩大,各团队间协调矛盾重重 所以,微服务出现了 微服务很多地方都在说,我这里只提几点,微服务的建模一定是需要基于领域的,而不是基于技术抽象。它不是为了完全解耦而存在的,架构需要做的是界定服务间的上下文,让服务完全无共享,至于服务内的重复和适当的耦合,不是最重要的事情。
Reference:
《演进式架构》 《领域驱动建模》