一、复用的分类
复用有多种形式,它可以分为技术复用和业务复用两大类。技术复用包括代码复用和技术组件复用;业务复用包括业务实体复用、业务流程复用和产品复用。从复用的程度来看,从高到低,我们可以依次划分为产品复用 > 业务流程复用 > 业务实体复用 > 组件复用 > 代码复用。

1.技术复用
这里包括你自己打包的类库,第三方提供 的 SDK,还有各种算法封装等。代码可以直接调用它们,物理上也和应用打包在一起,运行在同一个进程里。代码级复用是最低层次的复用,可以把它当作你自己源代码的一部分。
再往上是技术组件复用。这些组件有自己封装的,更多的是大量开源的中间件,比如 Redis、MQ、Dubbo 等;组件也包括各种开发框架,比如 Spring Cloud。这些基础组件 技术复杂度很高,它们的存在,极大地简化了开发工作。值得注意的是,代码级复用和技术组件复用都属于工具层面,它们的好处是在很多地方都可以用,但和业务场景隔得有点远,不直接对应业务功能,因此复用的价值相对比较低。
2.业务复用
系统最终是为业务而服务的,如果能够实现直接的业务复用,那系统开发的效率 就更高。微服务强调单个业务实体的封装和复用,而中台进一步实现了企业级业务力的复用。
(1)业务实体复用
业务实体复用针对细分的业务领域,比如订单、商品、用户等领域。它对各个业务领域的数 据和业务规则进行封装,将它变成上层应用系统可以直接使用的业务组件。
(2)业务流程的复用
业务流程的复用针对的是业务场景,它可以把多个业务实体串起来,完成一个端到端的任 务。比如说,下单流程需要访问会员、商品、订单、库存等多个业务,如果我们把这些调用 逻辑封装为一个下单流程服务,那下单页面就可以调用这个流程服务来完成下单,而不需要 去深入了解下单的具体过程。相比单个的业务实体复用,业务流程的复用程度更高,业务价值也更大。
(3)整个系统的复用
最高层次的复用是对整个系统的复用,比如说一个 SaaS 系统(Software-as-aService),它在内部做了各种通用化设计,允许通过各种参数配置,得到想要的 功能;或者说一个 PaaS(Platform-as-a-Service)平台,它会提供可编程的插件化支持, 允许我们“嵌入”外部代码,实现想要的功能。这种产品级的复用,它的复用程度无疑是最高的。这样的系统,在落地的时候,它无需核心 的开发团队进行开发,只由外围的实施团队负责就可以了,这样,一个项目的上线就能简化 为一次快速的实施,不但上线周期短,系统也更稳定。当然,实现这样的复用,难度也是很大的,你既要对所在行业的业务有很全面的理解,又要 有很强的抽象设计能力。这类系统中,比较典型的有 Salesforce 的 CRM 系统和 SAP 的 ERP 系统。
二、基础服务边界划分
服务边界划分要解决“我是谁”的问题,它实现了服务和周边环境的清晰切割。服务包含了业务数据和业务规则,并提供接口给外部访问,其中,接口是服务 的对外视图,它封装了服务的业务数据和规则。所以从边界划分的角度来看,我们就是要确定哪些数据属于这个服务,哪些接口功能由这个 服务提供。
1.服务的完整性原则
在划分服务的边界时,需要确保服务内部数据的完整性。举个例子,一个商品服务的数据模型,不仅要有商品基本信息,比如商品名称、价格、分 类、图片、描述等;还需要包含商品的扩展信息,如商品的各种属性、商品标签等;最后还 要包含各种复杂商品类型的定义,比如组合商品、套餐商品、多规格商品等。另外还要保证服务功能的完整性。对于服务使用者来说,他们是以业务的角度看服务, 而不是纯粹的数据角度。比如一个套餐商品,在服务内部,它是多个单品的复杂组合,但从 服务调用者的角度来看,它就是一个商品。那现在问题来了,对于套餐的价格,商品服务是给出一个最终价格呢?还是给出各个单品的 价格,然后让调用方自己算最终价格呢?我们知道,套餐的价格不是各个单品价格累加的结 果,它包含了一定的优惠,如果它的价格由服务调用方来算,这会导致商品的部分业务规则 游离于服务外面,破坏了商品服务的功能完整性。在实践中,有些服务只是存储基础数据,然后提供简单的增删改查功能,这样一来,服务只 是一个简单的 DAO,变成了数据访问通道。这样的服务,它的价值就很有限,也容易被服 务调用方质疑。因此要尽可能在服务内部封装完整的业务规则,对外提供完整的业务 语义,最大程度地简化服务的使用。所以,当你在划分服务边界时,要保证服务数据完整、功能全面,这样才能支撑一个完整的 业务领域。
2.服务的一致性原则
服务的数据和职责要一致,谁拥有信息,谁就负责提供相应的功能。服务内部的业务逻辑要尽量依赖内部数据,而不是接口输入的数据,否则会造成数据和业务 规则的脱节(一个在外面,一个在里面),如果服务对外部的依赖性很强,就无法提供稳定 的能力了。很多时候,我们对一个功能到底划分到哪个服务,有很大的争议。这时,我们可以结合这个 功能所依赖的数据来判断,如果功能所需要的大部分数据都存储在 A 服务里,那当然由 A 服务来提供接口比较合适,这样接口输入的数据比较少,不但简化了服务对外部的依赖,同 时也降低了接口调用的成本。给你举个例子,在订单小票上,我们经常能看到一些优惠信息,比如说商品原价是多少,其 中因为满减优惠了多少,因为商品特价减免了多少。这个优惠计算的结果是订单的一部分, 毫无疑问,它需要保存在订单服务里。但这个订单的优惠计算过程,却不是由订单服务来负责,而是由独立的促销服务负责的。因 为优惠计算所需要的优惠规则是在促销服务里定义的,促销服务可以在内部拿到所有的优惠 规则,然后完成整个优惠计算。否则,如果是由订单服务负责优惠计算,订单服务的调用者就需要在接口中提供完整的促销 规则,不但调用成本高,而且外部促销规则的改变会影响订单服务的内部实现。所以在这里,促销服务负责促销规则的维护,以及对应的优惠计算功能;订单服务负责优惠 结果数据落地,以及后续的查询功能。这样,每个服务存储的数据和对外提供的功能是一致 的。
3.正交原则
既然是基础服务,它们就处于调用链的底层,服务之间不会有任何的调用关系,也就是说基 础服务相互之间是正交的。比如说会员服务和商品服务,它们代表不同维度的基础业务域, 彼此之间不会有调用关系。正交还有另外一种情况:服务之间有数据的依赖关系,但没有接口的调用关系。比如说,订单明细里包含商品 ID 信息,但订单服务内部不会调用商品服务来获取商品详 情。如果页面需要展示订单的商品详情,针对这个具体的业务场景,我们可以在上层的聚合 服务里,通过聚合订单服务和商品服务来实现。
三、复用注意事项
在复用内容时,要考虑复用带来的影响,如果可能会产生问题,不要进行复用,比如一个mq队列不要干太多的事,不要将所有写库的消息都放在一个队列中,应根据功能放到不同队列中,此时一个队列的积压就不会影响另一个队列的消费。
注意:生产环境下不要把差别大的代码强行放到一起,防止以后改动A影响B的事情发生(如果做不好复用,还不于直接复制一套新的代码)
10万+

被折叠的 条评论
为什么被折叠?



