业务模块骨架与目录结构约定
确定 Maven 分层、业务子模块与跨技术栈对齐范围。
浏览全部手册
本页目录
文档来源与 Markdown 原文
权威正文:ineed-core/docs/project-development/business-module-layout-guide.md。网站按工作区快照同步,原文中的历史日期和验证范围保留。
开始编码前核对同版本源码;跨仓文件引用可在源码定位目录查找。
下载 Markdown 原文 ↓文档定位
本文档定义基于 Ineed-Core 开发业务模块时,推荐采用的:
- 聚合模块骨架
- 子模块命名规则
- 子模块物理目录模板
main/java下的语义包结构- 创建模块前必须执行的一致性检查
本文档面向两类对象:
- 人工创建新业务模块的开发者
- 基于 SQL / 需求说明自动生成代码的代码生成器或自动化工具
如果你要判断的是“普通 Java 模块、聚合模块、BOM 模块该不该有 src”,先读 java-module-directory-guide.md。
本文档只负责业务聚合模块及其子模块骨架,不替代通用 Java 模块物理目录规范。
适用范围
本文档描述的是“业务模块约定”,不是 ineed-core 自身的真实 Maven 聚合清单。
真实稳定模块清单仍以 module-inventory.md 为准。
这里定义的是业务代码在接入 Ineed-Core 时应遵守的标准骨架,适用于类似以下目录:
java/ineed-modules/ineed-security/ineed-security-authorization/ineed-security-authorization-access-credentialjava/ineed-modules/ineed-security/ineed-security-authorization/ineed-security-authorization-system-identity
命名规则
聚合模块命名
聚合模块目录名统一使用:
ineed-{domain}-{capability};存在实际能力分组时可继续使用 ineed-{domain}-{group}-{capability}
要求:
- 全部小写
- 使用短横线连接
- 必须体现业务域和能力;只有领域确有中间分组时才增加该层,不能为满足固定段数虚构分组
- 不允许使用含糊目录名,如
common、core2、temp、demo
示例:
ineed-config-category(域 + 能力)ineed-marketing-campaign(域 + 能力)ineed-security-authorization-access-credentialineed-security-authorization-system-identity
子模块命名
子模块目录名统一使用:
{aggregate-module}-{layer}
示例:
ineed-security-authorization-access-credential-coreineed-security-authorization-access-credential-engineineed-security-authorization-access-credential-jpaineed-security-authorization-access-credential-webmvc
标准业务模块骨架
正式业务聚合模块默认按完整骨架创建:
{module}/
pom.xml
{module}-core/
{module}-engine/
{module}-jpa/
{module}-jdbc/
{module}-mybatis-flex/
{module}-webmvc/
{module}-engine-reactive/
{module}-r2dbc/
{module}-webflux/
其中各子模块职责如下:
core承载常量定义、枚举定义、工具类、共享 contract,以及跨模块复用的共享 service 契约,如contract/ao、contract/vo、contract/query、contract/dtoengine承载阻塞式业务编排、service、manager接口与业务规则jpa承载基于 JPA 的实体、仓储、manager 实现、映射jdbc承载基于 JDBC 的实体、仓储、manager 实现、映射mybatis-flex承载基于 MyBatis-Flex 的实体、mapper、manager 实现、映射webmvc承载阻塞式 Web 入口层,如 controllerengine-reactive承载响应式业务编排、响应式service、响应式manager接口与业务规则r2dbc承载基于 R2DBC 的实体、仓储、manager 实现、映射webflux承载响应式 Web 入口层,如 controller
最小骨架规则
阻塞式最小骨架
如果当前模块只接入阻塞式主链,可暂时最少创建:
{module}/
pom.xml
{module}-core/
{module}-engine/
{module}-jpa/
{module}-jdbc/
{module}-mybatis-flex/
{module}-webmvc/
响应式最小骨架
如果当前模块只接入响应式主链,最少创建:
{module}/
pom.xml
{module}-core/
{module}-engine-reactive/
{module}-r2dbc/
{module}-webflux/
约束
core是阻塞式与响应式共用的共享契约层- 不再单独创建
{module}-reactive-core - 正式业务模块默认优先按完整骨架预留目录和
pom.xml - 即使某些子模块暂未实现,也建议先保留空骨架,避免后续模块结构失衡
- 只有在当前里程碑已明确限定为“阻塞式最小骨架”或“响应式最小骨架”时,才允许暂时省略其他子模块
- 后续补齐子模块时,必须继续沿用本文档的命名与分层,不得临时发明新的层名
标准子模块物理目录模板
每个业务子模块都必须先生成统一的 src 骨架:
src/
├── main
│ ├── java
│ │ └── .gitkeep
│ └── resources
│ └── .gitkeep
└── test
├── java
│ └── .gitkeep
└── resources
└── .gitkeep
规则:
- 即使当前还没有实现代码,也必须保留
main/java、main/resources、test/java、test/resources - 空目录通过
.gitkeep入库 - 不允许只创建
main/java而忽略测试目录
如果模块包含模块私有 ErrorCode、字段国际化或对象国际化,推荐继续补齐:
src/main/resources/
i18n/ineed/{module-segments}/
message.multiproperties
messages.properties
messages_en.properties
messages_zh_TW.properties
相关常量和 i18n 规则继续参考 constant-errorcode-i18n-guide.md。
模块自描述资源放在 META-INF/ineed 下,由框架通过 classpath*: 自动扫描聚合。不要把这类资源散落在 resources 根目录或业务目录下。
src/main/resources/
META-INF/ineed/permissions/
{module-key}.json
META-INF/ineed/security/whitelist/
{module-key}.properties
规则:
- 权限清单使用
META-INF/ineed/permissions/*.json。 - 安全访问白名单使用
META-INF/ineed/security/whitelist/*.properties。 - 不要再新增根目录
whitelist.properties,也不要使用security/{module}/whitelist.properties这类旧路径。 - i18n 继续放在
i18n/ineed/...,不要迁入META-INF。
权限清单 JSON 的节点类型统一使用 catalog / menu / page / api / button:
catalog表示目录或分组节点,不对应具体前端路由、组件或后端接口,不配置permissionUrl/component。一级大类如平台、业务、运营、安全中心使用catalog;多个独立菜单需要归组时,中间层也使用catalog。menu表示独立出现在菜单中的前端入口,必须配置前端permissionUrl和真实component。只要资源是独立入口,就使用menu,不要降级成page。page表示挂在某个菜单或目录下的隐藏/从属页面,不独立出现在菜单中,例如详情页、编辑页、配置页、主从资源的子列表页。典型识别信号是该页面依赖主资源 ID 进入,后端通常有ISearchOfMasterIdController/search-of-master-id能力。api表达后端接口能力,permissionUrl填后端接口路径;详情、批量详情、列表查询、字典查询、回收站查询、search-of-master-id都应生成api。button表达页面可见操作按钮,不填permissionUrl、component、icon。- 独立入口不要套成
menu -> page。例如“系统身份”和“系统身份分组”都是独立入口时,结构应为安全中心 catalog -> 系统身份管理 catalog -> 系统身份 menu / 系统身份分组 menu,而不是系统身份管理 menu -> 系统身份 page / 系统身份分组 page。 - 权限清单必须补齐
orderWeight,按权限树DESC规则设计;同一资源推荐顺序为辅助查询 API、列表查询、CRUD、状态操作、回收站操作。 permissionDescription面向授权人员阅读,必须使用业务语言,不写idSet、query、commonParameters等后端参数名。
子模块语义包结构
以下结构描述的是 src/main/java 下推荐的语义包布局。
源码注释约定
开发者、代码生成器或自动化工具在创建业务模块源码时,Javadoc、@author、字段声明、注解分行、类声明换行和 import 整理统一遵守 code-style-guide.md。
本文档只补充业务模块骨架要求:生成模板中的默认职责边界应保持一致,AO 承载入参和输入校验,Query 只表达查询条件,VO / Detail 面向展示返回,Entity 只表达表结构和持久化映射,Repository 只提供数据访问能力,Manager 承载单实体规则、查询拼装、状态操作和持久化编排,Service 只组织一次业务用例流程,Controller / API 只暴露访问契约。
如果生成后业务规则已经明确,开发人员应继续把模板生成的通用说明补充为更具体的业务规则、状态约束、生命周期说明或异常边界,而不是保留空泛注释。
JPA QueryDSL Javadoc 构建约定
JPA 模块如果通过 apt-maven-plugin 生成 QueryDSL Q* 类,生成目录通常是 target/generated-sources/java。业务源码可以直接引用这些生成类,例如 QLoginLog、QOperationLog。编译阶段能够通过,是因为 Maven 编译会纳入生成源码;但 maven-javadoc-plugin 默认只扫描 ${project.build.sourceDirectory},即 src/main/java。
因此,JPA 模块只要源码 Javadoc 阶段需要解析 Q* 类型,就必须在该模块的 maven-javadoc-plugin 中补齐 sourcepath:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<sourcepath>${project.build.sourceDirectory}${path.separator}${project.build.directory}/generated-sources/java</sourcepath>
</configuration>
</plugin>
这个配置的目的不是屏蔽 Javadoc 检查,也不是跳过 Q* 类,而是让 Javadoc 的类型解析范围与编译阶段一致,避免 cannot find symbol: class Q...。
父 POM 中的 <show>public</show> 只控制 Javadoc 对已纳入生成范围的 public API 生成文档;它不决定哪些源码目录被纳入类型解析,也不会专门屏蔽 QueryDSL Q* 类。Q* 生成类如果被 Javadoc 实际纳入文档生成集合,可能仍会出现 no comment 警告;这类 generated-sources 下的 Q* 注释警告可接受,不应为了它们给生成源码补手写注释或关闭真实源码的 Javadoc 质量检查。
需要注意的是,sourcepath 中加入 target/generated-sources/java 后,Javadoc 可以把 QueryDSL Q* 类当作依赖类型解析;但这不等同于把所有 Q* 类都作为本模块的主文档对象输出。也就是说,Q* 类虽然是 public,但只要它们只是被业务源码引用的解析依赖,而不是 Javadoc 插件本次要生成文档的源码入口,就不会因为 <show>public</show> 自动触发 no comment 警告。真正需要处理的是 src/main/java 下的业务源码 Javadoc 警告;target/generated-sources/java 下生成类的注释警告可以忽略。
MyBatis-Flex Annotation Processor 构建约定
MyBatis-Flex 模块如果使用 @Table 等注解生成辅助元数据,必须确保 mybatis-flex-processor 作为 annotation processor 参与编译。MyBatis-Flex 官方 Maven 文档建议把 mybatis-flex-processor 配置到 maven-compiler-plugin 的 annotationProcessorPaths;这属于当前编译模块自己的编译期处理器列表,不要假设依赖模块中的 processor 配置会自动作用到本业务模块。
Ineed 项目内可以在上层父 POM 统一维护 mybatis-flex.version,但 mybatis-flex-processor 本身应像 JPA QueryDSL APT 一样独立启用:只有需要生成 entity.table.*TableDef 的 {module}-mybatis-flex 子模块才配置该 processor。不要把 mybatis-flex-processor 放进全局父 POM 的通用 annotation processor path,避免所有 Java 模块都被无差别加载。
mybatis-flex-processor 只需要位于 annotation processor path 中,不应作为业务模块运行期依赖;如果为了 IDE 识别临时放入 <dependencies>,应使用 provided,不要把 processor 扩散为运行期依赖。
模块级核心配置示例:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths combine.children="append">
<path>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>${mybatis-flex.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
这个配置属于编译期 annotation processor 配置,不是 Javadoc sourcepath 配置;不要用 JPA QueryDSL 的 apt-maven-plugin 写法替代 MyBatis-Flex processor,也不要因为业务模块依赖了 ineed-core-data-mybatis-flex 就额外添加 junit-jupiter 等测试依赖。
core
新建业务模块推荐结构:
com/ineed/{domain}/{object}/
{Object}Constant.java
enums/
contract/dto/
contract/ao/
contract/query/
contract/vo/
service/
service/reactive/
util/
约束:
core只承载共享 contract、常量、枚举、工具类core可以放跨模块复用的共享service契约,但不放业务service实现core不放 ORMentitycore不放repository、mapper、controller- 同一共享能力如果同时存在阻塞式与响应式定义,阻塞式放
service/,响应式放service/reactive/ - 如果接口围绕当前登录上下文工作,优先直接传
SecuritySession等聚合上下文对象,不把identityType、principalId、tenantKey拆成多个零散参数 - 如果某个动作型共享契约在阻塞式侧返回
void,响应式侧统一返回Mono<Void>;只有查询、统计等读取型方法才返回具体结果值 - 新建模块和新增代码统一使用
contract/*包名,而不是直接散落在根包下,也不要生成新的pojo/*
兼容说明:
- 遗留业务模块中可能仍然存在
pojo/ao、pojo/query、pojo/vo一类旧命名 pojo/*只作为遗留代码识别项,不作为新模块或新增代码的生成目标- 旧模块如果已经大面积使用
pojo/*,新增对象必须先迁移到contract/*或明确拆出迁移任务,不要继续扩散旧结构 - 创建新模块时,不应因为参考了历史示例目录,就回退生成
pojo/*结构
engine
推荐结构:
com/ineed/{domain}/{object}/
manager/
service/
support/
约束:
service是业务事务与编排边界manager表达局部业务能力或规则support只放少量辅助编排类engine不放具体 ORM 实现- 阻塞式
service默认方法统一使用try/catch + ServiceSupport.logError(log, "...", e)记录异常后原样抛出,不再重复写if (log.isErrorEnabled())模板 - 阻塞式
manager如果要访问可选下游能力,统一优先使用OperationSupport.getOperation(Class<T>)/requireOperation(Class<T>)获取 typed operation
jpa
推荐结构:
com/ineed/{domain}/{object}/jpa/
entity/
repository/
manager/
converter/
jdbc
推荐结构:
com/ineed/{domain}/{object}/jdbc/
entity/
querydsl/
repository/
manager/
converter/
补充约定:
Repository默认生成interface,不要回退生成classRepository接口直接组合ineed-core-data-jdbc提供的 fragment,例如CreateRepository、UpdateRepository、FindByIdRepository、LastModifiedRepository、DataStatusRepository、LogicDeleteRepository、OrderRepository、TreeRepository、AccountRepository、PhysicsDeleteRepository等;按实体能力择需组合,不机械全量继承Repository接口统一继续继承GetEntityClassOperation<T>,并在接口内保留default Class<T> getEntityClass()- 关系表、明细表或只承载少量动作的仓储,只组合真正需要的 fragment,并直接在接口中声明模块私有查询 / 删除方法
- 不再生成模块内的
repository/support/*、Abstract*JdbcRepository或只做转发的RepositoryImpl - 只有确实存在 fragment 无法覆盖的自定义 SQL 或 Spring Data JDBC 限制时,才允许新增具体
Impl;此时实现只承载那部分真实差异,不重复包一层通用 CRUD - JDBC manager 需要组装 Querydsl SQL 查询、排序、搜索字段或 join 时,必须在模块内提供手写
querydsl/QXxx extends QuerydslSqlTable表路径类 QXxx类只承载 JDBC SQL 表达式元数据:表名、静态单例、业务字段StringPath/NumberPath/DateTimePath等;它是源码维护的 JDBC Querydsl SQL table path,不是 JPA APT 生成的Q*类- manager 统一覆盖
getSqlTablePath()返回对应QXxx静态单例,并在getKeySearchPath()、appendWhereOfQuery()、buildDefaultOrderBy()、join 查询中直接引用QXxx.xxx.field - 已存在
QXxx可表达的列,不允许在 manager 查询组装中继续散落"column_name"字符串、JdbcQuerydslUtil.stringColumn(getSqlTablePath(), "...")或等价临时列对象 - 当前 JDBC 代码生成的对标样板以
ineed-security-authorization-system-identity-jdbc为准,ineed-config-*与ineed-platform-tenant-jdbc已按这一路线收敛
mybatis-flex
推荐结构:
com/ineed/{domain}/{object}/mybatisflex/
entity/
mapper/
manager/
converter/
约束:
- MyBatis-Flex 实现层使用
mapper mapper/只保留持久化接口,不承载对象转换实现- 对象转换实现统一放
converter/,类名统一使用XxxConverter - 业务
*-mybatis-flex模块如果需要使用QueryWrapper拼装跨表查询,必须在本模块独立接入mybatis-flex-processor;如果父级已配置 Lombok / MapStruct processor,本模块使用annotationProcessorPaths combine.children="append"追加 MyBatis-Flex processor,不要重复复制整套父级 processor 配置 - 跨表查询优先引用 processor 生成的
entity.table.*TableDef,例如XxxTableDef.XXX.as("x")后再使用生成列对象拼装select / join / where / orderBy;不要在Manager或Mapper中手写一套散落的QueryTable / QueryColumn常量 - JPA / JDBC 实现层使用
repository - 新增或重构查询时,JPA 与 MyBatis-Flex 是优先优化路径:JPA 优先使用框架约定查询 / QueryDSL,MyBatis-Flex 优先使用
QueryWrapper+ 生成的TableDef。JDBC / R2DBC 保持当前 core 支撑层和集中 SQL 实现,只做必要维护,不为了风格统一主动引入 Criteria、jOOQ 或 MyBatis 集成。 - 字典查询默认标签列统一按
label_value/labelValue对齐;默认标签读取失败时再 fallback 到id
webmvc
推荐结构:
com/ineed/{domain}/{object}/
controller/
service/
如确实存在明显分工差异,可按需要继续分为:
controller/admin/controller/internal/controller/runtime/
但如果尚未形成稳定分层,不要提前过度拆分。
补充约束:
service/只放webmvc这一线的适配器或桥接实现,例如实现core中共享 service 契约的WebMvc*Servicewebmvc模块中的service不承担业务主编排,不替代engine层service
engine-reactive
推荐结构:
com/ineed/{domain}/{object}/
manager/
service/
support/
补充约定:
- 响应式
service与manager的默认方法统一按订阅期装配,requireManager(...)、requireOperation(...)、上下文读取和能力判断都放进 reactive 链内部 - reactive
manager缺少下游operation/support时,应在订阅时发出错误信号,不保留链外同步throw - 查询型 reactive
manager如果要给QueryParameters补默认过滤条件,也必须放进Mono.defer(...)或链内准备步骤;不要在返回 Publisher 之前同步修改入参 - create / update / delete / status / account 这类副作用型响应式
manager/service,阻塞式语义为void时,响应式统一返回Mono<Void>
r2dbc
推荐结构:
com/ineed/{domain}/{object}/r2dbc/
entity/
repository/
manager/
converter/
补充约定:
Repository默认同样生成interface- 响应式仓储与
jdbc侧保持同一套路:直接组合ineed-core-data-r2dbcfragment,并保留GetEntityClassOperation<T> + default getEntityClass() - 业务
Manager不再实现getEntityClass();实体类型从getOperation().getEntityClass()读取,避免Repository和Manager两边返回不一致 - 没有真实自定义持久化逻辑时,不生成
repository/support/*、Abstract*R2dbcRepository或空RepositoryImpl - 只有遇到 fragment 无法覆盖的自定义 SQL 或框架限制时,才新增具体
Impl,并保持实现范围最小化 getKeySearchColumns()和appendWhereOfQuery(...)中的主表列必须使用getQualifiedColumn("column_name");不要直接写裸列名。只有确实需要 join 或 VO 扩展字段时才覆盖buildSelect()/buildJoin()/getDefaultTableAlias()- 自定义
RepositoryImpl一旦进入响应式链路,entityClass、tableName、rowMapper等解析必须延迟到订阅期;优先复用getEntityClassMono()、getTableNameMono()或Mono.defer(...) - 不允许在 Publisher 装配期提前求值
getEntityClass()、getTableName(),也不要生成Mono.just(getEntityClass())、Mono.just(getTableName())、final Class<?> entityClass = getEntityClass()这类代码 - R2DBC manager 基类命名统一使用
AbstractR2dbcManager;旧的AbstractJdbcManager仅作为兼容别名保留,不要在新代码中继续引用
webflux
推荐结构:
com/ineed/{domain}/{object}/
controller/
service/reactive/
补充约束:
service/reactive/只放webflux这一线的适配器或桥接实现,例如实现core中响应式共享契约的WebFlux*Servicewebflux模块中的响应式适配器负责对外桥接和上下文转换,不替代engine-reactive层业务编排
补充约束:
- 转换层统一使用
converter/包,不再生成mapstruct/ - 转换实现类统一命名为
XxxConverter - MyBatis-Flex 的
mapper与对象转换层converter必须拆开,避免同一聚合中两套语义不同的XxxMapper XxxConverter继承AoEntityConverter/EntityVoConverter后,默认只保留继承关系和必要的INSTANCE;不要重复生成ao2Entity、entityCopy、appleAo、entity2VO- 需要字段表达式、字典标签、ignore 规则或其他方法级 MapStruct 配置时,才显式声明对应转换方法;普通一比一转换交给父接口继承方法和 MapStruct 生成实现
- 如果业务代码需要接入
CreateManager/UpdateManager/FindByIdManager/OrderManager一类抽象,统一实现getCreateEntityConverter()、getUpdateEntityConverter()、getEntityVoConverter() - 如果业务对象需要按稳定业务键重复创建或刷新,例如注册、同步、上报场景,显式实现
CreateOrRefreshManager并实现findEntityByBusinessKey(ao);业务入口继续使用register/sync/importOne等命名并委托save(ao),不要生成saveOrUpdate/upsert - 业务键查询需要在各数据栈同步落地:JPA / JDBC 返回
Optional<Entity>,R2DBC 返回Mono<Entity>,MyBatis-Flex 可由 mapper 查询实体后在 Manager 包装Optional;特殊 JDBC 更新只覆盖persistUpdatedEntity(...)
Java 类声明格式约定
业务模块生成 Manager、Service、Controller、Repository、Mapper、Converter 等类时,长 extends / implements 声明统一遵守 code-style-guide.md。
ineed-develop-generator-template 中的 Java 模板如果新增或调整 AO / Query / VO / Detail / Entity / Repository / Manager / Controller / API / Constant 等类模板,必须同步维护类级 Javadoc 的职责说明,避免重新生成后把业务模块注释质量打回空泛模板。
共享扩展契约与 Web 适配约定
当某个能力由 A 模块定义、由 B 模块实现,并且需要同时支持 webmvc 与 webflux 两条线时,统一采用以下约定:
- 共享接口定义优先放在发起方的
core共享层,而不是散落在某个webmvc/webflux实现模块 - 阻塞式共享契约放
service/ - 响应式共享契约放
service/reactive/ webmvc/webflux模块只保留各自的适配实现,命名可使用WebMvc*Service/WebFlux*Service- 调用方如果已经拿到当前会话对象,接口入参优先直接使用
SecuritySession一类聚合上下文对象,而不是继续拆分成多个基础字段 - create / update / delete / status / account 这类副作用型响应式共享契约不要向上层暴露影响行数;阻塞式为
void时,响应式统一定义为Mono<Void>
新功能跨模块对齐规则
无论人工实现、代码生成器生成还是自动化工具修改,只要需求没有明确限制范围,新增功能都必须默认同步评估并对齐:
- 数据层:
core、engine、engine-reactive、jpa、jdbc、mybatis-flex、r2dbc - Web 层:
webmvc、webflux - 对齐内容不只是类文件存在,还包括
entity、repository / mapper、manager、service、controller、映射、回调、统一返回、异常语义与测试入口 - 同一业务对象在不同模块中的目录层级、类命名、方法职责和代码风格必须保持一致,不能出现一边有
manager / repository、另一边缺层或风格漂移 - 阻塞式
Service的异常日志收口也属于对齐范围,统一走ServiceSupport.logError(...) - 阻塞式
Manager的可选能力解析也属于对齐范围,统一优先走 typedOperationSupport - JDBC / JPA / MyBatis-Flex 的字典查询默认过滤语义必须一致:统一先
handleQueryParameters(...),再强制dataStatus.enabled = true - 只有在需求文档已经明确限定“本次只做阻塞式”或“本次只做指定数据栈”时,才允许暂时缩小范围;否则不要只改
jpa或只改webmvc
字段生成补充约定
无论人工实现、代码生成器生成还是自动化工具修改,新增 entity、contract/* 下对象时统一遵守 code-style-guide.md,并额外满足:
AO字段如果标记@Schema(requiredMode = RequiredMode.REQUIRED),必须同步增加jakarta.validation.constraints校验注解;String/CharSequence使用@NotBlank,List/Set/Collection/Map/ 数组使用@NotEmpty,其它对象、数字、布尔、时间、枚举类型使用@NotNullAO字段如果标记RequiredMode.NOT_REQUIRED或未明确必填,不要为了“看起来更严格”生成@NotBlank、@NotEmpty、@NotNull;业务条件必填应放在 Service / Manager 规则或专门的校验逻辑中表达- 非必填字段的格式、范围、长度等输入形态校验,优先直接放在
AO字段上使用 Jakarta Validation,例如@Email、@Pattern、@Size、@DecimalMin、@DecimalMax;这类校验不需要额外编写*ValidationSupport或在Manager.beforeSave(...)/Manager.beforeUpdate(...)中重复调用 - 不要在 Controller 方法参数上新增
@Valid;Ineed-Core 的 Web 入口会按框架节奏手动调度 Jakarta Validation,便于统一封装报错消息,并精确控制什么时候验证、什么时候不验证 - 自定义 create / update / assign 等方法如果需要手动调度 AO 校验,应先记录入口日志,再执行 Jakarta Validation;不要在日志前展开 AO 字段做空值兜底,也不要为 AO 上已经表达的必填规则再写
validateRequiredFields(...) *ValidationSupport不承担 AO 字段级必填、格式、长度等输入形态校验;它只用于跨字段业务规则、依赖持久化状态的规则,或独立方法参数(例如 masterId、organizationId)这类不属于 AO 的入口参数- 只有需要访问业务状态、持久化数据或跨字段业务规则时,才在
Manager中覆盖validate(ao);不要为了业务校验覆盖save(ao)、update(ao)或仅转调super的beforeSave(...)、beforeUpdate(...) AO / VO默认按未来 Excel 导入导出对象考虑;凡是明确参与导入导出的字段,应直接补齐@ExcelProperty- 即使当前阶段暂未接入 Excel,也要保持
AO / VO的字段命名、中文语义和注解落点可平滑接入,避免后续为导入导出返工拆类或改字段
跨层禁行规则
core不放 ORMentitycore不把共享扩展契约散落到某一条 Web 线实现模块engine/engine-reactive不放repository/mapper实现jpa/jdbc/mybatis-flex/r2dbc不放controllerwebmvc/webflux不放持久化实现- 同一扩展点的响应式定义不与阻塞式定义混放在同一个
service根包下 Service不与Service互调- 跨表业务通过组合多个
Manager完成 - 删除顺序和依赖清理按业务规则落在正确的删除钩子或业务编排中
创建模块前的一致性检查
创建业务模块前,至少必须检查以下内容:
- SQL 文件、需求说明、目标模块目录三者是否一致
- 聚合模块名称是否符合
{domain}-{capability}-{object} - 子模块名称是否符合
{aggregate-module}-{layer} - 是否已创建统一
src骨架与.gitkeep - 当前模块应走完整骨架、阻塞式最小骨架,还是响应式最小骨架
core是否作为共享 contract 层被复用,而不是重复创建{module}-reactive-corecore下 contract 包命名是否已确定并在整个模块内保持一致- 如果存在共享扩展契约,是否已确定接口定义落在
core,并按service/与service/reactive/分包 - 围绕当前会话上下文的接口,是否优先采用
SecuritySession等聚合上下文对象作为入参 - 是否已按 Ineed-Core 文档约定确定:
Controller -> Service -> Manager -> Repository/EntityManagerService不互调- 删除扩展与日志/异常落点
- 如果业务涉及多对多关系、级联删除、认证/授权或多数据栈实现,是否已按 business-integration-test-guide.md 规划模块级主回归测试入口
- 是否已明确本次功能要同步覆盖哪些数据栈与 Web 入口;如果没有明确限制,是否已按默认规则对齐
jpa / jdbc / mybatis-flex / r2dbc以及webmvc / webflux - 字段风格是否已统一遵守 code-style-guide.md;是否没有生成新的
pojo/*;AO中RequiredMode.REQUIRED字段是否已同步补齐@NotBlank/@NotEmpty/@NotNull;需要导入导出的AO / VO是否已考虑@ExcelProperty
如果以上检查未完成,不应直接开始生成代码。
SQL / 需求 / 目标模块一致性约定
创建或补齐业务聚合模块前,至少保持三类输入一致:
- 一份 SQL 文档
- 一份需求说明
- 一个目标业务模块目录
命名应尽量保持一致,例如:
- SQL:
ineed_security_access_credential.sql - 需求:
ineed_security_access_credential.md - 目标模块:
ineed-security-authorization-access-credential
使用代码生成器或自动化工具生成前,必须先校验这三者和目标模块是否对齐;如不一致,应先停止并指出冲突。
参考说明
分类模块目前已包含九层业务骨架和 contract/* 目录,可用于理解各层接入。逐层源码与运行步骤见首个模块教程。
参考实际业务时,应核对当前 POM 和源码,不能沿用早期“缺少 JDBC/响应式、仍使用 pojo”的历史描述。新模块仍需按本规范决定实体能力、权限、租户和依赖边界,不逐字照抄样例中的所有业务规则。
阅读建议
- 想看当前
ineed-core框架真实稳定模块清单,先读 module-inventory.md - 想理解框架能力分层与进入路径,读 module-capability-map.md
- 想让代码生成器或自动化工具按固定规则建模块和写代码,结合 development-contract.md 一起使用本文档
最后更新:2026-05-15
仍有疑问?按反馈清单整理复现信息 →