Architecture

Modules and package structure

Module name equals table prefix; naming conventions at a glance; where the reference implementation is.

Package structure

cn.minimao
├── app/<module>/{controller,service,dto,vo,convert}
├── domain
│   ├── handler/<module> + handler/<module>/cmd
│   ├── enums / enums/base
│   └── exception
├── infra/<module>/{model,mapper,repository,query}
├── data          # BaseRepository + query-condition framework
├── plugin        # extension points (plugin chain)
├── common        # context, threads, utilities, exceptions
└── config        # response envelope, MyBatis, Sa-Token, JSON wiring

Naming conventions

LayerSuffixBase class / contractLocation
infraXxxDOmust extends BaseDOinfra/<module>/model
infraXxxMapperBaseMapper<XxxDO> + @Mapperinfra/<module>/mapper
infraXxxRepositoryextends BaseRepository<XxxMapper, XxxDO>infra/<module>/repository
infraXxxPageQuerypagination carrier for custom SQL, extends PageCarrierQueryinfra/<module>/query
domainXxxHandlerextends MiniMaoAbstractHandlerdomain/handler/<module>
domainXxxCmdplain @Datadomain/handler/<module>/cmd
appXxxService@Serviceapp/<module>/service
appXxxDTO / XxxVOrequest / responseapp/<module>/{dto,vo}
appXxxConvert@Mapper(componentModel = "spring")app/<module>/convert
  • DO / VO / DTO / Cmd all use @Data; those extending a base class also add @EqualsAndHashCode(callSuper = true).
  • @TableName("table_name") is mandatory, and the module name is the table prefix.
  • infra/<module>/model holds table entities only; query carriers go in query — mixing them forces every reader to inspect annotations one by one.

Reference implementation

To see what the "standard answer" looks like, read these three packages: app/users/**, domain/handler/users/**, infra/users/**.

Tip: do not copy directories by hand for a new module. Use the skeleton generator and spend the saved time on business logic.

Last updated 2026-10-09Edit this page →