Java 单体仓库:如何组织大型项目的代码
在现代 Java 项目开发中,经常面临一个问题:将所有组件存储在一个仓库(单体仓库)中,还是将它们分散到多个独立的仓库(多仓库)中?单体仓库(monorepo)是一种将多个项目、库和服务的代码存储在统一版本控制系统中的方法。在本文中,我们将分析如何使用 Maven 和 Gradle 在单体仓库中高效组织 Java 代码,并探讨实际案例。
Java 单体仓库的优缺点
在开始实施之前,了解何时使用单体仓库是合理的很重要。对于 Java 项目,如果多个服务共享公共库、数据模型或接口,单体仓库尤其有用。
单体仓库的优点
- 统一的依赖版本:所有模块使用相同版本的库,从而避免冲突。
- 简单的重构:对公共 API 或数据模型的更改可以在一次提交中原子化完成。
- 简化的 CI/CD:一个流水线即可构建和测试所有模块。
- 透明性:每位开发者都能看到整个项目的代码,从而更容易理解架构。
缺点及缓解方法
- 仓库大小:随着时间的推移,仓库可能变得非常庞大。解决方案:使用
.gitignore并且不存储二进制工件。 - CI 配置复杂:需要能够仅运行已更改的模块。像 Bazel 或 Gradle Build Cache 这样的工具会有所帮助。
- 代码审查缓慢:如果所有更改都通过一个合并请求。解决方案:使用分支策略和自动触发器。
Java 单体仓库的结构:实际示例
让我们考虑一个使用 Maven 的典型 Java 单体仓库结构。假设我们有三个模块:core(通用逻辑)、service-api(REST API)和 service-worker(后台处理器)。
my-java-monorepo/├── pom.xml # 父 POM├── core/│ ├── pom.xml│ └── src/│ └── main/java/com/example/core/│ └── Utils.java├── service-api/│ ├── pom.xml│ └── src/│ └── main/java/com/example/api/│ └── MainController.java├── service-worker/│ ├── pom.xml│ └── src/│ └── main/java/com/example/worker/│ └── WorkerTask.java└── .gitignore父 POM (pom.xml)
在项目根目录创建一个父 POM,用于管理版本并列出模块:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-java-monorepo</artifactId> <version>1.0.0</version> <packaging>pom</packaging>
<modules> <module>core</module> <module>service-api</module> <module>service-worker</module> </modules>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.0</version> <type>pom</type> <scope>import</scope>```