移动开发中的模块化 — 本质、原则与组织

作者: IT Sectr 发布日期: 2026-05-13 阅读时间: 9 分钟

模块化是一种原则,应用由独立模块组成,每个模块负责一项功能。根据 Android Developers,模块化通过并行编译加速构建,并允许团队独立处理应用的不同部分。模块化架构已成为拥有数十名开发人员的大型移动项目的标准。

要点

  • 模块化 — 将应用分解为具有清晰边界和接口的独立模块
  • Gradle 模块(Android)和 Swift Packages(iOS)— 模块化架构的主要工具
  • 代码隔离 防止不相关功能之间的意外依赖
  • 并行构建 模块可将大型项目的编译时间缩短 2–4 倍
  • Feature-first — 最流行的方法,每个屏幕或功能单独成为一个模块

移动开发中的模块化是什么

模块化是一种代码组织方式,应用由松散耦合的模块组成,每个模块通过公共接口提供严格定义的功能。与所有类都在一个项目中的单体架构不同,模块化方法将代码划分为物理上独立的编译单元。

模块化的主要目标是管理复杂性。开发人员可以专注于一个模块,而无需记住整个代码库。每个模块都有自己的职责范围,可以独立于其他模块进行开发、测试和部署。这在有 10 多名开发人员的项目中尤其有价值,因为在单体上的并行工作会导致频繁的合并冲突。

区分模块化与分层架构非常重要。层(Presentation、Domain、Data)按技术标准划分代码,而模块按功能标准划分。\u201c用户资料\u201d模块内部可以包含自己的层。在实践中,模块化方法和分层架构相结合:每个模块都有自己三层结构。

模块类型及其用途

功能模块 — 最常见的模块类型。每个屏幕或相关屏幕组都放在单独的模块中:Onboarding、Profile、Settings、Feed。功能模块包含功能所需的一切:UI、业务逻辑、数据层。模块的边界受到保护 — 其他功能无法访问其内部类。

核心模块包含公共基础设施:网络工作、数据库、分析、设计系统。它们不依赖于功能模块,但功能模块依赖于它们。这种划分保证了分析 SDK 的更改不会影响网络层,反之亦然。核心模块在功能之间重复使用,无需代码复制。

用于公共逻辑的共享模块

共享模块包含多个功能使用的代码:数据模型、工具、常量、自定义视图。共享模块的主要问题是变成垃圾场的风险(\u201cmisc module\u201d),不同代码会随时间积累。规则:共享模块应有明确的主题,例如 \u201cshared-ui\u201d 或 \u201cshared-models\u201d。

Android 中,共享模块通常被分离到以 lib 为前缀的库中:lib-network、lib-database、lib-ui-components。在 iOS 中,相同的功能由 Workspace 内的内部 Swift Packages 执行。在实践中,团队将共享模块的数量限制在 3–5 个,以避免创建使构建复杂化的过度依赖网络。

测试模块和测试隔离

单独的测试模块允许仅针对修改后的模块运行测试,而无需运行整个测试基础。这将 CI/CD pipeline 的时间从几小时缩短到几分钟。模块确保在构建级别进行分离:网络层的模块无法在测试中意外导入 UI 库。

每个模块都应具有明确定义的公共 API。在 Android 中,这通过访问修饰符和 Gradle 中的 api vs implementation 实现。在 iOS 中 — 通过 public/internal 访问修饰符和通过 Package.swift 管理的依赖关系。将可见性降至最低必要是模块化设计的关键实践。

Android 中的模块化:Gradle 模块

Gradle 原生支持模块化架构:每个模块都是独立的编译单元,拥有自己的 build.gradle 文件。Android 项目使用一个 application 模块(app)和多个 library 模块的组合。库模块不能作为应用运行,但可以作为 AAR 发布到仓库。

Gradle 的关键特性是独立模块的并行构建。如果模块 A、B 和 C 互不依赖,Gradle 同时编译它们,使用所有处理器核心。在 20+ 模块的项目中,这将完整的构建时间从 15 分钟缩短到 3–5 分钟。修改后模块的增量构建只需几秒钟。

Gradle 提供两种类型的模块间依赖关系:api(传递)和 implementation(非传递)。这种区别对模块化至关重要:implementation 对模块使用者隐藏传递依赖。如果 :profile 模块通过 implementation 使用 :networking,:profile 的使用者不知道 :networking 也无法引用它。

groovy
// settings.gradle — 模块声明
include ':app'
include ':feature:profile'
include ':feature:settings'
include ':core:network'
include ':core:database'

// build.gradle feature/profile — 模块依赖
dependencies {
    implementation project(':core:network')
    implementation project(':core:database')
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0'
}

代码展示了模块化 Android 项目的结构。Settings.gradle 列出所有模块,每个功能模块的 build.gradle 只指定其所需的核心模块。构建系统自动解析传递依赖并按正确顺序编译模块。

iOS 中的模块化:Swift Package Manager 和 CocoaPods

Swift Package Manager(SPM)— 自 2019 年以来 iOS 中模块化的标准工具。SPM 允许将应用拆分为 Swift Packages,每个包可以是库或可执行文件。Package 通过 Package.swift 定义模块(targets)及其依赖关系。SPM 集成在 Xcode 中,无需额外工具。

CocoaPods 仍然是第三方库的主要依赖管理器。Podfile 和 Podspec 定义模块化结构,CocoaPods 生成带有独立 pod 项目的 workspace。对于项目自身的模块化,团队越来越多地选择 SPM,因为它内置于 Xcode 中,无需安装。

iOS 模块化中,访问控制发挥重要作用:public、package、internal、fileprivate 和 private。模块只发布需要被其他模块访问的类型。内部实现细节隐藏在 internal 和 private 修饰符后面。这防止了模块之间出现隐藏依赖。

swift
// Package.swift — iOS 项目的模块化结构
let package = Package(
    name: "MyApp",
    platforms: [.iOS(SupportedPlatform.iOSVersion.v17)],
    products: [
        .library(name: "ProfileFeature", targets: ["ProfileFeature"]),
        .library(name: "NetworkCore", targets: ["NetworkCore"]),
    ],
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0")
    ],
    targets: [
        .target(name: "ProfileFeature", dependencies: ["NetworkCore"]),
        .target(name: "NetworkCore", dependencies: ["Alamofire"]),
    ]
)

Package.swift 声明两个库产品:ProfileFeature 和 NetworkCore。ProfileFeature 依赖于 NetworkCore,但不知道 Alamofire 的存在 — 它隐藏在 NetworkCore 内部。这种隔离是模块级分离的直接应用:HTTP 客户端中的更改不需要重新编译 ProfileFeature。

模块化架构的优势与挑战

模块化的主要优势是开发速度。团队并行处理不同模块,无代码冲突。CI/CD pipeline 只构建修改后的模块,只运行它们的测试。反馈时间缩短,发布频率增加。Spotify、Uber 和 Airbnb 发布了迁移到模块化架构的案例,指标提升了 2–3 倍。

第二个优势 — 错误隔离。Profile 模块中的 bug 不会影响 Payments 模块,如果它们之间没有直接依赖关系。这在高风险功能(支付、医疗数据)的应用中尤其重要,不相关屏幕上的错误不应阻止关键功能的发布。

主要挑战 — 依赖管理。设计不当会导致模块图出现,其中一个模块的更改级联重建数十个其他模块。解决方案 — 遵循无环规则:模块依赖图必须是有向无环图(DAG)。Gradle Module Graph Assert 等工具有助于在构建阶段检测循环。

第二个挑战 — 初始设置时间增加。创建模块化架构在项目启动阶段需要更多时间。有 1–3 名开发人员的小型项目可能无法从模块化中受益,在没有真正并行化需求的情况下花费时间维护模块边界。解决方案 — 从单体开始,随着团队成长逐步提取模块。

Feature-first 与 layer-first 方法

Feature-first 方法按功能对模块进行分组:每个屏幕或屏幕组成为一个独立模块。Layer-first 方法按技术标准划分代码:为 UI、业务逻辑和数据分别设置模块。在实践中,大多数团队选择带有核心模块的 feature-first — 这提供了更好的隔离和清晰的项目导航。

方法之间选择取决于团队规模和功能的可预测性。如果您确切知道项目中会有哪些屏幕,feature-first 允许每个开发人员对自己的模块负责。如果功能频繁更改并在屏幕间重叠,layer-first 在不同功能之间复用代码时提供了更大的灵活性。

常见问题

应用应该有多少个模块?

最佳数量取决于项目规模和团队规模。对于 5 人团队,6–10 个模块足够。对于 20+ 开发人员 — 20–40 个模块。规则:模块应足够小以便一个开发人员完全理解,又应足够大以避免创建过多的依赖网络。

模块化会减慢构建吗?

正确的模块化通过并行编译和缓存加速构建。但模块数量过多且依赖密集会减慢构建 — Gradle 和 Xcode 花费时间解析图。快速构建的关键 — 最小化传递依赖并遵循无环原则。

可以将现有应用模块化吗?

可以,但需要迭代进行。从提取核心模块(网络、数据库)开始,然后逐个提取功能。使用 feature flags 在旧单体代码旁并行启用新模块化代码。大型应用的完整迁移需要 3 到 12 个月。

模块化与微服务有什么区别?

模块是单个应用内的编译单元。微服务是在不同服务器上运行的独立进程。模块划分代码,微服务划分运行时。在移动开发中,\u201cmicroapps\u201d 这个术语常被用作混合体:可以独立运行的功能模块。

如何测试模块化应用?

每个模块都有自己的单元测试,可独立运行。集成测试检查模块间的交互。UI 测试使用模拟数据覆盖功能模块。模块化架构简化了测试:模拟另一个模块的依赖比模拟单体的一部分更容易。

总结

  • 模块化 — 将应用分解为具有清晰边界的独立编译单元
  • 功能模块将代码围绕功能分组,核心模块围绕基础设施分组
  • Gradle(Android)和 SPM(iOS)— 实现模块化架构的主要工具
  • 并行构建和代码隔离 — 模块化在大型项目中的主要优势
  • 依赖图必须是无环的,否则构建会变慢并出现循环引用
  • 带有核心模块的 Feature-first 方法被视为大型移动项目最有效的方式
  • 从单体开始,随着团队和代码库的增长逐步提取模块

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读