【灌水】聊聊系统中的包结构

将JavaWeb系统分为Controller/Service/Dao等层次,已经是开发们的习惯了。在这种分层思想的指导下,系统内的包结构一般都是这样的:

系统内的包结构一般都是这样的

当然,有时候bean、dao会被命名为model、pojo或者mapper,虽然名称各异,但含义相近。如果分层较细,可能还会有business、jms或者task这样的包。

在这种这种包结构中,某一项功能(如用户管理功能)的代码被拆分成Controller、Service和Dao三层,并放到各自对应的包下,很符合分层的习惯思路,而且看起来简单明了。无怪乎这种结构能够风靡大江南北,经久不衰。

但是,随着系统越做越大、业务越做越复杂,对于如何设置系统内的包结构、如何组织代码,我有了新的想法。

这个想法很简单:系统内划分包结构时,先分业务、再分层次。

作为对比,原先的方式实际上是先分层次、再分业务。类似这样:

先分层次、再分业务

上图是我们某个系统的包结构(隐去了com.company.system)。这张图比较清晰的展示出了传统包结构是怎样“先分层次、再分业务”的。它优先按照代码所处层次,把类分到controller或service(当然还有dao、bean等)包下去;然后,在controler/service/dao/bean等包内部,再按照类所属的业务功能,把他们分配到不同的包下去。

其实这个系统属于做得比较“精致”的了。大多数系统可能只是在按所属层次划分完包结构之后,就把同一层次的类都放到一起,不再按业务再分一次了。

“先分层次、再分业务”的包结构,应该说是我们耳熟能详的了。那么,“先分业务、再分层次”的包结构,是什么样子的呢?仍然用上面那个系统做例子,用新的方式分完包结构,它应该是这样子的:

先分业务、再分层次

也就是说,每个类都会优先按照它所属的业务功能被放到不同的包下(如auth、user等),然后再根据它所属层级被分到controller/service/dao或其它包下。如果用一张图来解释两种包结构的划分依据,大概就是这样的:

两种视角

无论优先层级还是优先业务,其实关注的都是如何拆分和管理复杂度。两种方式各有千秋,谈不上谁一定比谁好。所以,这里不聊“先分层次、后分业务”有什么不好,只说说我为什么喜欢“先分业务、后分层次”。

当时我们在对一个模块做重构。这个是一个计算模块。它非常重要、而且调用非常频繁,重构前后的计算结果必须分毫不差;同时它又非常复杂,即使经过重重测试,我们也不敢保证功能正确无误。因此,我们在新代码中输出了非常细致的日志,以确保任何一次调用都能够追踪到每一个步骤上的每一个变量。

显然,这个策略会导致线上日志量激增。上线后,这个模块的日志达到了15G/天。三天后,我们就收到了“磁盘可用空间不足10%”的报警。当然,也多亏了这么详尽的日志,我们在上线后第一天就修复了三个功能问题;在第三天时通过分析日志,找到并优化了一处性能瓶颈(把平均250ms+的调用优化到了35ms左右)。做完这些问题修复和性能优化之后,我们又观察了一天,以确保重构达到了目标。

最后,在第五天时,我们在log4j2.xml中改了两行配置,把日志量减少到了不足1G/天。这两行配置是这样的:

<!-- 原先下面这行配置的level为INFO -->
<Logger name="com.company.system.calculate" level="WARN" />
<!-- 原先没有下面这行配置 -->
<Logger name="com.company.system.calculate.web" level="INFO" />

相信大家一眼就能看明白这两行配置的作用。我们之所以能用这样简单的两行配置就减少这么多的日志量,首要原因就是我们的包结构是“先分业务、后分层次”的。这就是我使用这种包结构收获的第一个好处。

当然,这个好处没有什么说服力:调整日志输出量并不是什么痛点。不过,随着我们进一步重构系统,“先分业务、后分层次”显现出了另一个优点。

如前所述,这个计算模块重要、复杂、压力又大。所以我们后来决定:把这个模块拆分成一个独立的服务。在第一次重构之后,我们已经有了一套功能、性能、结构都还不错的代码。所以第二次重构时,我们并不打算再写一套代码,而只计划把这套代码copy出来。

这次copy异常简单而顺利,因为这个模块的核心代码都在同一个包下。把这个包移过来之后,就只剩下少量的公共代码和配置了。因此,我们非常轻松就完成了第二次系统重构。

这就是“先分业务、后分层次”这种包结构带来的第二个好处。这个好处似乎也没什么说服力:不是所有模块和代码都要时刻准备着被拆分为独立的服务。

不过我还是更喜欢“先分业务、后分层次”的包结构。它暗示了一种更倾向于产品、更倾向于业务抽象、更倾向于模块化和服务化的系统结构和设计思路。虽然这个说法还是没有什么说服力。

所以,是“先分业务、后分层次”还是“先分层次、后分业务”,其实没有孰优孰劣的区分,选择哪一个都没有问题。只不过,我们在系统设计和开发中的每一个选择、每一个决策,都应该是思考、比较和取舍之后的结果,而不是人云亦云、“一直如此”。

【灌水】聊聊系统中的包结构

猜你喜欢

转载自blog.51cto.com/winters1224/2486882