首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

业务与服务的隔离

(图片来源于网络)

成功的道路何其曲折,所以我们需要享受这一刻

雷鸟内部知识沉淀分享

第1篇-《业务与服务的隔离》张磊

(预计“悦读”11分钟)

在一个企业中我们常因为工作内容的不同而相应的在不同的工作岗位上,由不同的岗位进行有效的衔接构成一整个企业使命。在软件开发过程中,我们也是如此:从接触用户的终端、面向终端调用服务的云端、以及常驻监控的运维,甚至提出需求的产品也可以按照顺序契合至全链条的研发领域。这样的区分将使软件周期变得可控,不论人力和时间,变更的成本将有效降低。倘若我们不进行这样的划分,那么各自的工作区域变得模糊,并且在多人协作时会出现交接混乱,配合失职,交付延迟等问题。

在此篇文章中我们将细分责任的观点引入服务器的业务开发,去介绍业务服务与业务之间千丝万缕的关系做出简明阐释,并且从构建于系统服务之上的业务扩展出发,对如何做好服务业务界限的划定和分离策略进行一些思考,抛砖引玉进行服务地探索。此系列文章我们尽可能的以项目中的实例作为背景,避免照本宣科和完全抽象地理论宣讲。

在服务通信领域,按照事件顺序可以将繁琐的交互流程概括为几个阶段:当服务接受后,将触发映射寻找,直至定位到具体的可执行业务逻辑,当业务处理完成后,我们需要对包含的业务结果进行封装,进而将数据包返回以告知请求方处理结果。如下图是简化的服务处理逻辑:

我们将视角拉回到服务端的业务研发阶段,在此过程中研发按照产品或者运营策略进行需求定制开发,例如:订单服务的已购清单,待支付列表,媒资服务的栏目专题内容供给,应用商店服务的自升级等等。我们以购物车的商品展示为例,用户进入购物车,终端相应需要展示此用户的购物清单,面向后台服务的业务逻辑是将此用户的购物车数据查询返回,终端进行展示呈现。如下是一段伪代码描述了后台的购物车需求的处理逻辑:

表1伪代码1

上述代码中:

1代表通过shoppingCar对象通过一些参数获取购物清单;

2对查询的数据进行封装,此数据会回执终端用于呈现。

就这样便完成了此项需求,代码结构很清晰,1. 获取结果-> 2. 封装结果。考虑到真实情况,此服务是后台与终端协商好的数据格式和数据封装类型,例如json的数据格式:,我们也可以使用XML数据格式满足相同:admin10,不同的是在终端与后台需要进行匹配协同调整。那么为了能够适配这些服务类型,我们明确传递数据格式作为参数进行服务定制,那么我们在将第二步Struct.wrap(result)进行变更,追加json数据处理: Struct.wrap(result,“json”),很不错,现在已经满足与终端的数据需求。项目很顺利地完成,过了一段时间获取用户的购物清单被数据中心需要用于挖掘用户,构建推荐系统,相比与终端展示的数据而言仅在数据格式上不同,数据平台希望支持xml的数据格式报文,我们可以很快速地进行变更已满足此需求,copy,paste,rename,我们获得了支持xml数据格式的购物清单,如下:

表2伪代码2

情况看起来可控,那么我们在对接二进制的数据格式之后呢?情况就不是那么乐观了,并且出现了相似的代码。我们是否需要将结果和数据格式包装统一在业务处理中呢?这个问题似乎模棱两可,那么我们先将可以共性的代码提出,再来考虑这个问题:

表3伪代码3

上面我们可以看出整改业务处理已经十分简单,触发相应的逻辑(注释1),并且总是得到结果(注释2),对于结果的后续处理我们没有任何涉及,但是也正是这样,才获得了更高层面的处理方式,因为不关乎数据的格式包装,所以业务编码总是按照既定的规则(1,2)。那我们又是如何告知系统服务期望使用的数据格式呢?这里以使用JVM语言为例说明,在此方法的头部添加annotation,来设定面向外部的服务所采用的数据格式,系统通过此方式获得数据格式的上下文,对于处理数据格式的功能便剥离了业务研发。修改后的伪代码:

表4伪代码4

注释3将关联系统数据格式的上下文,表示系统在返回业务结果时,使用json数据格式包装结果,相应的如果使用xml,代码将变更为:@annotation(struct=”xml”),当然这里是需要服务器支持的,并不是只设定xml参数就万事具备。

经过上面的过程分析和阶段性重构,逐步将业务和服务进行剥离,凸显出扩展的简便和高效。对于开篇提出的问题,在编程技巧和行为准则的方面进行深入浅出的说明希望能够进行解答。

一、服务业务界限的划定

界限的划分,此篇文章只是简单地进行一些看法罗列,更加优秀的设计,同学们可以在开源社区中进行查阅。

1. 按照顺序进行拆分

我们按照事件流的顺序进行梳理,可以大致得到在业务逻辑模块中,拥有业务入口,业务返回,业务异常等几个阶段,在这些阶段中我们可以进行相关功能共性化处理,例如:业务返回我们可以按照设定进行数据包装,业务异常可以进行策略触发。也可以进行一些定制的服务拓展,例如:进入业务逻辑前进行权限判断,IP过滤等。那么按照这些事件,我们可以对自己的业务进行相应的分层和重构,把常用的作为系统的扩展预留(不一定实现),非必备的依然单独存在即可。

2.已有的逻辑共性化提炼

水滴穿石,优秀的服务来源于业务实践并且最终回归于业务。系统在公共服务层面会经历多个版本,以满足变化的业务。对于现存的业务在处理过程中,能够通过若干实例进行汇总,提炼至更高层级时,我们可以将其作为服务扩展,并且引入系统服务。在本篇购物服务中进行代码逐步迭代升级的过程便是此方式(提炼,修改,重构)。再举一个例子:我们在服务启动时,往往需要挂载很多业务逻辑,例如加载业务配置,注册服务,邮件通知等,如果每一项任务都在系统服务的main函数中进行编写则会导致“胖服务”的出现,并且也不利于服务的扩展(从字面上需要服务耦合,硬编码实现)。我们可以进行服务抽离,在服务启动时加载已配置的listener进行服务瘦身,这样服务可以按照规则进行研发,且服务本身通过灵活的配置形式进行动态加载。

表5胖服务伪代码

表6逻辑共性化提炼后的伪代码

二、服务业务的分离策略

界限划分清晰后,将进行部分代码的重构和整理,重构过程中所使用的技巧多种多样,如果是系统层面的重构常伴有设计模式的引入,业务的重构则需依赖于具体的场景进行分析:

1. 确定上下文

分层的划分有助于我们进行复杂业务的处理,每一项的划分,可以在领域或者任务上降低耦合的范围和模块逻辑设计的复杂度。对于分层带来的一项结果便是需要进行上下文的信息传递,也就是我们通常说的上下文。上下文按照最简原则暴露和保留最小粒度的关系将大大提升系统在面对纷杂业务场景下进行适配的能力。以我们所展示的终端与云端在数据格式上的处理为例,采用分层设计的直接好处便是:在业务处理完毕之后,触发下一个逻辑时需要确定上一个逻辑的中间值(这里为annotation(struct=”json”)),如果缺少此设定将导致上下文脱节,但只暴露此项至下文又可以省去业务的干扰。

2.使用接口和抽象类

使用接口和抽象类的好处在于,我们可以制定很多流程式的处理方式,而不必关注具体的实现,当然这里我们强调:接口定义步骤,抽象类关联步骤,或者在原有的步骤基础上进行定制。(具体的我们后续文章中会详述)。

3.不必追求完美

在分层阶段,很有可能不是一次到位的,所以不必对结构化的层级过于执着,因为很有可能好的改进是出现在太多共性化功能之后产生的,所以一开始的分层也不一定是最终的方案。

在本篇结尾,期望大家能够从中引起一些共鸣和想法,进而在代码的实践中进行落地,实践是检验真理的必经之路。

想要了解更多我们的动态?加入我们?

雷鸟风曝

(雷鸟的大喇叭)

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180403G14WI000?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券