Java 单体仓库:代码组织的实战指南
\n在现代 Java 开发中,越来越多地面临在单体仓库(monorepo)和多个独立仓库之间做出选择的问题。单体仓库是一种将所有项目代码(多个服务、库、配置)存储在一个 Git 仓库中的方法。在本文中,我们将探讨如何在实际中组织 Java 单体仓库:从文件夹结构到构建和 CI/CD 的配置。您将获得现成的代码示例和避免常见错误的建议。
\n\n为什么选择 Java 单体仓库?
\n对于许多模块相互依赖的 Java 项目,单体仓库尤其有用。主要优势包括:
\n- \n
- 统一的依赖版本 — 所有模块使用相同版本的库,从而避免冲突。 \n
- 简化的重构 — 在一个模块中更改 API 会立即在所有依赖它的模块中体现。 \n
- 共享的构建配置 — Gradle 或 Maven 只需为所有模块配置一次。 \n
- CI/CD 的完整性 — 所有服务使用一个流水线,更容易跟踪变更。 \n
然而,单体仓库需要纪律:没有清晰的结构,它会变得混乱。让我们看看正确的组织方式。
\n\nJava 项目单体仓库的结构
\n推荐的 Java 单体仓库结构(以 Gradle 为例):
\nmy-monorepo/\n├── build.gradle.kts # 根构建文件\n├── settings.gradle.kts # 包含所有模块\n├── gradle.properties # 公共属性\n├── libs/ # 公共库\n│ ├── common-utils/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── logging/\n│ ├── build.gradle.kts\n│ └── src/\n├── services/ # 微服务\n│ ├── user-service/\n│ │ ├── build.gradle.kts\n│ │ └── src/\n│ └── payment-service/\n│ ├── build.gradle.kts\n│ └── src/\n├── config/ # 配置(例如 checkstyle, spotbugs)\n│ └── checkstyle.xml\n└── docker/ # 每个服务的 Dockerfile\n ├── user-service.Dockerfile\n └── payment-service.Dockerfile\n\n关键原则:
\n- \n
- 模块分为
libs(库)和services(可执行应用程序)。 \n - 根目录的
build.gradle.kts定义公共插件和版本。 \n - 文件
settings.gradle.kts包含所有子项目。 \n
settings.gradle.kts 示例:
rootProject.name = "my-monorepo"\n\ninclude(\n "libs:common-utils",\n "libs:logging",\n "services:user-service",\n "services:payment-service"\n)\n\n\n配置模块间的依赖关系
\n单体仓库的主要任务是管理内部依赖。我们来看一个 user-service 依赖于 common-utils 的例子。
步骤 1. 在根目录的 gradle.properties 中声明版本:
# gradle.properties\nkotlinVersion=1.9.0\nspringBootVersion=3.2.0\ncommonUtilsVersion=1.0.0\n\n步骤 2. 在根目录的 build.gradle.kts 中引入插件并设置公共配置:
plugins {\n id("java-library") apply false\n id("org.springframework.boot") version "3.2.0" apply false\n id("io.spring.dependency-management") version "1.1.4" apply false\n}\n\nallprojects {\n group = "com.example"\n version = "1.0.0"\n\n repositories {\n mavenCentral()\n }\n}\n\nsubprojects {\n apply(plugin = "java-library")\n\n java {\n sourceCompatibility = JavaVersion.VERSION_17\n targetCompatibility = JavaVersion.VERSION_17\n }\n\n dependencies {\n implementation("org.slf4j:slf4j-api:2.0.9")\n testImplementation("org.j