简介
- 搬砖师,他们的编程和业务能力基本上停留在堆叠代码,按照要求去实现功能需求的层面。
- 工程师,致力于不断提升软件代码的工程质量的程序员,会把写出来的代码改了又改,直到让自己满意为止。
- 架构师,软件架构师和软件工程师最根本的差别又在哪里?掌控全局。光靠把控软件工程师的水平,依赖他们自觉保障的工程质量,是远远不够的。需要一个能够掌控整个工程全局的团队,来规划和引导整个系统的演变过程。
- 软件架构师要对软件工程的执行结果负责,这包括:按时按质进行软件的迭代和发布、敏捷地响应需求变更、防范软件质量风险(避免发生软件质量事故)、降低迭代维护成本。
- 掌控全局的前提是:在自己心中去重新构建出整个世界。在这个过程中,你不需要一上来沉浸在某个技术的实现细节(除非它影响了你对这个世界构建过程的理解),但是你知道整个世界的脉络,知道整个世界的骨架。
- 我们不只是要知道当前的用户需求是什么,我们还要预测需求未来可能的变化,预判什么会发生,而什么一定不会发生。预测什么不会发生最为重要,只有做到这一点,才能真正防止架构的过度设计,把简单的事情复杂化。《程序员修炼之道》预测未来很难, 不要冲出“前灯”范围,任何大到需要“占卜”的任务,都会很快超出理性猜测的范畴。
需求分析
问:我要做一个最小机器人系统,需要考虑需求的变化点和稳定点。该怎么考虑呢?答:挺典型的问题。这个问法是一种典型的需求陈述误区。描述需求需要有几个典型的要素:
- 用户,面向什么人群;
- 他们有什么要解决的问题;
- 我解决这个问题的核心系统。只有满足这几个要素的需求才能进一步讨论变化点和稳定点。最小机器人可能符合上面的第三点,但是用户人群和要解决的问题没有描述,也就无法进一步去思考到底哪些因素是稳定的,哪些是易变的。
设计系统架构的前提是用户需求分析,用户需求包括分析出稳定需求点和变化需求点。从功能上看,稳定需求点一般是实现偏核心需求的需求点,变化需求点往往是实现偏扩展性需求的需求点。从层次结构上看,稳定需求点往往在系统层次的底层,而变化需求点往往在更加抽象层(上层)。从从属关系上看,稳定点需要提供功能给变化点使用,变化点调用稳定点提供的功能。从时间顺序看,稳定需求往往先现是变化点实现的基础,变化点通过调用已经实现的稳定点提供的功能来实现更为抽象的功能。一般的架构设计描述起来类似按自顶向下顺序,采用分治思想完成。但许老师的方法又有些巧:架构好比搭积木,许老师是先有了很多积木(需求点),然后把再确定这些积木放在哪一层次的格子里。这简化了架构设计的难度,好比用市场经济代替计划经济。
对需求的前瞻性探索很重要,也是最难的。要尝试去做前瞻,预测错了并不可怕,但可以事后复盘到底是缺失了什么重要的信息让你判断出现了什么偏差。目前几乎所有的架构课程,都是基于确定的需求来讲技术架构,例如秒杀系统怎么做高可用高并发。架构在于创造,如果你从事的事情总是重复别人,那这个公司又有何价值?即使有所参考,也应该有自己的精气神,这个精气神是需要架构师把它干出来的。
业务系统设计
- 设计思想,比如ddd等
- 设计原则
- 编程范式
如何设计一个复杂的业务系统?云原生在技术上能够最大程度的解决众多非功能性质量和技术需求(如上图),那作为一个企业级应用架构,自然会把专注点转移到业务应用功能性设计本身上来。现在来说对于一个复杂业务架构进行设计,我们要想做到又快又好,无非是两种情况:一是架构师本身对业务理解很深、能力超强、炉火纯青;二是原有的业务系统本身模型清晰,足够的“高内聚低耦合”,可以快速在其基础之上分析业务变化形成新的业务架构设计。从架构设计角度,以下三点是最为关键的:
- 让我们的模型、组件和业务划分尽量靠近变化的本质,比如对于一般电商系统来说,就是用户、商品、交易、支付等,这样的划分能够让我们将变化“隔离”在一定的范围(业务模块)内,从而帮助我们有效减少改变点。
- 设计上,业务模型内部是高内聚,模型之间是低耦合,即各自完成的业务是相对独立的,不会因为一方掉线而牵连另外一方,比如商品推荐功能挂掉了,但是交易和支付业务应该继续正常提供服务,可能提示用户暂时无法提供推荐服务,或者干脆降级为兜底策略。
- 模型、组件在业务上尽可能是复用的,正是这样的复用才成就了今天的互联网级架构,我们不会每做一个电商系统都从零做起。而被“复用”最多的业务模块显然会重点设计和运营,成为核心业务模块。当然架构上这样的电商系统必然也会比较健壮。
业务软件开发的常见病:从一个小的项目不断开发演化变成一个大型业务系统,但随着新需求的不断增加,最终演变成了开发团队的噩梦。而这些噩梦大部分是源于软件的概念完整性遭到了破坏。这些业务代码可能是一代又一代的开发人员各行其道的堆叠起来的(我们又称之为“屎山”),而这个过程中没人有意识的去维护软件的概念完整性。而 DDD 领域设计,特别是 DDD 提供的战略建模层面的概念,是维护软件概念完整性的良药。
架构系统设计
- 高并发系统设计
- 代码层面
- 数据库层面
- 架构设计层面
- 分布式系统为服务化
- 无状态化支持水平弹性扩缩容
- 业务逻辑层面 failfast 快速失败
- 调用链路热点数据前置
- 多级缓存设计
- 提前容量规划等等
- 高可用系统设计
- 代码层面:需要关注分布式事务问题
- 负载均衡问题
- 幂等设计问题。
f(x) = f(f(x))
- 分布锁
- 服务的熔断降级
- 发布方式的可用性
- 数据一致性系统设计
- 事务一致性问题
- 一致性算法
架构分解中有两大难题:其一,需求的交织。不同需求混杂在一起,也就是存在所谓的全局性功能。其二,需求的易变。不同客户,不同场景下需求看起来很不一样,场景呈发散趋势。我们核心要掌握的架构设计的工具其实就只有两个:
- 组合。用小业务组装出大业务,组装出越来越复杂的系统。
-
如何应对变化(开闭原则)。和“架构的本质是业务的正交分解”一脉相承,与其修改模块的业务,不如实现一个新业务。只要业务的分解一直被正确执行的话,实现一个新的业务模块来完成新的业务范畴,是一件极其轻松的事情。模块的业务变化点,简单一点的,通过回调函数或者接口开放出去,交给其他的业务模块。复杂一点的,通过引入插件机制把系统分解为 “最小化的核心系统 + 多个彼此正交的周边系统”。事实上回调函数或者接口本质上就是一种事件监听机制,所以它是插件机制的特例。
- 架构就是对业务系统的正交分解。怎么做业务分解?业务分解就是最小化的核心系统,加上多个正交分解的周边系统(这里面最难的是领域理解。所以需求分析很关键)。核心系统一定要最小化,要稳定。坚持不要往核心系统中增加新功能,这样你的业务架构就不可能有臭味。所以业务做正交分解的第一件事情,就是要分出哪些是核心系统,哪些是周边子系统。核心系统构成了业务的最小功能集,而后通过不断增加新的周边功能,而演变成功能强大的复杂系统。
- 架构行为的三步曲:“需求分析”、“概要设计”、模块的 “详细设计”,背后都直指业务的正交分解,只是逐步递进,一步步从模糊到越来越强的确定性,直至最终形成业务设计的完整的、精确无歧义的解决方案。
- 比框架(架构图)更重要的是数据结构,比数据结构更重要的是接口。为什么数据结构比框架(架构图)更重要?业务数据结构是架构实现机制的灵魂。从共识确认的角度,数据结构相比框架而言,是更重要的共识(为了降低风险,系统设计阶段也应该有代码产出),而架构图显然并不精确。为什么接口更重要?接口是需求的流程分解,即可以体现用户地图。接口是业务的抽象,同时也是它与使用方的耦合方式。一些架构师能够想清楚实现,但是想不清楚业务。他们用实现替代对业务系统的抽象。用实现机制替代业务的典型案例是定义了数据结构,但是不抽象数据的业务逻辑,直接让使用方操作成员变量,或者定义一堆成员变量的 get/set 接口(PS:这个方法/接口不体现业务)。关注业务接口的定义,我们自然就把焦点转向关注业务如何由相互正交的子业务组合而来。PS:作为小组leader,只是设计好了db表结构,并不能防止小伙伴写出读不懂的代码。
- 架构过程是团队共识形成与确认的过程。共识是需要精确的、无歧义的。而架构图显然并不精确。团队没有精确的共识很可怕,它可能导致不同模块的工作牛头不对马嘴,完全无法连接起来,但是这个风险没有被暴露,直到最后一刻里程碑时间要到了,要出版本了,大家才匆匆忙忙联调,临时解决因为架构不到位产生的“锅”。这时候人们的动作通常会走形。追求的不再是架构设计的好坏,而是打补丁,怎么把里程碑的目标实现了,别影响了团队绩效。更精确描述架构的方法是定义每个模块的接口。接口可以用代码表达,这种表达是精确的、无歧义的。架构图则是辅助模块接口,用于说明模块接口之间的关联。为了证明接口的有效性,架构师还应该过一遍所有的用户故事,以伪代码或流程图的方式,把所有用户故事过一遍,确认模块之间的接口串起来是可以正常工作的。代码即文档。代码是理解一致性更强的文档。这样做的好处是,我们把联调工作做到了前头,工程的最大风险就得到了管理。剩下来的就是每个模块自身的好坏,这就和组织能力无关,只取决于我们招聘的工程师个体素质了。所以模块的接口,是架构设计的核心。
许式伟:架构就是对业务系统的正交分解。因此,整个信息科技的演化过程,自然而然形成了分层:基础架构 + 业务架构。基础架构的产生是对业务架构不断深入理解的过程。越来越多的共性需求从业务架构抽离出来,成为信息科技的基础设施。作为架构师,我们需要坚持对业务进行正交分解的信念,要坚持不断地探索各类需求的架构分解方法。这样的思考多了,我们就逐步形成了各种各样的架构范式。这些架构范式,并不仅仅是一些架构思维,而是 “一个个业务只读、接口稳定、易于组合的模块 + 组合的方法论”,它们才是架构师真正的武器库。发现架构无法很方便地支持某个需求,就意味着架构存在缺陷。
《程序员修炼之道》一个改变不影响另一个,则这些事物是正交的。非正交系统天生复杂,难以变更,当组件之间高度依赖时,就没有局部修理这回事。大部分人对设计正交系统的必要性都很熟悉,只不过他们可能会使用其他一些词来描述这个过程,例如模块化、基于组件和分层等。 编写害羞的代码——模块不会向其它模块透漏任何不必要的信息(基于存储、计算、通信实现),也不依赖于其它模块实现。
就像机械系统,一定要考虑公差。系统要与误差共舞,要能抵抗变化,必须处于一个动态的平衡点。
人
首次揭秘,字节跳动数据平台为什么不选“纯中台制”我们在组织层面做了一些创新,设置了数据 BP 机制。BP 全称是 Business Partner,类似于 HRBP,组织形式上是集中式的,可以统一管理调配,执行上分布式到各个业务,解决业务问题。数据 BP 同学在组织上都汇报在数据平台,统一培养和调度,相互学习经验的角度,对中台能力也保证足够的熟悉度,以便根据不同业务的特性,灵活组合,提供综合性的数据解决方案,也保证了复用性,不轻易重复造轮子。在具体工作时,他们会扑在不同的业务线上,跟业务同学坐在一起,把自己视为业务线的一部分,保障与业务一起成功。
演进
半文:前瞻性(想到是做到的前提)以及基于前瞻性判断的架构设计,一定是可考察的。可考察就意味着一定是有方法的。是有“套路的”。否则根本没办法做出一个面向未来的架构,只能凭运气或者架构演进&维持。
- 架构演进,扩展性。这么多年了spring的IOC本质发生变化了吗?没有。为什么?因为是基于我们核心架构定位(DI,控制翻转等)出发来定义的。这样本质不变,主体结构就不变,发展就是横向以及纵向发展的。
- 横向:会有更多的平台产品,业务场景出现,但是关联关系不变。
- 纵向:会有更多的前后功能延伸出现,但是本质不变(比如spring做了很多的cloud)
- 网状:关系变化,所以我们建议把“独立”作为核心架构原子概念,这样关系就是另外的一个概念(半文链接理论)。
- 业务演进,一定还是符合互联网扩展形式的。如果我们不是做商业模式的扩展。那么一定是原有商业模式的商业效率扩展。比如,淘宝并没有更改交易的本质,营销也是类似于传统的吆喝。场地费其实就是地皮寻租的概念。整个互联网社区,从最早的BBS-天涯-贴吧-校内-微博-知乎-抖音-小红书。等等。可以抽象定义出来不同的模式和不同的内容载体(视频,文字,图片)。然后传播从以前的单点-广播-网状-核心KOL。但是想想,和最早以前人们下班了之后,搬起来小板凳村口唠嗑。没什么本质的区别。业务演进部分,最终就是回归到:架构延续这个命题上的。因为业务本质是不会发生变化的,所以我们围绕架构定位设定的核心领域模型,就不会发生根本的变化。除非我们的领域发生变化了。
其它
要提升架构能力,首先得做到规格为先,而不是实现为先。不要动不动问怎么实现的。要首先谈这个规格合不合理,是否存在多余的依赖。进一步来说,要多去谈这个函数(或软件实体)的业务范畴合不合理,是否应该换一个切分的姿势。在代码量非常大的时候,人的脑容量就完全无法把这个实现装到头脑中。这时 “规格重于实现” 背后的意义就完全体现出来了。通过规格串起整个业务系统,以此把业务系统装到脑子里,这就是很朴素的架构 “骨架” 思维。在极有限的时间里,在没有电脑的情况下,我们只能选择把更多的逻辑装进脑子里。这个过程还可以更进一步。我们不断训练自己对不同业务领域的架构范式的理解。直至最终,我们头脑中可以装得下整个信息科技的骨架。
架构师要不要做全才?架构师绝对不是要把自己打造为全才。架构师掌控全局的核心思想是打通经络,让自己的内力在全身自然流通,浑然一体。在不影响理解的情况下,你需要放弃很多实现细节的钻研,但有一天你需要细节的时候,你能够知道存在这些细节,并且快速钻研进去。架构师核心是把知识串起来,构建一个完整的认知,不留疑惑。大部分知识是不需要深入细节的,只在你需要的时候深入,但深入的时候要很深。高阶的技术可以按需学,按精力学,更根本的还是要打好基础,这也更有助于你判断是否应该深入学习某些技术。
云计算:云计算从跨组织协同的角度来看,不过是一种新的交付方式。我们不再是源代码交付,而是服务交付。所以,你也可以把云计算看着一种外包,我们称之为服务外包。,云计算与传统外包不同,它对结果负责,有服务 SLA 承诺。简化了业务方的业务系统,让它得以能专注自己真正的核心竞争力的构建。