架构实战案例解析
01 | 架构的本质:如何打造一个有序的系统?
我们知道,现在的软件系统越来越复杂,当然相应地,架构的作用也越来越明显。作为开发人员,我们每天都在和架构打交道,在这个过程中,对于架构也经常会产生各种各样的问题:
- 什么是架构?架构都有哪些分类,分别解决什么问题呢?
- 怎样才是一个好的架构设计?我怎么才能成长为一名优秀的架构师呢?
这些问题涉及我们对架构的认识,也是学习和运用架构的开始。所以,今天,我们就来深入地分析架构的实质,让你能够透彻地理解它。
作为专栏的第一讲,我希望先和你讨论架构中理念性的部分,就是所谓架构的道,这样可以指导你学习后续的实操层面的内容,也就是架构的术。
接下来,我们就正式开始吧,先说下我对架构本质的理解。
架构的本质
物理学中有个很著名的“熵增定律”:一个封闭系统,都是从有序到无序,也就是它的熵(即混乱程度)会不断地增加,最终系统会彻底变得无序。这个理论放在软件系统的演化上,也是非常适用的。
一方面,随着业务需求的增加,我们会往系统里不停地添加业务功能;另一方面,随着访问量的不断增加,我们会不断通过技术手段来加强系统非业务性功能。如果事先不做良好的设计,随着时间的推进,整个系统野蛮生长,就会逐渐碎片化,越来越无序,最终被推倒重来。
不过,自然界中的生物可以通过和外界交互,主动进行新陈代谢,制造“负熵”,也就是降低混乱程度,来保证自身的有序性,继续生存。比如,植物通过光合作用,把光能、二氧化碳和水合成有机物,以此滋养自己,延续生命。对于软件系统,我们也可以主动地调整系统各个部分的关系,保证系统整体的有序性,来更好地适应不断增长的业务和技术变化。这种系统内部关系的调整就是通过架构实现的,所以,架构的本质就是:
通过合理的内部编排,保证系统高度有序,能够不断扩展,满足业务和技术的变化。
这里包含两层意思,我们具体展开说下:
首先,架构的出发点是业务和技术在不断复杂化,引起系统混乱,需要通过架构来保证有序。我们知道架构这个词来源于建筑行业,那为什么建筑行业需要“架构”呢?
搭一个草房子很简单,可以直接上手;盖一个 2 层楼房,稍微复杂一些,但在工匠的经验指导下,问题也不大;而盖一座高楼,复杂性就大不一样了,我们需要考虑内部结构、承重、采光、排水、防雷抗震等,这就需要专业人员事先做好整体的架构设计,并严格地按照设计来施工。
这里,你可以看到,建筑里的架构不是天然就有的,而是因为建筑越来越复杂,我们需要通过架构来管理这种复杂性,避免建造过程的失控。
软件系统也是如此,从简单的桌面应用发展到现在的大型互联网平台,这个过程中,系统规模越来越大,业务和技术也越来越复杂。我们同样需要通过架构设计,消化复杂性带来的混乱,使系统始终处于一个有序状态,能够应对现有和将来的需求变化。
其次,架构实现从无序到有序,是通过合理的内部编排实现的,基本的手段,就是“分”与“合”,先把系统打散,然后将它们重新组合,形成更合理的关系。
具体地说,“分”就是把系统拆分为各个子系统、模块、组件。拆分的时候,首先要解决每个部分的定位问题,然后根据定位,划分彼此的边界,最后实现合理的拆分,我们比较熟悉的微服务架构,就是一种典型的拆分做法。
“合”就是基于业务流程和技术手段,把各个组件有机整合在一起。比如说在微服务架构中,拆分为具体微服务后,我们需要对这些服务进行归类和分层,有些属于底层基础服务,有些属于上层聚合服务,还要尽可能地实现服务的平台化,比如我们最近说的中台,这些都是合的思想体现。
这个分与合的过程将系统的复杂性分解为两个层次:
首先,各个子系统承担独立的职责,内部包含了自身的复杂性。子系统的复杂性对外部是透明的,外部不用关心。
其次,子系统通过封装后,简化为职责明确的一个点,因此,我们只需要在合的过程中,解决各个点之间的依赖关系,这样就可以定义出系统整体。
举个例子,我们都知道 GoF 的 23 个设计模式,在 Builder 模式中,它的主逻辑只需要给出各个部件的组装关系即可,它不关心创建某个具体部件的内部逻辑,这个可以交给工厂模式去实现。这里,Builder 模式负责粗粒度的组装逻辑,它承担的是合的部分;工厂模式负责细粒度的构造逻辑,承担的是分的部分,大家各自管理自己的复杂性。
通过合理的“分”与“合”,系统不是回到了原点,而是把原先铁板一块的系统变成一个富有弹性的结构化系统。这样,系统的复杂性有效地分解了,系统的有序度大幅度地提升了。
当然,系统的复杂性是多方面的,有技术上和业务上的,架构也是一个体系,会有多种架构一起来应对这些复杂性挑战。那么接下来,我们就来具体看下。
架构的分类
按照不同的角度,架构可以有很多分类,但一般来说,主要分为业务架构、应用架构和技术架构。那么,这些架构分别为谁服务,解决什么问题,相互之间是什么关系呢?回答这些问题前,我们先来看下系统的落地过程。系统首先由人来开发,然后由机器来运行,人和机器共同参与一个系统的落地。
对于负责开发的人来说,比较头疼的是,业务太复杂,脑子想不清楚,即使当前勉强把业务逻辑转化为代码,系统后续的维护也是问题。因此,开发人员的要求是系统概念清晰,业务逻辑容易理解,可以直观地进行代码开发。
对于负责运行的机器来说,比较头疼的是,外部请求并发量太大,导致机器扛不住,有的时候,硬件还会出问题。因此,它的要求是系统能够水平扩展,支持硬件容错,保证系统的高性能和高可用。
这里,开发的痛点主要由业务架构和应用架构来解决,机器的痛点主要由技术架构来解决。为什么这么说呢?我们看下,这些架构具体都是做什么用的。
简单来说,业务架构就是讲清楚核心业务的处理过程,定义各个业务模块的相互关系,它从概念层面帮助我们理解系统面临哪些问题以及如何处理;而应用架构就是讲清楚系统内部是怎么组织的,有哪些应用,相互间是怎么调用的,它从逻辑层面帮助我们理解系统内部是如何分工与协作的。
技术架构就是讲清楚系统由哪些硬件、操作系统和中间件组成,它们是如何和我们开发的应用一起配合,应对各种异常情况,保持系统的稳定可用。所以,技术架构从物理层面帮助我们理解系统是如何构造的,以及如何解决稳定性的问题。
这里你可以看到,业务架构、应用架构和技术架构,分别从概念、逻辑和物理层面定义一个系统。业务架构给出了业务模块的划分和依赖关系,这也大致决定了应用系统如何分工和协作,当然这不需要严格地一一对应,比如一个商品业务,可能对应 3 个应用,一个前台商品展示应用、一个后台商品管理应用,以及一个商品基础服务,但这不影响我们从逻辑上理解,一个业务场景,有哪些应用参与,并且它们是如何协作的。
而技术架构呢,通过保障应用的稳定运行,最终保证业务不出问题。比如在大促的时候,多个应用可能会受大流量冲击,技术架构就要考虑怎么通过技术手段,保障相关的应用能够处理高并发,从而保证大促顺利进行。
这里,我举个拍电影的例子,来帮助你更直观地理解这三种架构的关系:业务架构定义了这个电影的故事情节和场景安排;应用架构进一步定义有哪些角色,每个角色有哪些职责,并且在每个场景中,这些角色是如何互动的;技术架构最后确定这些角色由谁来表演,物理场景上是怎么布置的,以此保证整个拍摄能够顺利完成。
最后,我想强调一下:系统是人的系统,架构首先是为人服务的。因此,业务概念清晰、应用分工合理、人好理解是第一位的。然后,我们再考虑技术选型的问题,保证系统非功能性目标的实现。所以做架构设计时,一般是先考虑业务架构,再应用架构,最后是技术架构。
什么是好的架构?
从上面的内容,我们不难看出,一个好的架构必须满足两方面挑战:业务复杂性和技术复杂性。
1. 业务复杂性
系统首先要满足当前的业务需求,在此基础上,还要满足将来的业务需求,因此系统要能不断地扩展变化,包括调整现有功能,以及增加新功能。而且,系统的功能变化不能影响现有业务,不要一修改,就牵一发动全身,到处出问题。因此,在架构设计上,要做到系统的柔性可扩展,能够根据业务变化做灵活的调整。
此外,市场不等人,上新业务要快,之前花了半年上了个业务,这回再上个类似的新业务,需要短时间就能落地。因此,架构设计上,还要做到系统功能的可重用,这样才能通过快速复用,实现业务敏捷和创新。
2. 技术复杂性
要保证一个业务能正常运行,除了满足业务功能之外,还要保证这个系统稳定可用。一个复杂系统是由很多部分组成的,如应用程序、服务器、数据库、网络、中间件等,都可能会出问题。那怎么在出问题时,能够快速恢复系统或者让备用系统顶上去呢?
还有流量问题,平时流量不大,少量机器就可以处理,但在大促的时候,大量流量进来,系统是不是能够通过简单地加机器方式就能支持呢?
此外还有低成本的问题,系统能否做到,使用廉价设备而不是高大上的 IOE 设备,使用免费的开源组件而不是昂贵的商业套件,使用虚拟化技术而不是物理机,并且在流量低谷和高峰的不同时期,让系统能够弹性缩容和扩容呢?
这些都属于技术性的挑战,解决的是系统的非业务性功能,也都是架构设计要支持的。
因此,一个好的架构设计既要满足业务的可扩展、可复用;也要满足系统的高可用、高性能和可伸缩,并尽量采用低成本的方式落地。所以,对架构设计来说,技术和业务两手都要抓,两手都要硬。
那么,一个优秀的架构师需要具备什么样的能力,才能设计一个好的架构呢?
什么是好的架构师?
一个优秀的架构师,应具备很强的综合能力,要内外兼修,“下得厨房,上得厅堂”,下面我来通过典型的架构方式,来介绍一名优秀架构师应该具备的能力:
一个驾校教练,必定开车技术好;一个游泳教练,必定游泳水平好,因为这些都是实践性很强的工作。架构师也是一样,TA 必定是一个出色的程序员,写的一手好代码。
在此基础上,架构师要有技术的广度(多领域知识)和深度(技术前瞻)。对主流公司的系统设计非常了解,知道优劣长短,碰到实际问题,很快就能提供多种方案供评估。
此外,架构师还需要有思维的高度,具备抽象思维能力。抽象思维是架构师最重要的能力,架构师要善于把实物概念化并归类。比如,面对一个大型的 B2C 网站,能够迅速抽象为采购 -> 运营 -> 前台搜索 -> 下单 -> 履单这几大模块,对系统分而治之。
架构师还需要有思维的深度,能够透过问题看本质。透过问题看本质是由事物的表象到实质,往深层次挖掘。比如,看到一段 Java 代码,知道它在 JVM(Java Virtual Machine,Java 虚拟机)中如何执行;一个跨网络调用,知道数据是如何通过各种介质(比如网卡端口)到达目标位置。透过问题看本质,可以使架构师能够敏锐地发现底层的真实情况,以端到端闭环的方式去思考问题,能够识别系统的短板并解决它。
还有很重要的一点,能落地的架构才是好架构,所以架构师还需要具备良好的沟通能力(感性),能确保各方对架构达成共识,愿意采取一致的行动;而良好的平衡取舍能力(理性),可以确保架构在现有资源约束下是最合理的,能让理想最终照进现实。
总结
我今天和你分享了架构的本质,架构的终极目标是保证系统的有序,通过拆分和整合,使系统具有柔性,能够进化,从而可以满足现有的和将来的各种变化。如果你能深入地理解架构的这些本质和手段,就可以不用照搬某某大厂的方案了,而是能够根据实际情况,以最合理的方式来解决系统面临的问题。这里呢,我也分享了架构的三种典型分类,包括它们各自的定位和相互关系,相信你现在对架构整体有了一个简明的框架,知道架构设计都要做哪些事情了。
最后,我还为你提供了高标准的架构师能力模型,这样,你能比较清楚自己的努力方向是什么,这些要求很高,但你也不要有任何的畏难情绪,你可以在架构实践中,逐步地往这个目标上靠近,通过本专栏后续的学习,相信你也可以更快地达到这个目标。最后,给你留一道思考题:除了本文提到的三种架构,你还知道有哪些架构分类,它们分别做什么用?
为什么开发人员要学习架构?
这其实可以从系统和个人发展这两方面来看。
一、无架构,不系统
现在的软件系统规模越来越大,业务上和技术上都非常地复杂,大一点的互联网公司,技术人员都有几千号人。那么,如何开发这么复杂的系统?如何有效地组织他们的工作呢?在这里,一个好的架构设计无疑是至关重要的,无论你是有一定经验的开发人员,还是已经开始从事系统设计的架构师,深入学习和理解架构都是必不可少的,掌握好架构设计,可以让你轻松应对技术和业务的挑战。但是很多技术人员,由于个人项目经验有限,又缺乏很好的学习途径,对架构设计一知半解。在实际工作中,不能把握好架构设计的度,要么设计不足,要么过度设计,导致系统变来变去,严重影响开发效率和质量。
二、拓展你的职业发展空间
此外,对于技术人员来说,公司通常会提供两个职业发展通道供你选择:管理路线和技术路线。现实中,大部分同学应该都是走技术路线的,很多程序员的职业发展目标,也都是想要成为一名优秀的架构师。这不仅仅意味着更优渥的薪水和更持久的职业生涯,更因为在架构师这个舞台上,你可以凭借个人出色的架构能力,为项目的落地发挥巨大的作用,你会有更大的成就感。所以说,无论从软件发展的趋势,还是从个人职业发展方向上考虑,你都应该拥抱架构,主动学习,尽快成长为一个能力全面的架构师。
业务架构篇
02 | 业务架构:作为开发,你真的了解业务吗?
你好,我是王庆友,今天我们一起聊聊业务架构。作为开发人员,我们平常讨论比较多的是技术层面的东西,比如 Spring 框架、Redis 缓存、MySQL 数据库等等,我们喜欢讨论这些,是因为纯技术的东西比较通用,和业务相关性不大,沟通起来比较方便。
但你要知道,一个项目能否成功落地,首先需要的是把业务分析做到位,至于选用什么技术来实现,这是我们第二位才去考虑的因素。从架构角度看,业务架构是源头,然后才是技术架构。所以,作为专栏的第二讲,今天我们就从业务架构开始说起。
在软件开发的过程中,你肯定知道需求分析是怎么回事,但不一定知道业务架构设计是怎么回事;你也肯定清楚需要产品经理这个角色,但不一定清楚有时还需要业务架构师这个角色。关于需求分析和业务架构设计,相信你经常会有以下几个疑问:
- 业务架构师和产品经理有什么区别?
- 需求分析和业务架构设计有什么区别,业务架构到底有什么用?
我们知道,项目的开发都是从收集业务需求开始的,原始的需求一般来自于最终用户。但是,每个用户其实只清楚自己所负责的那部分,因此这些原始需求往往是零散和碎片化的,特别是当一个业务流程跨多个部门的时候,更没有一个人能够说清楚这个业务的全貌。
所以说,仅仅基于这些原始的需求来指导开发是远远不够的,这时,就需要产品经理和架构师介入进来,填补这段空白。接下来,我们就一起看下,产品经理和架构师在这个过程中都会做些什么,他们是如何帮助业务落地的。
产品经理的职责
简单来说,产品经理的职责就是:告诉用户,系统长什么样子;告诉开发,他要实现什么功能。产品经理首先会收集用户的原始需求,然后,将它们梳理成一个个业务流程,每个业务流程由多个业务步骤组成。一个业务步骤包含三部分的内容:输入、输出和业务功能。
比方说,一个典型的交易流程,它包含商品浏览、商品加购物车、下单、支付等步骤。其中,下单步骤的输入,就是订单的各种信息,下单的功能,就是整合这些信息,创建一个具体的订单,而下单的输出结果,就是新创建的订单。
需求梳理好后,产品经理会把每个步骤具体化为页面原型。在原型中,会以直观的方式给出各个步骤的输入或输出,以及用户的操作过程,最后再把这些页面串起来,形成一个业务流程。
你可以看到,经过产品经理的工作,大量零散的原始需求经过梳理和关联,变成一系列有序的业务流程,以及流程里面的业务步骤(业务步骤也称之为业务节点),然后产品经理把这一系列的业务流程和业务节点以用户界面的方式定义出来,总的来说,产品经理定义了系统的外表。
这些产出对于用户了解系统长什么样子,应该如何使用这个系统,以及系统是否满足他们的需求来说,是足够的,但对于开发者来说还远远不够,因为他们需要能进一步看到系统的内部结构。而这一步,就是业务架构师要做的事情。
业务架构师的职责
在这之前,我们不妨先思考下,如果是按照产品的输出,直接以业务流程的角度来构建系统,会是什么样子呢?如果按照这个思路,我们将为每个业务流程搭建一个对应的系统模块,然后业务流程中的每个业务步骤,将对应系统模块中的一个接口,包括它的功能、输入和输出。
就拿前面的购物流程来说,我们设计一个购物流程模块,里面包含商品查询、添加购物车、下单和支付接口,来分别对应流程里的 4 个业务步骤。
以这样的方式构建系统,表面上看起来,业务和系统的映射好像非常简单,但在实际中,落地的难度非常很大。因为只是这样一个小小的购物流程模块,就要同时涉及商品、购物车、下单和支付四个业务,模块的开发者要同时非常清楚这四部分的数据模型和业务逻辑。
同样的道理,系统里的其他模块也是包含多个业务领域的内容,如果一个业务领域的需求发生了变化,比如说,订单要增加一个新的状态,那么所有涉及该订单的模块都要知道这个变化,并要做出相应的调整。这就要求,每个开发者都是全知全能的,对所有业务都了如指掌,我们知道,这是不可能的。
每个业务都有其本身的专业性,比如订单业务、商品业务、支付业务,它们的数据模型和业务逻辑都相当复杂,构成了一个个相对独立的业务领域。如果我们是按照业务流程来划分系统模块,结果是把不同业务混在了一个模块里,所以,这种模块划分的方式并没有降低总的业务复杂度。
我们可以换一种做法,先把所有的业务流程拆散,这样得到了一堆业务节点;然后把业务节点进行归类,相关的业务节点放在同一个系统模块里。判断节点是否相关,主要看它们是否属于同一个业务领域,比如一个订单查询的节点,和订单创建的节点,它们都属于订单域,那么这些节点都可以归属到同一个订单模块里。
下图就清楚地表示出了系统模块按业务流程拆分,和按业务域拆分的不同。
- 如果按照业务流程来拆分系统模块,那么,有多少业务流程,就有多少个系统模块,这个对应关系比较直接,但实现起来很困难。
- 如果按照业务域来拆分,有多少业务领域,就有多个系统模块,流程中的业务节点按照业务域的不同,可以划分到不同的系统模块。
在实际业务场景中,一个业务节点可能会涉及不同业务领域的功能。比如说,一个下单节点,会涉及到获取商品信息、获取用户信息、扣库存、下订单等多个业务功能,那么你就可以进一步分解这个节点的功能,把不同的功能分到对应的业务域和系统模块。
基于业务域,构建了系统模块后,我们就可以按照这样的方式还原整个业务流程,比如上面的购物流程例子,我们就可以这样还原它:
购物流程 = 商品模块. 商品搜索 + 购物车模块. 添加商品 + 订单模块. 创建订单 + 支付模块. 支付
如果你把这个定义画成序列图,就很直观和容易理解,也比较符合开发人员思维,系统实现起来非常容易。通过这种系统模块之间的不同功能组合,我们很容易给出各个业务流程的定义。
所以,对业务架构师来说,TA 的工作,就是把业务流程和节点打散,按照业务域的维度来划分系统模块,并定义这些模块之间的关系,最终形成一个高度结构化的模块体系。这样,开发人员既容易理解业务,又方便落地系统。
现在,我们就可以回答文章开头的问题了,产品经理和业务架构师的工作既有区别又有联系,简单地说,产品经理定义了系统的外观,满足了用户;业务架构师在此基础上,进一步定义了系统的内部模块结构,满足了开发人员。
当然,满足现有的业务需求,保证系统顺利落地,这只是业务架构的最基本目标,业务架构的意义远不止于此,它有一系列更高的目标,下面,我就逐一为你展开介绍。
架构目标之一:业务的可扩展
第一个目标是业务的可扩展,我们都知道,业务需求是不断变化的,不断创新是业务的内在要求。而对于系统来说,它的要求却是相对稳定,尽量避免大的调整。
那么,我们如何才能实现业务的快速变化和系统的相对稳定呢?
这也是业务架构要重点解决的问题,具体地讲,业务架构设计要能支持打造一个柔性系统,通过提供良好的业务扩展性,允许业务不断调整和快速生长。
可以看到下图中,左边部分就比较形象地展示了业务和系统的不同特点:业务的主题是变化和创新,系统的主题是稳定和可靠。
在右边图中,我们通过巧妙的业务架构设计,很好地解决了业务和系统之间的矛盾。
这里,我们把业务平台和业务线剥离开,让业务平台封装基础通用的功能,这样,它就变得相当地稳定;让各个业务线包含自己的个性化需求,业务线只依赖业务平台,业务线彼此之间互相独立,可以自由变化。这样的业务架构设计,就同时保证了系统的相对稳定和业务的快速创新。
为了帮助你更好地理解业务架构的扩展性,这里,我给出了支付宝的业务架构变化过程。
在支付宝一代的业务架构中,前台的业务和后台的业务直接耦合,形成了多对多的网状结构,如果修改一个后台业务线,就会影响到很多前台业务线;如果增加一条新的前台业务线,需要同时和很多后台业务线对接,这样的架构无疑是对业务的扩展非常不利的。
而在支付宝二代业务架构中,你会发现,他们在前后台业务线之间,构建了独立的支付清算平台,从而实现了前台业务和后台业务的解耦。在这里,不管前台业务,还是后台业务,都只需要对接中间的支付清算平台,把系统的变化收敛到一个点,而业务线之间相互不影响,这样的方式,自然可以很好地支持业务扩展。
好了,这里我们说完了业务架构的可扩展目标,接着再说说业务架构的另一个目标:可复用。
架构目标之二:业务的可复用
你肯定会有这样的体验:一个项目过来,你和伙伴们一起加班加点、紧赶慢赶,总算把它成功落地了。结果这时候又有另一个类似的项目过来,你们又要按照同样的方式,重新吃一遍苦,结果就是开发不满意,项目经理不满意,老板也不满意。
对于类似的业务需求,如果原来做的工作可以大量复用的话,这是非常理想的结果,无论对于开发效率和开发质量的提升都很有帮助。
当然,能不能复用,能在多大程度上复用,这和业务架构设计很有关系,也是业务架构设计的重要目标之一。
那么,业务架构设计如何实现业务的可复用呢?
你可以试想一下,在业务架构设计中,如果只是简单地基于业务流程来定义系统模块,这个系统模块就要和业务流程严格对应。我们知道,业务流程对应业务场景,而业务场景是经常变化或是定制的,这就导致系统模块也是经常变化和定制的,那么,这样的系统模块就很难在不同业务场景中复用。
如果我们按照业务域来划分业务,把业务流程中的节点拆分到各个业务域,按照业务域构造系统模块,这样的复用效果会如何呢?我们都知道,业务域是相对固定的,它有明确的数据模型和业务规则,这样一来,系统模块也就比较固定和通用,也就具备比较好的复用基础。
但要想实现高复用,业务架构对系统模块的定义,还有更多的要求。
首先,模块的职责定位要非常清晰。对于模块来说,在定位范围内的职责要全部涵盖到,而不在这个范围的职责全部不要。
其次,模块的数据模型和接口设计要保证通用。架构师需要归纳业务场景,通过抽象提炼,形成通用化的设计,以此来满足多个类似场景的需求。
小提示:清晰的模块定位和通用化设计,是模块能够复用的内在要求。
最后,实现模块的高复用,还需要做好业务的层次划分。我们知道,越是底层的业务,它就相对更固定。举个例子,同样是订单业务域,对于底层订单的增删改查功能,不同类型的订单都是一样的,但对于上层的订单生命周期管理,外卖订单和堂食订单可能就不一样。
所以,在做高复用设计时,我们可以尝试把一个业务域按照层次拆分得更细,比如,把订单模块拆分为多个上层订单模块和一个基础订单模块,这样,基础订单模块对于所有类型的订单,都能够提供复用。
就拿当前非常流行的微服务架构来说,很多公司在微服务的基础上,通过服务分层,进一步落地了共享服务体系和中台架构,这些都是业务架构复用能力的体现。
下面是一个三方支付平台的业务架构图,你可以看下,在一个实际的业务架构中,模块是怎么划分的,架构的可扩展和高复用是如何体现的。
总结
今天,我带你了解了产品经理和业务架构师的不同职责,产品经理是站在用户的角度进行需求分析,而业务架构师是站在开发者的角度定义系统内部结构。通过今天的讲解,你应该对业务架构也有了更清楚的认识。除了满足当前的业务需求外,业务架构师还需要面向未来,实现业务的可扩展和高复用两大目标,我也大致介绍了架构师实现这些目标的思路。在接下来的文章里,我还会针对这两大目标,结合实际案例,具体讲解如何实现它们,让你能更加深入地理解业务架构设计,并可以在工作中学会去运用这些手段。
老师好,如何展示或用什么工具去展示业务架构和应用架构,能让人更容易理解,有时去画这两种架构时常常会混在一起,自己画着都分不清了,不知道老师是否遇到这种情况,如何避免和解决这种情况
作者回复: 混在一起问题也不大,很多时候业务架构和应用架构长的差不多。一般来说三个图: 1. 模块的静态结构图,描述各个模块的层次关系 2. 模块的动态关系图,比如业务流,数据流,调用关系 3. 针对核心的场景,补充交互序列图,从具体过程理解交互关系 或者你静态关系图偏业务架构,动态关系偏应用架构。 最后根据需要,可能还需要状态机,核心库表模型等等。主要是说明问题,大家理解清晰就可以。
作者回复: 业务线是个大的概念,比如美团有外卖业务线,打车业务线,酒店业务线等,一个业务线包含多个业务流程,比如外卖订单有它的流程,堂食订单有它的流程。如果多个业务线共享业务域,比如订单是通用的,那这个业务域可以下沉,变成一个共享的基础业务域,不属于那个业务线。
03 | 可扩展架构:如何打造一个善变的柔性系统?
在实际工作中,业务需求总在不断变化,因此,你经常会面临以下这些问题:
- 如何快速地上线新业务?老板很可能明天就想看到效果。
- 对某个功能进行修改,如何不影响到系统其它的功能?
对于新的需求变化,我们一方面要快快搞定,另一方面要稳稳接住。但问题是软件虽然姓“软”,但也不是想变就能变,如果事先没有经过良好的设计,调整起来,往往牵一发动全身,导致系统到处出问题。
那如何设计一个具有良好扩展性的系统,能够快速支持业务变化落地呢?接下来,我们围绕系统的可扩展,先来了解下什么是系统,什么样的系统才能具备良好的扩展能力。然后通过一个实际的例子,说明如何通过架构手段打造一个可扩展的系统。
系统的构成:模块 + 关系
我们天天和系统打交道,但你有没想过系统到底是什么?在我看来,系统内部是有明确结构的,它可以简化表达为:系统 = 模块 + 关系。
在这里,模块是系统的基本组成部分,它泛指子系统、应用、服务或功能模块。关系指模块之间的依赖关系,简单地讲,就是模块之间有调用,我们知道,调用区分发起方和服务方,因此,依赖关系是有方向性的。这个模型虽然简单,但它给我们提供了一个深入分析系统的工具。接下来,我们就从业务扩展性出发,讨论什么样的模块是容易修改的,什么样的依赖关系是容易调整的。
模块
我们先看模块,模块定义系统都有哪些基本的“玩家”,分别承担什么职责。从业务的角度看,每个模块都代表了某个业务概念,或者说业务领域。模块内部由数据和业务逻辑组成,其中数据是核心,业务逻辑围绕着数据,对数据做进一步加工,方便外部使用。
从扩展性的角度出发,首先,我们对模块的要求是:定位明确,概念完整。
每个模块要有明确的定位,模块有了定位,说明我们已经想清楚了它的核心职责是什么,这样,每个人对它的期望和理解就会一致。在实践中,我们经常会争论一个功能应该放到 A 模块还是 B 模块,表面上看,各有各的道理,谁也说不服谁,但如果对照模块的定位,回到模块设计的初心,我们往往很快就能有答案。
定位比较抽象,在具体划分模块职责的时候,要保证模块业务概念的完整性。数据上,模块需要覆盖对应业务领域的全部数据,比如一个订单模块,它要覆盖所有渠道的订单,包括三方平台的订单、自有商城的订单、线下门店的订单等,这些不同类型订单的数据模型和实际数据,都由订单模块负责。
功能上,模块要包含业务领域的全部功能,比如订单模块包含所有订单相关的功能,包括订单数据的增删改查、订单业务规则校验、订单的状态和生命周期管理等。
其次,模块还要:自成体系,粒度适中。
模块的业务逻辑尽量围绕自身内部数据进行处理,对外部依赖越小,模块的封装性越好,稳定性也越强,不会随着外部模块的调整而调整。
模块的粒度要保持适中,不能为了追求定位清晰,把粒度划分得很小,导致系统的碎片化。比如系统早期的时候,一般我们把积分功能放到用户模块里面,不单独构建积分模块,如果后续积分的概念越来越突出,承载的业务越来越复杂,到时候可以把积分功能分离出来,单独成模块。
这里,为帮助你更好的理解,我举一个模块划分的反面例子。在实际工作中,很多老系统都有体量很大的模块,我们称之为“肿瘤”,它的特点就是定位模糊,职责泛滥,功能无所不包,这样,模块的可维护性很差,没人敢轻易对它动刀子。好了,说完了模块,我们再继续看下模块的依赖关系。
依赖关系
依赖关系定义了模块如何协作,一起完成业务流程,依赖关系实质上体现的是模块的组织结构。
如果不对模块的依赖关系做针对性设计的话,依赖关系就是一个多对多的网状结构,一个有 N 个模块的系统,理论上有 N×N 个依赖关系,如果考虑依赖具有方向性,这个数字还要加倍。
所以,要简化模块的依赖关系,我们就要同时简化依赖的方向和减少依赖的数量。
首先,我们希望模块之间的依赖是单向的,尽量避免相互调用,为什么单向更好呢?我们知道业务流程是有顺序的,如果模块依赖关系越直观地体现业务流程的顺序,越能帮助人理解,否则,我们会被双向的依赖箭头绕的晕头转向,很难通过模块之间的依赖关系还原实际业务的处理过程。
接下来,我们看下模块的组织结构。我们知道,网状结构是一种松散的结构,节点之间的依赖关系比较复杂,一般用于表示非正式的关系,比如人群的社交关系;而层次结构是一种更有序的结构,一般用于表示正式的关系,比如公司内部的人员关系。
在模块的组织结构设计上也是如此,我们要尽量把网状结构转化为层次结构,模块结构层次化是简化模块依赖关系的有力手段。
具体做法就是,我们按照模块定位的不同,把模块划分为不同层次,比如划分为上面的应用层和下面的资源层。这样,一个层通过把多个模块组织在一起,就形成了概念上更大粒度的模块。有了层以后,我们理解业务时,因为模块定位相同,往往关注这个更大粒度的层就可以,依赖关系只要指向这个层,而不是层里面的各个模块。这样,从人理解业务的角度,依赖的数量大幅度地减少了。
另外,我们知道,层与层之间的依赖关系都是层与层之间自上而下的依赖,相对于多对多的网状依赖,层次依赖的方向更清晰,特别符合人的理解习惯。
举个具体例子,作为开发,我们都比较了解 MVC 架构,系统模块按照定位,分为表示层、应用层、聚合服务层、基础服务层。
- 表示层,对应前端的模块,如 App、小程序、公众号等,属于 View 层。
- 应用层,对应和前端表示层直接关联的服务端,属于 Control 层。
- 聚合服务层,如果系统业务比较复杂,经常需要单独的聚合服务层负责业务流程的编排组合,这个属于 Model 层的加强。基
- 础服务层,代表最基础的业务模块管理,如订单、商品、用户等,属于实际的 Model 层。
我在这里贴了一张 MVC 分层结构图,你可以看到,模块总体上是非常清晰的层次结构。
现在,我们清楚了一个可扩展系统对模块和依赖关系的要求,接下来,我们再回到系统扩展性目标,做个深入总结。
扩展性的本质
在文章开头,我们说因为业务总在变化,所以需要架构设计给系统提供良好的扩展性。
这只是表象,深层的原因是,一个新的需求进来,系统不只是为它增加一个新功能这么简单,系统的调整会引起一系列的连锁反应,从而大面积地影响系统的现有功能。架构设计时,如果模块划分的不好,一个 N 个模块的系统,它的复杂度就是 N×N(这个在上一讲介绍的支付宝一代架构中,体现得很明显)。如果再加一个新的模块,复杂度就变成 (N+1)×(N+1),系统的复杂度随着功能的数量指数级地上升,这样一来,当系统的规模到一定程度,复杂度就会失控,导致系统彻底无序。
所以,要支持系统的扩展,架构设计上必须能够控制系统的复杂度,面对新需求,要让系统复杂度做加法而不是乘法,从而保证系统的调整是局部化和最小化的,所以,业务架构扩展性的本质是:通过构建合理的模块体系,有效地控制系统复杂度,最小化业务变化引起的系统调整。
那如何打造一个合理的模块体系呢?具体的架构手段就是按照业务对系统进行拆分和整合:通过拆分,实现模块划分;通过整合,优化模块依赖关系。
接下来,我们以一个在线出行公司为例,它有出租车、快车和顺风车 3 条业务线,来具体看下如何为它打造合理的模块体系。
接下来,我们以一个在线出行公司为例,它有出租车、快车和顺风车 3 条业务线,来具体看下如何为它打造合理的模块体系。
打造可扩展的模块体系:模块拆分
我们先对系统进行模块化拆分,拆分有两种方式:水平拆分和垂直拆分。
水平方向拆分
水平拆分是指从上到下把系统分为多层,按照系统处理的先后顺序,把业务拆分为几个步骤。
比如,整个叫车过程,我们可以分为 UI 展现、地图搜索、运力调度和订单支付等几个环节,这是根据系统的处理过程进行划分的。
这样一来,我们就把一个复杂流程,分解为几个相对独立的环节,分别进行处理,这么做带来了很多好处。
首先,UI 展现部分独立成为一个模块,实现了前后端的分离。我们知道,前端的用户体验和界面样式会经常变化,而后端的数据和业务逻辑相对稳定,通过水平拆分,我们实现了稳定部分和不稳定部分的分开,避免相互影响。
这里的后端包含三个模块,其中地图搜索负责路径规划,运力调度负责人车匹配,订单支付负责交易管理。
可以看到,通过水平拆分,可以使每一块职责都比较明确,功能内聚,每个模块管理自己内部的复杂性。同时,模块之间相互松耦合,一个模块的修改不影响另一个模块,比如地图搜索模块中改变了优先路径的推荐,不会影响运力调度模块中的人车匹配算法。
水平分层可以很好地满足现有业务做深度扩展,当业务有变化时,系统在特定层做调整,对其他层影响有限,这样把变化局限在一个小范围。
垂直方向拆分
垂直拆分指的是按照不同的业务线拆分,比如,将整个出行业务分为出租车业务、快车业务和顺风车业务,按照不同的业务场景,自上而下进行竖切,让每个业务都自成体系,形成自己的业务闭环。
通过垂直拆分,一个复杂的出行场景就拆分为几个具体的场景,我们可以根据各个业务线的特点去设计系统,从而降低了整个系统的复杂性。
垂直拆分可以很好地满足业务广度上的扩展,比如说增加一条新的业务线,可以按照这个思路落地系统。一般做业务架构时,我们先考虑垂直拆分,从大方向上,把不同业务给区分清楚,然后再针对具体业务,按照业务处理流程进行水平拆分。
如果同时进行垂直拆分和水平拆分,一个大系统被拆分为了一个二维的模块矩阵,每个模块既属于某个业务线,也属于业务流程的某个环节。这样一来,每个模块的职责都很清晰,当业务变化了,我们可以清楚地知道,这个变化涉及哪些模块,然后,对这些模块进行相应的调整就可以。
为了帮你更好地理解这两种拆分方式的好处,我这里举个搭积木的例子。经过拆分,每个业务模块都成为一个积木,然后,我们以搭积木的方式来构造系统。当业务发生变化,我们就调整对应的积木,如果系统拆分得合理,拆分后的模块就具有良好的封装性,也就意味着我们主要是调整积木的内部,而它的外观基本不变。这样一来,相邻的积木不会受到影响,系统整体也不需要大的调整。结果是,系统的变化是局部和可控的,保证了灵活的应对变化能力。
打造可扩展的模块体系:模块整合
系统拆完后,接下来就是模块整合的工作,整合也有两种好的手段:通用化和平台化。
通用化整合
通用化指的是通过抽象设计,让一个模块具备通用的能力,能够替代多个类似功能的模块。回到刚才的出行平台,我们发现 3 条业务线都有地图搜索、运力调度、订单支付这些模块,不同的业务线之间,这些同名的模块逻辑高度类似,只是细节方面有差别。
那么,我们能不能对这些类似的模块进行抽象化处理,整合成一个通用的模块呢?答案是可以的,我们可以在模块接口中,通过输入参数标识调用来自哪个业务,是出租车、快车还是顺风车,然后在模块内部,针对不同业务线的差异化部分做针对性处理。结果可能是这个通用模块增加 5% 的逻辑,但避免了 95% 的重复逻辑,这样,经过通用化整合,新的模块以很低的代价,就为多个业务线提供了复用。而且,当新的业务线进来,很可能这个通用化的模块,就已经提供了现成的支持。
通过模块通用化,模块的数量减少了,模块的定位更清晰,概念更完整,职责更聚焦。在实践中,当不同业务线对某个功能需求比较类似时,我们经常会使用这个手段。
平台化整合
平台化是把定位相同的模块组织在一起,以组团的方式对外提供服务。对于外部系统来说,我们可以把这些模块看成是一个整体,一起对业务场景提供全面的支撑。如下图所示,我们可以看到,地图搜索、运力调度、订单支付,都是各个业务线都需要的基础和通用的业务能力,当我们增加新的业务线时,还是离不开这些基础能力。
所以,我们可以把这些基础模块放在同一层,构成一个基础业务平台。之前,它们是一个个离散的服务,独立地输出能力,现在变成一个大的业务平台,可以提供整体的能力输出。
通过打造业务平台,一方面,我们对多个业务模块进行包装,形成更大粒度的抽象,相当于减少了模块的数量;另一方面,作为平台,它的定位更明确,系统依赖关系也更清晰;而且,如果新的业务线进来,它可以基于业务平台快速落地。
业务平台化是模块依赖关系层次化的一个特例,只是它偏向于基础能力,在实践中,当业务线很多,业务规则很复杂时,我们经常把底层业务能力抽取出来,进行平台化处理。
总结
好了,下面我来总结一下今天所讲的内容。
首先,我们对系统进行建模,系统 = 模块 + 关系,这样会简化你对系统的认识。基于这个模型,我们对模块划分和关系定义提出具体的要求,你可以在实际设计时参考这些要求。
另外,我们深入地分析了扩展性的本质。系统的扩展能力来自于内部模块体系的有序,这样才能低成本地应对业务变化,认识到了这一点,有助于你从根本上理解和重视架构的扩展性设计。
然后,我提供了一个出行平台的例子,来帮助你理解,如何通过模块拆分和整合的手段,具体地设计一个可扩展的架构,希望你能在工作中灵活运用。
最后,给你留一道思考题:你所在公司里有没有类似的肿瘤系统,它包含了太多职责,导致系统内部结构混乱,大家都不敢对它进行调整?
07 | 可复用架构:如何实现高层次的复用?
在前面几讲中,我们讨论了如何打造一个可扩展的架构,相信你对架构的可扩展有了一定的了解,而架构还有一个非常重要的目标,那就是可复用。所以从今天开始,我就来和你聊一聊,如何打造可复用的架构。
作为开发人员,你对复用这个概念一定不陌生。在开发过程中,我们把系统中通用的代码逻辑抽取出来,变成公共方法或公共类,然后在多个地方调用,这就是最简单的技术上的复用。但一开始,我们不会过多地考虑复用,当一个新项目过来,我们会选择最直接的方式来实现,结果往往是欲速而不达,比如说:好不容易搞定了一个项目,接着又有新的类似项目过来,我们又要从头再来;项目的代码是定制的,项目结束后,系统维护的噩梦刚刚开始。
如果项目缺乏沉淀,每个项目都是全新的开始,出现这些情况,一点都不意外。而要想解决这个问题,我们一开始就要考虑系统的复用性。复用,它可以让我们站在巨人的肩膀上,基于现有的成果,快速落地一个新系统。
那么,我们在做架构设计时,如何实现系统的高可复用呢?今天,我就针对复用这个话题,首先和你介绍一下,复用具体都有哪些形式;然后,我会针对最有价值的业务复用,带你了解如何划分服务的边界,让你能够在工作中,设计一个可以高度复用的系统。
复用的分类
复用有多种形式,它可以分为技术复用和业务复用两大类。技术复用包括代码复用和技术组件复用;业务复用包括业务实体复用、业务流程复用和产品复用。
从复用的程度来看,从高到低,我们可以依次划分为产品复用 > 业务流程复用 > 业务实体复用 > 组件复用 > 代码复用。
接下来,我就按照复用度从低到高,对这些复用方式进行一一分析,帮助你更好地理解架构的可复用性。
技术复用
首先是代码级复用,这部分应该是你最熟悉的了。这里包括你自己打包的类库,第三方提供的 SDK,还有各种算法封装等。我们的代码可以直接调用它们,物理上也和我们的应用打包在一起,运行在同一个进程里。代码级复用是最低层次的复用,你可以把它当作你自己源代码的一部分。
再往上,是技术组件复用。这些组件有我们自己封装的,更多的是大量开源的中间件,比如 Redis、MQ、Dubbo 等;组件也包括各种开发框架,比如 Spring Cloud。这些基础组件技术复杂度很高,它们的存在,极大地简化了我们的开发工作。
值得注意的是,代码级复用和技术组件复用都属于工具层面,它们的好处是在很多地方都可以用,但和业务场景隔得有点远,不直接对应业务功能,因此复用的价值相对比较低。
业务复用
我们知道,系统最终是为业务而服务的,如果能够实现直接的业务复用,那系统开发的效率就更高。在前面的课程中,我们讨论架构的演进过程时,很多地方谈到了业务能力的复用,比如说,微服务强调单个业务实体的封装和复用,而中台进一步实现了企业级业务能力的复用。
所以接下来,我们就从比较简单的业务实体复用开始说起。
业务实体复用针对细分的业务领域,比如订单、商品、用户等领域。它对各个业务领域的数据和业务规则进行封装,将它变成上层应用系统可以直接使用的业务组件。
业务流程的复用针对的是业务场景,它可以把多个业务实体串起来,完成一个端到端的任务。比如说,下单流程需要访问会员、商品、订单、库存等多个业务,如果我们把这些调用逻辑封装为一个下单流程服务,那下单页面就可以调用这个流程服务来完成下单,而不需要去深入了解下单的具体过程。相比单个的业务实体复用,业务流程的复用程度更高,业务价值也更大。
最高层次的复用是对整个系统的复用,比如说一个 SaaS 系统(Software-as-a-Service),它在内部做了各种通用化设计,允许我们通过各种参数配置,得到我们想要的功能;或者说一个 PaaS(Platform-as-a-Service)平台,它会提供可编程的插件化支持,允许我们“嵌入”外部代码,实现想要的功能。
这种产品级的复用,它的复用程度无疑是最高的。这样的系统,在落地的时候,它无需核心的开发团队进行开发,只由外围的实施团队负责就可以了,这样,一个项目的上线就能简化为一次快速的实施,不但上线周期短,系统也更稳定。
当然,实现这样的复用,难度也是很大的,你既要对所在行业的业务有很全面的理解,又要有很强的抽象设计能力。这类系统中,比较典型的有 Salesforce 的 CRM 系统和 SAP 的 ERP 系统。
现在,我们先对复用做个总结。从技术复用到业务复用,越往上,复用程度越高,复用产生的价值也越大,但实现起来也越复杂,它能复用的场景就越有限。在实际工作中,技术层面上的复用相对比较简单,我们对这部分的认知也最多,而且由于开源的普及,现在有丰富的中间件让我们选择,我们可以基于它们,逐步构建适合自己的技术体系。
但如果我们能进一步打造业务中间件,并在这个基础上,形成业务平台,这样,我们就能实现更高的业务级复用,可以更高效地支持系统的快速落地。
而在实现业务组件化和平台化的过程中,首要的问题就是基础服务边界的划分。边界划分决定了服务的粒度和职责,在实际工作中,也是非常困扰我们和有争议的地方。接下来,我就针对基础服务边界的划分,和你分享我自己在项目开发的过程中,总结的一些实用的原则和做法。
基础服务边界划分
服务边界划分要解决“我是谁”的问题,它实现了服务和周边环境的清晰切割。我们都知道,服务包含了业务数据和业务规则,并提供接口给外部访问,其中,接口是服务的对外视图,它封装了服务的业务数据和规则。
所以从边界划分的角度来看,我们就是要确定哪些数据属于这个服务,哪些接口功能由这个服务提供。这里,我总结了 3 个基础服务边界划分的原则,供你设计时做参考。
首先,是服务的完整性原则
你在划分服务的边界时,需要确保服务内部数据的完整性。举个例子,一个商品服务的数据模型,不仅要有商品基本信息,比如商品名称、价格、分类、图片、描述等;还需要包含商品的扩展信息,如商品的各种属性、商品标签等;最后还要包含各种复杂商品类型的定义,比如组合商品、套餐商品、多规格商品等。
另外,你还要保证服务功能的完整性。对于服务使用者来说,他们是以业务的角度看服务,而不是纯粹的数据角度。比如一个套餐商品,在服务内部,它是多个单品的复杂组合,但从服务调用者的角度来看,它就是一个商品。
那现在问题来了,对于套餐的价格,商品服务是给出一个最终价格呢?还是给出各个单品的价格,然后让调用方自己算最终价格呢?我们知道,套餐的价格不是各个单品价格累加的结果,它包含了一定的优惠,如果它的价格由服务调用方来算,这会导致商品的部分业务规则游离于服务外面,破坏了商品服务的功能完整性。
在实践中,有些服务只是存储基础数据,然后提供简单的增删改查功能,这样一来,服务只是一个简单的 DAO,变成了数据访问通道。这样的服务,它的价值就很有限,也容易被服务调用方质疑。因此,我们要尽可能在服务内部封装完整的业务规则,对外提供完整的业务语义,最大程度地简化服务的使用。
所以,当你在划分服务边界时,要保证服务数据完整、功能全面,这样才能支撑一个完整的业务领域。
其次,是服务的一致性原则
也就是说,服务的数据和职责要一致,谁拥有信息,谁就负责提供相应的功能。服务内部的业务逻辑要尽量依赖内部数据,而不是接口输入的数据,否则会造成数据和业务规则的脱节(一个在外面,一个在里面),如果服务对外部的依赖性很强,就无法提供稳定的能力了。
很多时候,我们对一个功能到底划分到哪个服务,有很大的争议。这时,我们可以结合这个功能所依赖的数据来判断,如果功能所需要的大部分数据都存储在 A 服务里,那当然由 A 服务来提供接口比较合适,这样接口输入的数据比较少,不但简化了服务对外部的依赖,同时也降低了接口调用的成本。
给你举个例子,在订单小票上,我们经常能看到一些优惠信息,比如说商品原价是多少,其中因为满减优惠了多少,因为商品特价减免了多少。这个优惠计算的结果是订单的一部分,毫无疑问,它需要保存在订单服务里。
但这个订单的优惠计算过程,却不是由订单服务来负责,而是由独立的促销服务负责的。因为优惠计算所需要的优惠规则是在促销服务里定义的,促销服务可以在内部拿到所有的优惠规则,然后完成整个优惠计算。
否则,如果是由订单服务负责优惠计算,订单服务的调用者就需要在接口中提供完整的促销规则,不但调用成本高,而且外部促销规则的改变会影响订单服务的内部实现。所以在这里,促销服务负责促销规则的维护,以及对应的优惠计算功能;订单服务负责优惠结果数据落地,以及后续的查询功能。这样,每个服务存储的数据和对外提供的功能是一致的。
最后一个,是正交原则
既然是基础服务,它们就处于调用链的底层,服务之间不会有任何的调用关系,也就是说基础服务相互之间是正交的。比如说会员服务和商品服务,它们代表不同维度的基础业务域,彼此之间不会有调用关系。
正交还有另外一种情况:服务之间有数据的依赖关系,但没有接口的调用关系。
比如说,订单明细里包含商品 ID 信息,但订单服务内部不会调用商品服务来获取商品详情。如果页面需要展示订单的商品详情,针对这个具体的业务场景,我们可以在上层的聚合服务里,通过聚合订单服务和商品服务来实现。
总结
可复用是架构设计的一个重要目标,今天我们对复用进行了梳理,包括复用有哪些形式,以及它们有哪些价值,相信你现在对复用已经有了一个整体的认识。业务上的复用比纯粹的技术复用有更高的价值,我们要尽量往这个方向上靠。
在实践中,落地基础服务是实现业务复用的有效方式,而基础服务边界的划分,它有科学的成分,但更多的是一种艺术,这里我提供了几个实用的划分原则,你可以在工作中结合实际情况,灵活地运用它们。在专栏的下一讲,我会通过一个具体的订单服务例子,来帮助你更好地落地基础服务。
最后,给你留一道思考题:我们在落地服务时,有时会冗余存储其它服务的数据,你对这个有什么看法呢?
