Maven 多模块项目设计 核心概念 Maven 多模块项目是一个父项目 包含多个子模块 的结构,通过统一的 POM 文件管理依赖版本、编译配置等,实现代码复用和项目解耦。
为什么使用多模块?
优势
说明
依赖管理
统一管理版本号,避免版本冲突
代码复用
公共代码提取到独立模块
模块解耦
各模块职责清晰,便于维护
构建优化
支持增量构建,提高效率
团队协作
不同团队可独立开发不同模块
典型的多模块结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 my-project/ ├── pom.xml # 父 POM (聚合和依赖管理) ├── my-common/ │ ├── pom.xml │ └── src / ├── my-service/ │ ├── pom.xml │ └── src / ├── my-web/ │ ├── pom.xml │ └── src / └── my-app/ ├── pom.xml └── src /
模块分类
父模块 :只包含 POM,不含源代码
公共模块 :提供通用工具、常量、基类等
业务模块 :核心业务逻辑
Web/API 模块 :对外接口层
启动模块 :应用入口
父 POM 配置 1. 聚合配置(Aggregation) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <project > <modelVersion > 4.0.0</modelVersion > <groupId > com.example</groupId > <artifactId > my-project</artifactId > <version > 1.0.0</version > <packaging > pom</packaging > <modules > <module > my-common</module > <module > my-service</module > <module > my-web</module > <module > my-app</module > </modules > </project >
作用 :执行 mvn clean install 时,会自动构建所有子模块
2. 依赖管理(Dependency Management) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-dependencies</artifactId > <version > 2.7.0</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > my-common</artifactId > <version > ${project.version}</version > </dependency > </dependencies > </dependencyManagement >
关键点 :
只声明版本,不引入依赖
子模块可选择是否使用
避免版本冲突
3. 插件管理(Plugin Management) 1 2 3 4 5 6 7 8 9 10 11 12 13 <pluginManagement > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.1</version > <configuration > <source > 11</source > <target > 11</target > </configuration > </plugin > </plugins > </pluginManagement >
子模块 POM 配置 基本结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <project > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > com.example</groupId > <artifactId > my-project</artifactId > <version > 1.0.0</version > <relativePath > ../pom.xml</relativePath > </parent > <artifactId > my-common</artifactId > <name > Common Module</name > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > </project >
模块间依赖 1 2 3 4 5 6 7 8 <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > my-common</artifactId > </dependency > </dependencies >
设计最佳实践 1. 模块职责划分 1 2 3 4 5 6 7 my -project/ ├── my -common/ ├── my -domain/ ├── my -repository/ ├── my -service/ ├── my -controller/ └── my -app/
依赖流向 :app → controller → service → repository → domain ← common
2. 版本管理策略 1 2 3 4 5 6 7 8 9 10 11 12 13 <properties > <project.version > 1.0.0</project.version > <spring-boot.version > 2.7.0</spring-boot.version > <java.version > 11</java.version > </properties > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > <version > ${spring-boot.version}</version > </dependency >
3. 避免循环依赖 1 2 ❌ 错误:A → B → A ✅ 正确:A → B → C(单向依赖)
检查命令 :
4. 模块粒度控制
粒度
优点
缺点
粗粒度
构建快,管理简单
模块职责混乱
细粒度
职责清晰,复用性好
构建复杂,依赖管理困难
建议 :根据团队规模和项目复杂度平衡
常用 Maven 命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 mvn clean install mvn clean install -pl my-common mvn clean install -pl my-service -am mvn clean install -DskipTests mvn dependency:tree mvn dependency:tree | grep "CIRCULAR"
实战示例:电商系统 1 2 3 4 5 6 7 8 ecommerce-platform/ ├── pom.xml ├── ecommerce-common/ ├── ecommerce-domain/ ├── ecommerce-repository/ ├── ecommerce-service/ ├── ecommerce-api/ └── ecommerce-app/
依赖关系 1 2 3 4 5 6 7 8 ecommerce -app ├── ecommerce-api │ ├── ecommerce-service │ │ ├── ecommerce-repository │ │ │ └── ecommerce-domain │ │ └── ecommerce-common │ └── ecommerce-common └── ...
常见问题 Q1: 子模块如何访问其他子模块的资源? A : 通过依赖声明,不要直接引用文件路径
1 2 3 4 5 6 7 8 <dependency > <groupId > com.example</groupId > <artifactId > my-common</artifactId > </dependency >
Q2: 如何只构建某个模块? A : 使用 -pl 参数
1 mvn clean install -pl my-service
Q3: 子模块版本与父模块不同可以吗? A : 不建议。应该保持一致,使用 ${project.version}
Q4: 如何处理模块间的版本冲突? A : 在父 POM 的 dependencyManagement 中统一定义
总结
要点
说明
聚合
父 POM 通过 <modules> 统一构建
继承
子模块通过 <parent> 继承配置
依赖管理
使用 <dependencyManagement> 统一版本
模块划分
按职责清晰划分,避免循环依赖
版本策略
统一版本号,便于维护和发布
框架类型项目设计(SPI 模式) 框架类型项目与普通业务系统不同,需要支持可插拔 和扩展性 。典型场景:
RAG 系统:多种向量数据库(ES、PG、Neo4j、Milvus)
ORM 框架:多种数据库方言(MySQL、PostgreSQL、Oracle)
消息队列:多种实现(Kafka、RabbitMQ、RocketMQ)
核心设计思想 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌─────────────────────────────────────────────────────────────┐ │ 用户应用层 │ │ (只依赖 API + 需要的实现) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ rag-core (核心模块) │ │ 定义接口 + 默认实现 + SPI 加载机制 │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ rag-store-es │ │ rag-store-pg │ │ rag-store-neo4j │ │ (ES 实现) │ │ (PgVector) │ │ (Neo4j 实现) │ └─────────────────┘ └─────────────────┘ └─────────────────┘
关键原则 :
依赖倒置 :核心模块定义接口,实现模块依赖核心
按需引入 :用户只引入需要的实现模块
SPI 发现 :运行时自动发现并加载实现
实战示例:RAG 系统模块结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 rag-framework/ ├── pom.xml │ ├── rag-core/ │ ├── pom.xml │ └── src/ main/ java/ │ └── com/ example/ rag/ │ ├── api/ │ │ ├── VectorStore.java │ │ └── DocumentSplitter.java │ ├── spi/ │ │ └── SpiLoader.java │ └── model/ │ └── Document.java │ ├── rag-store-elasticsearch/ │ ├── pom.xml │ └── src/ main/ │ ├── java/.../ElasticsearchVectorStore.java │ └── resources/ META-INF/ services/ │ └── com.example.rag.api.VectorStore │ ├── rag-store-pgvector/ │ ├── pom.xml │ └── src/ main/ │ ├── java/.../PgVectorStore.java │ └── resources/ META-INF/ services/ │ └── com.example.rag.api.VectorStore │ ├── rag-store-neo4j/ │ └── ... │ ├── rag-splitter-fixed/ │ └── ... │ ├── rag-splitter-sentence/ │ └── ... │ ├── rag-splitter-semantic/ │ └── ... │ └── rag-spring-boot-starter/ └── ...
核心模块设计(rag-core) 1. 定义 SPI 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface VectorStore { String getType () ; void store (List<Document> documents) ; List<Document> search (String query, int topK) ; }public interface DocumentSplitter { String getType () ; List<Document> split (Document document) ; }
2. SPI 加载器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class SpiLoader { private static final Map<Class<?>, Map<String, Object>> CACHE = new ConcurrentHashMap <>(); @SuppressWarnings("unchecked") public static <T> T load (Class<T> clazz, String type) { Map<String, Object> implementations = CACHE.computeIfAbsent( clazz, k -> loadImplementations(clazz) ); T impl = (T) implementations.get(type); if (impl == null ) { throw new IllegalArgumentException ( "No implementation found for type: " + type ); } return impl; } private static <T> Map<String, Object> loadImplementations (Class<T> clazz) { Map<String, Object> map = new HashMap <>(); ServiceLoader<T> loader = ServiceLoader.load(clazz); for (T impl : loader) { String type = getType(impl); map.put(type, impl); } return map; } }
3. 核心模块 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-core</artifactId > <name > RAG Core</name > <dependencies > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > </dependency > </dependencies > </project >
实现模块设计 1. ES 实现模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class ElasticsearchVectorStore implements VectorStore { @Override public String getType () { return "elasticsearch" ; } @Override public void store (List<Document> documents) { } @Override public List<Document> search (String query, int topK) { return null ; } }
2. SPI 配置文件 1 2 # src/main/resources/META-INF/services/com.example.rag.api.VectorStore com.example.rag.store.es.ElasticsearchVectorStore
3. 实现模块 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-store-elasticsearch</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > </dependency > <dependency > <groupId > co.elastic.clients</groupId > <artifactId > elasticsearch-java</artifactId > <version > 8.10.0</version > </dependency > </dependencies > </project >
用户使用方式 按需引入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > <version > 1.0.0</version > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-store-pgvector</artifactId > <version > 1.0.0</version > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-splitter-semantic</artifactId > <version > 1.0.0</version > </dependency > </dependencies >
代码使用 1 2 3 4 5 6 7 8 9 VectorStore store = SpiLoader.load(VectorStore.class, "pgvector" );DocumentSplitter splitter = SpiLoader.load(DocumentSplitter.class, "semantic" );@Value("${rag.store.type}") private String storeType;VectorStore store = SpiLoader.load(VectorStore.class, storeType);
依赖关系图 1 2 3 4 5 6 7 8 9 用户项目 │ ├── rag-core (必需) │ ├── rag-store-xxx (选一个) │ └── rag-core │ └── rag-splitter-xxx (选一个) └── rag-core
关键点 :
用户不需要 同时引入所有实现
运行时通过 SPI 自动发现引入的实现
新增实现只需要添加新模块,不修改核心代码
Spring Boot Starter 封装 为了更好的集成体验,可以提供 Starter:
1 2 3 4 5 6 7 8 9 10 11 <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-autoconfigure</artifactId > </dependency > </dependencies >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Configuration @EnableConfigurationProperties(RagProperties.class) public class RagAutoConfiguration { @Bean @ConditionalOnMissingBean public VectorStore vectorStore (RagProperties props) { return SpiLoader.load(VectorStore.class, props.getStore().getType()); } @Bean @ConditionalOnMissingBean public DocumentSplitter documentSplitter (RagProperties props) { return SpiLoader.load(DocumentSplitter.class, props.getSplitter().getType()); } }
1 2 3 4 5 6 7 rag: store: type: pgvector splitter: type: semantic chunk-size: 500
框架设计对比
设计模式
适用场景
典型框架
SPI + 多实现模块
同一接口多种实现
JDBC、SLF4J
策略模式
算法/行为可替换
切片方式、嵌入模型
模板方法
固定流程可扩展步骤
Spring Template
工厂模式
对象创建解耦
各种 Factory
扩展性设计要点
接口隔离 :核心接口放在独立模块,无第三方依赖
可选依赖 :使用 <optional>true</optional> 避免传递依赖
默认实现 :核心模块提供简单默认实现
配置驱动 :通过配置切换实现,而非代码修改
版本兼容 :接口变更需考虑向后兼容
完整框架结构(含应用层) 实际项目中,除了可插拔的底层模块,还需要应用层 来组合这些模块并对外提供服务。
完整模块结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 rag-framework/ ├── pom.xml │ │ == == = 框架层(可独立发布给第三方使用)== == = │ ├── rag-core/ │ └── 无外部依赖,纯接口定义 │ ├── rag-stores/ │ ├── pom.xml │ ├── rag-store-core/ │ ├── rag-store-elasticsearch/ │ ├── rag-store-pgvector/ │ └── rag-store-neo4j/ │ ├── rag-splitters/ │ ├── pom.xml │ ├── rag-splitter-core/ │ ├── rag-splitter-fixed/ │ ├── rag-splitter-sentence/ │ └── rag-splitter-semantic/ │ ├── rag-embeddings/ │ ├── pom.xml │ ├── rag-embedding-core/ │ ├── rag-embedding-openai/ │ └── rag-embedding-local/ │ ├── rag-spring-boot-starter/ │ │ == == = 应用层(组合模块,对外提供服务)== == = │ ├── rag-service/ ├── rag-api/ └── rag-server/
抽象层设计(rag-store-core) 抽象层的职责:
抽象基类 :实现公共逻辑,子类只需实现差异部分
工具类 :连接池管理、序列化、重试机制等
配置基类 :公共配置项
依赖关系 1 2 3 4 5 6 7 rag-store -elasticsearch │ ▼ rag-store -core (抽象层) │ ▼ rag-core (接口)
抽象层代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public abstract class AbstractVectorStore implements VectorStore { protected final VectorStoreConfig config; protected AbstractVectorStore (VectorStoreConfig config) { this .config = config; } @Override public void store (List<Document> documents) { validateDocuments(documents); List<List<Document>> batches = partition(documents, config.getBatchSize()); for (List<Document> batch : batches) { doStore(batch); } } @Override public List<Document> search (String query, int topK) { if (topK <= 0 || topK > config.getMaxTopK()) { topK = config.getDefaultTopK(); } return doSearch(query, topK); } protected abstract void doStore (List<Document> documents) ; protected abstract List<Document> doSearch (String query, int topK) ; protected void validateDocuments (List<Document> docs) { if (docs == null || docs.isEmpty()) { throw new IllegalArgumentException ("Documents cannot be empty" ); } } protected <T> List<List<T>> partition (List<T> list, int size) { List<List<T>> result = new ArrayList <>(); for (int i = 0 ; i < list.size(); i += size) { result.add(list.subList(i, Math.min(i + size, list.size()))); } return result; } }@Data public class VectorStoreConfig { private int batchSize = 100 ; private int defaultTopK = 5 ; private int maxTopK = 100 ; private int connectTimeout = 5000 ; private int readTimeout = 30000 ; }
具体实现(继承抽象层) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public class PgVectorStore extends AbstractVectorStore { private final JdbcTemplate jdbcTemplate; public PgVectorStore (PgVectorConfig config, JdbcTemplate jdbcTemplate) { super (config); this .jdbcTemplate = jdbcTemplate; } @Override public String getType () { return "pgvector" ; } @Override protected void doStore (List<Document> documents) { String sql = "INSERT INTO documents (content, embedding) VALUES (?, ?::vector)" ; jdbcTemplate.batchUpdate(sql, documents, documents.size(), (ps, doc) -> { ps.setString(1 , doc.getContent()); ps.setArray(2 , toSqlArray(doc.getEmbedding())); }); } @Override protected List<Document> doSearch (String query, int topK) { String sql = """ SELECT content, 1 - (embedding <=> ?::vector) as score FROM documents ORDER BY embedding <=> ?::vector LIMIT ? """ ; return jdbcTemplate.query(sql, documentRowMapper, queryVector, queryVector, topK); } }@Data @EqualsAndHashCode(callSuper = true) public class PgVectorConfig extends VectorStoreConfig { private String tableName = "documents" ; private int dimension = 1536 ; private String indexType = "ivfflat" ; }
聚合模块 POM 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-stores</artifactId > <packaging > pom</packaging > <name > RAG Vector Stores</name > <modules > <module > rag-store-core</module > <module > rag-store-elasticsearch</module > <module > rag-store-pgvector</module > <module > rag-store-neo4j</module > </modules > </project >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-stores</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-store-core</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > </dependency > </dependencies > </project >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-stores</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-store-pgvector</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-store-core</artifactId > </dependency > <dependency > <groupId > org.postgresql</groupId > <artifactId > postgresql</artifactId > </dependency > <dependency > <groupId > com.pgvector</groupId > <artifactId > pgvector</artifactId > </dependency > </dependencies > </project >
父 POM 聚合配置 1 2 3 4 5 6 7 8 9 10 11 <modules > <module > rag-core</module > <module > rag-stores</module > <module > rag-splitters</module > <module > rag-embeddings</module > <module > rag-spring-boot-starter</module > <module > rag-service</module > <module > rag-api</module > <module > rag-server</module > </modules >
模块依赖全景图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────┐ │ rag-server │ │ (启动类 + 配置 + 打包) │ └─────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ rag-api │ │rag-store-pg │ │rag-splitter- │ │ (Controller) │ │ (选择的实现) │ │ semantic │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┐ │ │ │ rag-service │ │ │ │ (业务逻辑) │◄───────┴───────────────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ rag-core │ │ (核心接口) │ └──────────────┘
Service 层设计(rag-service) Service 层负责组合 底层组件,实现具体业务逻辑。
模块 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-service</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-core</artifactId > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > </dependency > </dependencies > </project >
Service 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @Service public class RagService { private final VectorStore vectorStore; private final DocumentSplitter splitter; private final EmbeddingModel embeddingModel; public RagService (VectorStore vectorStore, DocumentSplitter splitter, EmbeddingModel embeddingModel) { this .vectorStore = vectorStore; this .splitter = splitter; this .embeddingModel = embeddingModel; } public void ingest (Document document) { List<Document> chunks = splitter.split(document); for (Document chunk : chunks) { float [] embedding = embeddingModel.embed(chunk.getContent()); chunk.setEmbedding(embedding); } vectorStore.store(chunks); } public List<Document> retrieve (String query, int topK) { return vectorStore.search(query, topK); } public String chat (String question) { List<Document> contexts = retrieve(question, 5 ); String prompt = buildPrompt(question, contexts); return llmClient.generate(prompt); } }
Controller 层设计(rag-api) Controller 层负责对外暴露 HTTP 接口 。
模块 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-api</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-service</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > </project >
Controller 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @RestController @RequestMapping("/api/rag") public class RagController { private final RagService ragService; public RagController (RagService ragService) { this .ragService = ragService; } @PostMapping("/ingest") public ResponseEntity<Void> ingest (@RequestBody IngestRequest request) { Document doc = new Document (request.getContent(), request.getMetadata()); ragService.ingest(doc); return ResponseEntity.ok().build(); } @GetMapping("/retrieve") public ResponseEntity<List<DocumentVO>> retrieve ( @RequestParam String query, @RequestParam(defaultValue = "5") int topK) { List<Document> docs = ragService.retrieve(query, topK); return ResponseEntity.ok(DocumentVO.from(docs)); } @PostMapping("/chat") public ResponseEntity<ChatResponse> chat (@RequestBody ChatRequest request) { String answer = ragService.chat(request.getQuestion()); return ResponseEntity.ok(new ChatResponse (answer)); } }
DTO 定义 1 2 3 4 public record IngestRequest (String content, Map<String, Object> metadata) {}public record ChatRequest (String question) {}public record ChatResponse (String answer) {}
启动模块设计(rag-server) 启动模块负责选择具体实现 + 打包部署 。
模块 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <project > <parent > <groupId > com.example</groupId > <artifactId > rag-framework</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag-server</artifactId > <packaging > jar</packaging > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag-api</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-spring-boot-starter</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-store-pgvector</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag-splitter-semantic</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
启动类 1 2 3 4 5 6 7 @SpringBootApplication public class RagServerApplication { public static void main (String[] args) { SpringApplication.run(RagServerApplication.class, args); } }
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 server: port: 8080 rag: store: type: pgvector datasource: url: jdbc:postgresql://localhost:5432/rag username: postgres password: secret splitter: type: semantic chunk-size: 500 overlap: 50 embedding: type: openai api-key: ${OPENAI_API_KEY}
多种部署方式 通过不同的 server 模块,可以灵活组合:
1 2 3 4 5 6 7 8 9 10 11 rag-server-pg/ ├── pom.xml # 引入 pgvector + semantic └── application.yml rag-server-es/ ├── pom.xml # 引入 elasticsearch + fixed └── application.yml rag-server-all/ ├── pom.xml # 引入所有实现 └── application .yml
模块职责总结
模块
职责
依赖
rag-core
核心接口定义
无
rag-store-xxx
向量存储实现
core
rag-splitter-xxx
切片策略实现
core
rag-spring-boot-starter
自动配置
core
rag-service
业务逻辑,组合组件
core
rag-api
REST Controller
service
rag-server
启动模块,选择实现
api + starter + 具体实现
框架使用者 vs 框架开发者
角色
使用模块
说明
框架使用者
core + starter + 实现模块
集成到自己项目
框架开发者
全部模块
开发和维护框架
独立部署
server
直接运行服务
简化三层结构(推荐) 对于中小型项目,可以采用更简洁的三层结构:
1 2 3 4 5 6 rag4j/ ├── pom.xml ├── rag4j-framework/ ├── rag4j-project/ ├── rag4j-app/ └── test/
各层详细结构 1. 框架层(rag4j-framework) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 rag4j-framework/ ├── pom.xml └── src/ main/ java/ com/ example/ rag4j/ framework/ │ ├── core/ │ ├── VectorStore.java │ ├── DocumentSplitter.java │ ├── EmbeddingModel.java │ └── Document.java │ ├── store/ │ ├── AbstractVectorStore.java │ └── VectorStoreConfig.java │ ├── splitter/ │ ├── AbstractSplitter.java │ └── SplitterConfig.java │ ├── embedding/ │ └── AbstractEmbeddingModel.java │ ├── spi/ │ └── SpiLoader.java │ └── util/ ├── RetryUtil.java └── BatchUtil.java
特点 :纯抽象,无第三方依赖(或最小依赖)
2. 实现层(rag4j-project) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 rag4j-project/ ├── pom.xml │ ├── rag4j-store-pgvector/ │ ├── pom.xml │ └── src/.../PgVectorStore.java │ ├── rag4j-store-elasticsearch/ │ └── ... │ ├── rag4j-store-neo4j/ │ └── ... │ ├── rag4j-splitter-fixed/ │ └── ... │ ├── rag4j-splitter-semantic/ │ └── ... │ ├── rag4j-embedding-openai/ │ └── ... │ └── rag4j-spring-boot-starter/ └── ...
特点 :每个实现独立模块,按需引入
3. 应用层(rag4j-app) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 rag4j-app/ ├── pom.xml └── src/ main/ ├── java/ com/ example/ rag4j/ app/ │ ├── Rag4jApplication.java │ ├── config/ │ │ └── Rag4jConfig.java │ ├── service/ │ │ └── RagService.java │ ├── controller/ │ │ └── RagController.java │ └── dto/ │ ├── ChatRequest.java │ └── ChatResponse.java └── resources/ └── application.yml
特点 :组合框架层和实现层,对外提供服务
依赖关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ┌─────────────────────────────────────────────────────────────┐ │ rag4j-app │ │ (Controller + Service + 启动配置) │ │ │ │ 依赖: framework + 选择的实现模块 + starter │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ rag4j-store-pg │ │rag4j-splitter- │ │ rag4j-embedding │ │ │ │ semantic │ │ -openai │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └───────────────────┼───────────────────┘ ▼ ┌───────────────────────────┐ │ rag4j-framework │ │ (接口 + 抽象类 + SPI ) │ └───────────────────────────┘
POM 配置示例 父 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <project > <groupId > com.example</groupId > <artifactId > rag4j</artifactId > <version > 1.0.0</version > <packaging > pom</packaging > <modules > <module > rag4j-framework</module > <module > rag4j-project</module > <module > rag4j-app</module > <module > test</module > </modules > <properties > <java.version > 17</java.version > <spring-boot.version > 3.2.0</spring-boot.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-framework</artifactId > <version > ${project.version}</version > </dependency > </dependencies > </dependencyManagement > </project >
应用层 POM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <project > <parent > <groupId > com.example</groupId > <artifactId > rag4j</artifactId > <version > 1.0.0</version > </parent > <artifactId > rag4j-app</artifactId > <dependencies > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-framework</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-store-pgvector</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-splitter-semantic</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-embedding-openai</artifactId > </dependency > <dependency > <groupId > com.example</groupId > <artifactId > rag4j-spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies > </project >
两种结构对比
对比项
细粒度结构
三层简化结构
模块数量
多(10+)
少(3-4 + 实现)
适用规模
大型框架
中小型项目
灵活性
高
适中
维护成本
高
低
学习曲线
陡
平缓
典型案例
Spring Framework
业务系统
建议 :
对外发布的框架 → 细粒度结构,方便用户按需引入
内部项目/业务系统 → 三层简化结构,开发效率高