Architecture

Layering and dependency direction

app to domain to data/infra to common/config; the direction only points down and the build enforces it.

Layering is not decoration. It exists so that any person and any AI, given a requirement, can predict which layer the code belongs in.

Dependency direction

app  ──▶  domain  ──▶  data / infra  ──▶  common / config
 ▲                                        │
 └──────────── reverse is forbidden ───────┘

Warning: a reverse dependency is not "discouraged", it is a build failure: the assertion names the file and method.

What each layer owns

LayerResponsibilityTypical types
appAccept requests, validate input, convert DTO to VO, orchestrate callsXxxController / XxxService / XxxDTO / XxxVO
domainMulti-step flows, cross-table consistency, rule orchestrationXxxHandler / XxxCmd / enums / exceptions
infraTouches the database and nothing else: entities, mappers, repositoriesXxxDO / XxxMapper / XxxRepository
dataShared foundation for queries and writesBaseRepository / PageQuery / PageQueries
common / configContext, threads, global exceptions, framework wiringMiniMaoContextHolder / ResponseUtils

Hard rules

  • app must not import infra.mapper directly — it goes through the Repository.
  • Controllers only bind parameters, validate, call and return. No business logic, no queries, no condition assembly.
  • domain must not reference Spring Web types and must not return transport-layer objects.
  • @Transactional must not appear in the Repository layer.
  • Field injection with @Autowired is banned; always final plus a constructor.
  • System.out and new Date() are banned (there is a banned-pattern assertion).
  • new Thread and self-built thread pools are banned; async work uses the framework executor.

What a violation looks like

Not a warning and not a review comment: the build fails and the output names the class, method and line. That is why nobody has to remember the conventions.

Transitional exceptions

Tip: a few legacy types still have reverse dependencies. They are transitional exceptions and must not spread. Write new code by the rules on this page; when you hit an exception, log it rather than copying it.

Last updated 2026-10-09Edit this page →