开发规范

业务模块骨架与目录结构约定

确定 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-credential
  • java/ineed-modules/ineed-security/ineed-security-authorization/ineed-security-authorization-system-identity

命名规则

聚合模块命名

聚合模块目录名统一使用:

ineed-{domain}-{capability};存在实际能力分组时可继续使用 ineed-{domain}-{group}-{capability}

要求:

  • 全部小写
  • 使用短横线连接
  • 必须体现业务域和能力;只有领域确有中间分组时才增加该层,不能为满足固定段数虚构分组
  • 不允许使用含糊目录名,如 commoncore2tempdemo

示例:

  • ineed-config-category(域 + 能力)
  • ineed-marketing-campaign(域 + 能力)
  • ineed-security-authorization-access-credential
  • ineed-security-authorization-system-identity

子模块命名

子模块目录名统一使用:

{aggregate-module}-{layer}

示例:

  • ineed-security-authorization-access-credential-core
  • ineed-security-authorization-access-credential-engine
  • ineed-security-authorization-access-credential-jpa
  • ineed-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/aocontract/vocontract/querycontract/dto
  • engine 承载阻塞式业务编排、servicemanager 接口与业务规则
  • jpa 承载基于 JPA 的实体、仓储、manager 实现、映射
  • jdbc 承载基于 JDBC 的实体、仓储、manager 实现、映射
  • mybatis-flex 承载基于 MyBatis-Flex 的实体、mapper、manager 实现、映射
  • webmvc 承载阻塞式 Web 入口层,如 controller
  • engine-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/javamain/resourcestest/javatest/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 表达页面可见操作按钮,不填 permissionUrlcomponenticon
  • 独立入口不要套成 menu -> page。例如“系统身份”和“系统身份分组”都是独立入口时,结构应为 安全中心 catalog -> 系统身份管理 catalog -> 系统身份 menu / 系统身份分组 menu,而不是 系统身份管理 menu -> 系统身份 page / 系统身份分组 page
  • 权限清单必须补齐 orderWeight,按权限树 DESC 规则设计;同一资源推荐顺序为辅助查询 API、列表查询、CRUD、状态操作、回收站操作。
  • permissionDescription 面向授权人员阅读,必须使用业务语言,不写 idSetquerycommonParameters 等后端参数名。

子模块语义包结构

以下结构描述的是 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。业务源码可以直接引用这些生成类,例如 QLoginLogQOperationLog。编译阶段能够通过,是因为 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-pluginannotationProcessorPaths;这属于当前编译模块自己的编译期处理器列表,不要假设依赖模块中的 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 不放 ORM entity
  • core 不放 repositorymappercontroller
  • 同一共享能力如果同时存在阻塞式与响应式定义,阻塞式放 service/,响应式放 service/reactive/
  • 如果接口围绕当前登录上下文工作,优先直接传 SecuritySession 等聚合上下文对象,不把 identityTypeprincipalIdtenantKey 拆成多个零散参数
  • 如果某个动作型共享契约在阻塞式侧返回 void,响应式侧统一返回 Mono<Void>;只有查询、统计等读取型方法才返回具体结果值
  • 新建模块和新增代码统一使用 contract/* 包名,而不是直接散落在根包下,也不要生成新的 pojo/*

兼容说明:

  • 遗留业务模块中可能仍然存在 pojo/aopojo/querypojo/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,不要回退生成 class
  • Repository 接口直接组合 ineed-core-data-jdbc 提供的 fragment,例如 CreateRepositoryUpdateRepositoryFindByIdRepositoryLastModifiedRepositoryDataStatusRepositoryLogicDeleteRepositoryOrderRepositoryTreeRepositoryAccountRepositoryPhysicsDeleteRepository 等;按实体能力择需组合,不机械全量继承
  • 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;不要在 ManagerMapper 中手写一套散落的 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*Service
  • webmvc 模块中的 service 不承担业务主编排,不替代 engineservice

engine-reactive

推荐结构:

com/ineed/{domain}/{object}/
  manager/
  service/
  support/

补充约定:

  • 响应式 servicemanager 的默认方法统一按订阅期装配,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-r2dbc fragment,并保留 GetEntityClassOperation<T> + default getEntityClass()
  • 业务 Manager 不再实现 getEntityClass();实体类型从 getOperation().getEntityClass() 读取,避免 RepositoryManager 两边返回不一致
  • 没有真实自定义持久化逻辑时,不生成 repository/support/*Abstract*R2dbcRepository 或空 RepositoryImpl
  • 只有遇到 fragment 无法覆盖的自定义 SQL 或框架限制时,才新增具体 Impl,并保持实现范围最小化
  • getKeySearchColumns()appendWhereOfQuery(...) 中的主表列必须使用 getQualifiedColumn("column_name");不要直接写裸列名。只有确实需要 join 或 VO 扩展字段时才覆盖 buildSelect() / buildJoin() / getDefaultTableAlias()
  • 自定义 RepositoryImpl 一旦进入响应式链路,entityClasstableNamerowMapper 等解析必须延迟到订阅期;优先复用 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*Service
  • webflux 模块中的响应式适配器负责对外桥接和上下文转换,不替代 engine-reactive 层业务编排

补充约束:

  • 转换层统一使用 converter/ 包,不再生成 mapstruct/
  • 转换实现类统一命名为 XxxConverter
  • MyBatis-Flex 的 mapper 与对象转换层 converter 必须拆开,避免同一聚合中两套语义不同的 XxxMapper
  • XxxConverter 继承 AoEntityConverter / EntityVoConverter 后,默认只保留继承关系和必要的 INSTANCE;不要重复生成 ao2EntityentityCopyappleAoentity2VO
  • 需要字段表达式、字典标签、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 类声明格式约定

业务模块生成 ManagerServiceControllerRepositoryMapperConverter 等类时,长 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 模块实现,并且需要同时支持 webmvcwebflux 两条线时,统一采用以下约定:

  • 共享接口定义优先放在发起方的 core 共享层,而不是散落在某个 webmvc / webflux 实现模块
  • 阻塞式共享契约放 service/
  • 响应式共享契约放 service/reactive/
  • webmvc / webflux 模块只保留各自的适配实现,命名可使用 WebMvc*Service / WebFlux*Service
  • 调用方如果已经拿到当前会话对象,接口入参优先直接使用 SecuritySession 一类聚合上下文对象,而不是继续拆分成多个基础字段
  • create / update / delete / status / account 这类副作用型响应式共享契约不要向上层暴露影响行数;阻塞式为 void 时,响应式统一定义为 Mono<Void>

新功能跨模块对齐规则

无论人工实现、代码生成器生成还是自动化工具修改,只要需求没有明确限制范围,新增功能都必须默认同步评估并对齐:

  • 数据层:coreengineengine-reactivejpajdbcmybatis-flexr2dbc
  • Web 层:webmvcwebflux
  • 对齐内容不只是类文件存在,还包括 entityrepository / mappermanagerservicecontroller、映射、回调、统一返回、异常语义与测试入口
  • 同一业务对象在不同模块中的目录层级、类命名、方法职责和代码风格必须保持一致,不能出现一边有 manager / repository、另一边缺层或风格漂移
  • 阻塞式 Service 的异常日志收口也属于对齐范围,统一走 ServiceSupport.logError(...)
  • 阻塞式 Manager 的可选能力解析也属于对齐范围,统一优先走 typed OperationSupport
  • JDBC / JPA / MyBatis-Flex 的字典查询默认过滤语义必须一致:统一先 handleQueryParameters(...),再强制 dataStatus.enabled = true
  • 只有在需求文档已经明确限定“本次只做阻塞式”或“本次只做指定数据栈”时,才允许暂时缩小范围;否则不要只改 jpa 或只改 webmvc

字段生成补充约定

无论人工实现、代码生成器生成还是自动化工具修改,新增 entitycontract/* 下对象时统一遵守 code-style-guide.md,并额外满足:

  • AO 字段如果标记 @Schema(requiredMode = RequiredMode.REQUIRED),必须同步增加 jakarta.validation.constraints 校验注解;String / CharSequence 使用 @NotBlankList / Set / Collection / Map / 数组使用 @NotEmpty,其它对象、数字、布尔、时间、枚举类型使用 @NotNull
  • AO 字段如果标记 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) 或仅转调 superbeforeSave(...)beforeUpdate(...)
  • AO / VO 默认按未来 Excel 导入导出对象考虑;凡是明确参与导入导出的字段,应直接补齐 @ExcelProperty
  • 即使当前阶段暂未接入 Excel,也要保持 AO / VO 的字段命名、中文语义和注解落点可平滑接入,避免后续为导入导出返工拆类或改字段

跨层禁行规则

  • core 不放 ORM entity
  • core 不把共享扩展契约散落到某一条 Web 线实现模块
  • engine / engine-reactive 不放 repository / mapper 实现
  • jpa / jdbc / mybatis-flex / r2dbc 不放 controller
  • webmvc / webflux 不放持久化实现
  • 同一扩展点的响应式定义不与阻塞式定义混放在同一个 service 根包下
  • Service 不与 Service 互调
  • 跨表业务通过组合多个 Manager 完成
  • 删除顺序和依赖清理按业务规则落在正确的删除钩子或业务编排中

创建模块前的一致性检查

创建业务模块前,至少必须检查以下内容:

  1. SQL 文件、需求说明、目标模块目录三者是否一致
  2. 聚合模块名称是否符合 {domain}-{capability}-{object}
  3. 子模块名称是否符合 {aggregate-module}-{layer}
  4. 是否已创建统一 src 骨架与 .gitkeep
  5. 当前模块应走完整骨架、阻塞式最小骨架,还是响应式最小骨架
  6. core 是否作为共享 contract 层被复用,而不是重复创建 {module}-reactive-core
  7. core 下 contract 包命名是否已确定并在整个模块内保持一致
  8. 如果存在共享扩展契约,是否已确定接口定义落在 core,并按 service/service/reactive/ 分包
  9. 围绕当前会话上下文的接口,是否优先采用 SecuritySession 等聚合上下文对象作为入参
  10. 是否已按 Ineed-Core 文档约定确定:
  • Controller -> Service -> Manager -> Repository/EntityManager
  • Service 不互调
  • 删除扩展与日志/异常落点
  1. 如果业务涉及多对多关系、级联删除、认证/授权或多数据栈实现,是否已按 business-integration-test-guide.md 规划模块级主回归测试入口
  2. 是否已明确本次功能要同步覆盖哪些数据栈与 Web 入口;如果没有明确限制,是否已按默认规则对齐 jpa / jdbc / mybatis-flex / r2dbc 以及 webmvc / webflux
  3. 字段风格是否已统一遵守 code-style-guide.md;是否没有生成新的 pojo/*AORequiredMode.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”的历史描述。新模块仍需按本规范决定实体能力、权限、租户和依赖边界,不逐字照抄样例中的所有业务规则。

阅读建议

最后更新:2026-05-15

仍有疑问?按反馈清单整理复现信息 →