近年来微服务/SOA很是流行,我们团队赶时髦,也玩了玩。虽然用的时间还不长,但也已经踩过不少坑。今天想记录下自己对边界问题的一些思考。

很多人在谈起微服务时,总是会很自豪的说,微服务为我们提供了高内聚低耦合的明显好处,因为微服务强化了模块化的概念。但是, 如何模块化,如何明确的定义模块的边界,却很少有人提及,而这正是微服务架构的难点,也恰恰是开发人员技术能力的体现。如何正确的定义模块的边界,似乎还没人总结出一套理论原则。             

当我们将一个单体服务进行模块化拆分时,我们总是能够很轻易地找到模块边界。那些不知道该放到哪个模块里的代码,便是我们模块边界的所在。在整理这些棘手的代码时,就是一个划分边界的过程。如果实在挪不开,这说明他们本就该属于同一个模块中或者他们上层应该还有一个服务。

在实际开发过程中,对单体服务进行重构的时候并不多。更多的时候,我们一开始做服务架构时便在使用微服务的架构模式。随着系统需求的渐渐复杂化,我们会发现,服务间的耦合越来越严重,甚至出现了循环依赖。这种时候我们不得不质疑自己,当初的模块是否划分的足够清晰。

我们做了一个借贷系统。系统中有两个核心服务,借款与还款。

借款的核心需求:

A。用户可以借款;

B。用户可以查看自己的借款明细。包括借款时间/金额等。

还款的核心需求:

A。用户可以对借款进行还款;

B。用户可以查看还款明细。

一开始,一切都显得很美好。我们遵循kiss原则,让各个模块尽量简单;同时为了项目组开发的便利,我们尽量将模块细化。于是划分了一个借款模块,一个还款模块。此时,我们服务的依赖关系很清晰。还款模块会单向依赖借款模块。还款成功后,通过消息进行解耦,对借款余额进行更新。

但随着系统需求的变化,借款详情中需要展示用户当前的还款情况,并计算未来的还款计划,这些都需要依赖还款模块。这种情况下,我们便形成了双向依赖的循环。借款模块会依赖还款模块,还款模块也依赖了借款模块。

这种情况下,出于项目进度,我们并没有进行任何的处理,而是任由双向依赖的发生,甚至,在还款模块直接依赖了借款的数据库来达到快速开发的目的。但这也为日后欠了技术债。

A。循环依赖日后可能会带来循环调用,这种结构上允许的闭环调用,很有可能在将来造成无意识的递归,让系统崩溃。

B。项目构建时,A/B模块的循环依赖,造成无法构建甚至循环构建。对我们系统的持续集成造成了障碍。

C。两个模块通过数据库进行共享来达到代码解耦,以后数据表结构若发生变化,代码维护将极其困难。

这种事情,相信很多人都遇到过,解决方法或许也与我们当前的类似。但是,其实这里有很好的办法来解决这个问题。我们只需要在借款模块与还款模块的上层,抽象出一个借还款核心模块,其负责将两个模块的耦合严重的业务进行整合,比如由他来发现借款订单,通过调用还款模块来计算还款计划,便可以消除整个循环依赖了。但是,这样会造成这种中间模块越来越多,对以后的系统维护又造成了一定难度。但这也不失为一种方法。

所以很多时候在想,这两个模块一开始是否划分的太细,是否一开始就应该在一起呢?我们划分模块边界的依据又是什么呢?欢迎大家一起探讨。