最近几天忙着搬家,代码几乎没怎么写,趁着周日晚上有点空来水一篇文章。
大概两年前决定自己做个独立的项目作为未来几年的空余时间消磨利器,并且在其中尝试使用各种最新技术,然后业务也比较复杂(不然也不能做这么久),现在项目迭代了这么久,也上架一段时间了,打算写点文章大概介绍下里面用到的一些技术和思路。
现在项目中大概有十几个模块,拆分模块的主要目的是为了降低未来的修改成本,同时模块的拆分也能反映出技术架构和业务架构。
目前项目的模块关系图大概如下图所示。
上图中的所有同层级的模块都是平行模块,这意味着它们不会互相依赖,模块的依赖关系按照图中箭头的方向单向依赖。
理解业务
不同的软件有不同的业务,模块设计应该因地制宜,一个好的设计一定是需要先充分理解业务的。
如果两个模块在业务上就有依赖关系,那么一定要在软件架构上体现出来。 一些原本就有耦合关系的业务但是在软件架构中却彻底分离,这会给未来带来无穷无尽的麻烦。
在理解业务的基础之上可以进行业务形式化建模,在对业务有了足够充分的认知之后再进行软件架构设计,业务架构和软件架构尽可能保持一致。
比如目前国内很多项目中都在使用的路由框架就承担了解除耦合的责任,架构中把一些看起来关系不大的模块做拆分,然后通过路由框架进行通信,实际上造成了业务边界和关系的混乱。因为通过路由跳转就意味着业务有关联,既然业务上有关联那么架构上也应该有所体现,原本可以简单的通过语法来约束和表达的事情最后却只能用 URI 来表达,约束校验只能推迟到运行时再做判断了。
一个解决办法是提供一个上图所示的 Biz Framework 模块和 Common Biz 模块。
Framework
Framework 模块是纯技术的、业务无关的、但根据业务需求编写的通用能力。
它不依赖任何业务模型,只依赖一些 Library,其中包含一些对第三方库的简单化工具,业务无关的基础能力以及各种类型的工具类。
Biz Framework
既然有了技术上的 Framework,那么有一个业务上的 Framework 也不过分吧。
对于一些足够通用,甚至可以作为项目基石的一些业务可以考虑放入这个模块。
由于这个模块是业务的最底层,必须足够抽象和基础,所以这里面大部分会是接口和数据模型。
比如作为一个 Microblogging 客户端,无论是哪个业务模块几乎都会使用到诸如 User、Blog 这样的模型,以及无论哪个模块,都会判断登录状态,发起登陆等,因此可以把它们定义在此处。
Common Biz
通用业务模块,一般来说,大部分的通用业务应该在此处,比如数据分析、通用 UI 组件、通用页面等。该模块负责解决一些通用的能力,可能会被任何一个上层模块依赖,同时也会依赖 Biz Framework 模块获取其中的数据类型等。
对于一些通用的业务工具类也可以放在此处,比如对 Blog 中时间的不同格式化方式、列表内容加载流程范式等。
甚至一些简单的业务也可以放在这里,因为 Features 模块包含的是比较大的业务,对于一些小到不值得划分模块的业务写到这里也可以接受。
Features
这个模块的职责就很清晰了,Features 下面的每个模块都仅包含一个独立的业务。比如上图中的 Feeds 模块就是 Feeds 相关的部分,Account 是账户管理部分等。
对于我的项目来说,我有四个 Features 模块,刚好对应首页底部的四个 TAB。
到了这里会有个问题,不同 Feature 之间几乎肯定是会有互相跳转的需求的,虽然业务比较独立,但这种需求也偶尔会出现,这里可以选择在 common biz 模块提供一个不同模块的 Visitor 接口,每个模块各自实现,然后通过这个 Visitor 来跳转。
如果对于一些更复杂的场景,以及包含了 DeepLink 等需求的场景,可以考虑使用路由,但是使用路由跳转应该谨慎一点,慎重考虑之后再做决定。
Plugins
Plugins 模块一般根据项目的情况决定需不需要,它作为插件化架构的插件层存在,这里的插件是指软件架构中的一种定义。
对于一些可能的动态功能,或者具体实现依赖于运行环境的功能,可以考虑放入此处。
插件层一般不需要被任何模块依赖,它与 Application 处于同一个层级(至少源码级别是这样的),编译时将他打入包内即可,可以通过依赖注入或者一些 SPI 机制获取其实现。
Application
这个模块就更简单了,主要用来组合所有的 Feature 模块,一般不会包含太多代码。
对于跨平台项目来说,可能存在多个 Application 模块,每一个对应一种平台。
上面就是我在项目中使用的模块划分方式,目前使用下来感觉很丝滑,没遇到什么坑,这也是演进了两年的结果,也就是我自己的项目能这么玩了,哪里看着不顺眼就来重构一下,也希望这对大家有所帮助。