业务拆分可谓是软件架构师手中的"瑞士军刀",良好的业务拆分能够带来多方面的优势:提高代码的可维护性、增强团队协作效率、实现功能的灵活组合与替换、降低系统耦合度、提升编译速度,轻松应对各种复杂任务或流程。反之,不当的业务划分则会导致代码混乱、依赖复杂、维护成本高昂等问题。
一句话,遇到处理不了的问题的时候业务拆分要管,想要更好的处理业务的时候业务拆分也要管。
那如何进行业务拆分呢,以下为业务拆分三原则
一 区分核心子系统与周边子系统
1.1 核心子系统的概念与特征
核心子系统是整个应用架构中经过高度抽象后形成的最小必要模块集合,它体现了系统最本质、最不可简化的功能特征。正如所有的电子设备系统经过层层抽象后都会归结到冯·诺依曼结构一样,一个良好的业务拆分也应当能够识别出系统中的这种"冯·诺依曼结构"。
核心子系统通常具有以下特征:
-
不可替代性:如果移除该子系统,整个系统将无法完成其基本使命。例如,在一个电商应用中,商品管理系统和订单处理系统通常属于核心子系统。
-
稳定性:核心子系统的接口和功能相对稳定,不会随着业务需求的频繁变化而大幅调整。
-
抽象性:核心子系统通常是经过高度抽象的业务实体,不包含具体的实现细节或业务规则。
-
独立性:核心子系统应当尽可能独立于具体的业务场景和技术实现,保持技术中立。
1.2 周边子系统的识别与管理
与核心子系统相对的是周边子系统,它们通常具有以下特点:
-
可变性:周边子系统往往会随着业务需求的变化而频繁调整。例如,促销活动模块、广告系统等。
-
依赖性:周边子系统通常依赖于一个或多个核心子系统,而不是相反。
-
可插拔性:良好的周边子系统设计应当支持在不影响系统核心功能的情况下的灵活替换或移除。
1.3 区分核心与周边的实践方法
在实际项目中,如何准确区分核心与周边子系统呢?以下是一些实用的方法:
-
业务价值分析法:从业务角度出发,识别哪些功能直接贡献于产品的核心价值主张,哪些是辅助性或增强性的功能。
-
变更频率观察:通过分析需求变更历史,识别哪些模块变化频繁(通常是周边),哪些相对稳定(可能是核心)。
-
失败影响评估:假想某个子系统完全失效,评估对整体系统的影响程度。影响越大,越可能是核心子系统。
-
依赖关系分析:绘制模块间的依赖关系图,被广泛依赖的模块往往更接近核心。
以典型的社交应用为例,用户关系图谱、内容发布系统通常是核心子系统,而节日特效、小游戏等功能则属于周边子系统。
二 子系统或模块的保持完整性原则
2.1 子系统与最小模块的定义标准
一个良好定义的子系统或最小模块应当具备完整性,这意味着:
-
功能完备:模块应当封装完成特定业务功能所需的全部逻辑和数据,外部使用者不需要了解内部实现细节。
-
数据闭环:模块应当管理自己领域内的完整数据生命周期,包括创建、读取、更新和删除(CRUD)操作。
-
自治性:模块应当能够在合理范围内自主决策,不依赖于外部系统的微观管理。
在Android开发中,完整性原则可以通过恰当的包结构设计、数据封装和接口抽象来实现。例如,一个用户认证模块应当包含从UI到网络请求的全部相关代码,而不是将这些部分分散在不同的包中。
2.2 模块的独立性标准
独立性是模块化设计的核心价值所在,具体体现在:
-
编译时独立:理想情况下,模块应当能够独立编译和测试,不依赖于其他业务模块的具体实现。
-
运行时解耦:模块间的通信应当通过明确定义的接口进行,避免直接的方法调用或数据共享。
-
依赖最小化:遵循依赖倒置原则(DIP),模块应当依赖于抽象而非具体实现。
在Android项目中,可以通过Gradle的模块化支持来实现编译时独立,使用接口或抽象类来定义模块边界,以及采用依赖注入框架(如Dagger或Hilt)来管理模块间的依赖关系。
2.3 自包容系统的设计要点
自包容意味着模块既是功能完整的独立单元,又能与外部系统清晰地区分开来。实现自包容需要注意:
-
明确的边界定义:通过Java/Kotlin中的接口、Android中的AIDL或自定义协议来明确定义模块的边界。
-
统一的交互方式:为模块间的交互建立统一的模式,如回调函数、事件总线或响应式流。
-
完备的上下文环境:模块应当能够获取执行其功能所需的全部上下文信息,而不需要向调用方索取额外数据。
例如,在Android中设计一个支付模块时,应当通过明确的接口暴露支付功能,接收标准化的支付请求参数,并通过统一的回调接口返回支付结果。模块内部则封装所有与支付平台交互的细节,包括网络通信、数据解析、错误处理等。
三 子系统与模块的组织要有方法论
3.1 接口设计与契约编程
模块组合的基础是良好的接口设计。在Android开发中,接口设计应当遵循以下原则:
-
角色接口而非头等接口:接口应当代表模块在特定上下文中的角色,而非简单地反映内部实现。
-
最小权限原则:只暴露必要的操作,保持接口的简洁性。
-
稳定抽象:接口一旦发布,应当尽量保持稳定,变更时考虑兼容性策略。
3.2 通信模式与事件处理
模块间的通信应当尽可能松耦合。Android开发中常用的通信模式包括:
-
回调接口:适用于一对一的请求-响应式交互。
-
事件总线:适用于一对多的通知场景。
-
响应式流:适用于数据变化的持续观察。
3.3 分层组合与架构模式
在大型Android应用中,模块的组合往往需要遵循一定的架构模式。常见的分层方式包括:
-
横向分层:
-
表现层(UI)
-
领域层(业务逻辑)
-
数据层(持久化和网络)
-
-
纵向切分:
-
按业务功能划分模块(如用户模块、商品模块、订单模块)
-
每个功能模块内部可以包含自己的分层结构
-
现代Android架构推荐结合使用Clean Architecture和模块化设计:
以下示例第一层为模块化设计,里层为一对多的Clean Architecture模式
app/
└── features/
├── feature1/
│ ├── presentation/
│ ├── domain/
│ └── data/
└── feature2/
├── presentation/
├── domain/
└── data/
libs/
├── core/
└── shared/
3.4 动态组合与插件化
对于需要更高灵活性的场景,可以考虑动态模块组合技术:
-
Android动态功能模块:
// build.gradle
android {
dynamicFeatures = [':feature:payment']}
-
按需加载:
-
服务发现模式:
interface ServiceProvider {fun <T> getService(serviceClass: Class<T>): T?}// 模块注册服务class PaymentServiceProvider : ServiceProvider {override fun <T> getService(serviceClass: Class<T>): T? {return when(serviceClass) {
PaymentService::class.java -> PaymentServiceImpl()else -> null}}}
// 使用服务val paymentService = serviceProvider.getService(PaymentService::class.java)
四 业务拆分的演进与趋势
从单体到模块化的演进路径
-
初级阶段:按包名分模块
-
中级阶段:Gradle模块化
-
高级阶段:动态功能模块+独立部署
最后
业务拆分作为软件架构师手中的"瑞士军刀",是一种强大而灵活的技术手段。通过区分核心与周边子系统、明确定义模块边界、合理组合模块组件,Android架构师可以构建出既灵活又稳定的应用架构。
有效的业务拆分能够带来多方面的收益:
-
提高代码的可维护性和可测试性
-
支持团队的大规模并行开发
-
实现功能的灵活组合与动态部署
-
降低系统复杂度,使各部分的演化更加独立
然而,业务拆分也不是银弹,需要根据项目规模、团队结构和业务特点来合理应用。随着Android生态的不断发展,业务拆分技术也将持续演进,为构建更加健壮、灵活的移动应用提供支持。