后端分层架构规范和标准包结构

1. 后端分层架构规范

目标

在真实复杂业务下,保持依赖方向清晰,避免 Service 互相纠缠,保证系统可长期演进。


1.1. 分层总览(必须遵守)

java 复制代码
Controller
    ↓
Service / Facade(业务主体 / 流程编排)
    ↓
Query(只读查询模型,允许 JOIN)
    ↓
Dao(单表持久化)
    ↓
Table

依赖只能自上而下,禁止反向。

1.2. 核心概念定义(统一口径)

1.2.1. 业务主体(Domain Entity)

具有独立业务语义和生命周期的对象,例如:

  • 用户、订单、库存、账户、商品

一个业务主体 = 一个 Service

1.2.2. 附属表(附属/技术性数据)

不具备独立业务语义,仅服务于某一主体:

  • 日志、流水、历史表、快照、审计表

可以由主体 Service 直接操作

1.2.3. 跨主体流程

涉及 两个及以上业务主体 的写操作:

  • 下单扣库存
  • 订单 + 支付
  • 用户 + 账户初始化

必须上移到 Facade / Flow Service

1.3. 各层职责与硬规则

1.3.1. Controller 层(入口层)

职责

  • 参数校验
  • 权限校验
  • 调用 Service / Facade

禁止

  • ❌ 写业务逻辑
  • ❌ 访问 Dao / Query
  • ❌ Controller 互调

1.3.2. Service 层(业务主体层)

职责

  • 本业务主体的业务规则
  • 状态变更
  • 操作本主体表及其附属表

允许

  • ✅ 写:本主体 Dao
  • ✅ 写:本主体附属表 Dao(日志 / 流水)
  • ✅ 读:Query(用于校验 / 展示 / 计算)

禁止

  • ❌ 写其他业务主体表
  • ❌ 调用其他业务主体 Service
  • ❌ 编写 JOIN SQL

判断口诀

"如果删掉另一个业务主体,这个方法是否仍成立?"

  • 是 → 留在本 Service
  • 否 → 上移 Facade

1.3.3. Facade / Flow Service(流程编排层)

职责

  • 跨业务主体流程
  • 调用顺序
  • 事务边界
  • 异常与失败处理

允许

  • ✅ 调用多个 Service
  • ✅ 管理事务

禁止

  • ❌ 直接访问 Dao
  • ❌ 编写 SQL
  • ❌ 实现领域细节规则

1.3.4. Query 层(只读模型,JOIN 主要归属)

职责

  • 多表查询
  • 列表 / 详情 / 统计 / 报表
  • 性能优化查询

允许

  • ✅ JOIN 多表
  • ✅ 跨业务主体
  • ✅ 返回 DTO / View Object

禁止

  • ❌ 写操作(INSERT / UPDATE / DELETE)
  • ❌ 承载业务规则
  • ❌ 修改业务状态

命名建议

  • XxxOnlyQuery
  • XxxReadModel
  • XxxQueryRepository

1.3.5. Dao 层(持久化层)

职责

  • 单表 CRUD
  • 与表结构一一对应

规则

  • ❌ 不允许 JOIN 多个业务主体表
  • ❌ 不承载业务语义
  • ❌ 不被跨业务主体复用

1.4. JOIN SQL 放置规则(强制执行)

一句话规则(写在 Code Review 标准里)

凡是 JOIN 了多个业务主体的 SQL,一律不进主体 Dao。

1.4.1. JOIN SQL 决策表(必须按顺序判断)

❓1️⃣ 是否跨多个业务主体?

  • 是 → Query
  • 否 → 继续

❓2️⃣ 是否只读?

  • 是 → Query
  • 否 → ❌ 拆为 Service 调用(禁止 JOIN 写)

❓3️⃣ 是否承载业务规则?

  • 是 → ❌ 不允许(回 Service)
  • 否 → Query

1.4.2. 允许放在主体 Dao 的唯一例外

同时满足以下所有条件:

  • JOIN 的表全部属于 同一业务主体

  • 关联的是:

    • 明细表
    • 日志表
    • 快照表
  • SQL 不影响其他主体决策

✅ 示例(允许):

java 复制代码
OrderDao.findOrderWithItems()
-- Order + OrderItem

❌ 示例(禁止):

java 复制代码
OrderDao.findOrderWithUser()
OrderDao.joinStock()

1.5. 最容易犯错的 3 种情况(必须避免)

1.5.1. 在 Service / Dao 中写跨主体 JOIN

java 复制代码
UPDATE order o
JOIN stock s ...

1.5.2. 为了"只是查一下",直接跨主体 Dao

java 复制代码
orderService → userDao

1.5.3. 统计 / 报表复用业务 Dao

java 复制代码
statisticsService → orderDao

1.6. 快速决策表(写代码前 10 秒)

场景 放置位置
单主体业务写 对应 Service
多主体业务写 Facade / Flow Service
多表 JOIN 查询 Query
日志 / 流水 主体 Service
报表 / 统计 StatisticsService + Query

1.7. 团队共识(强烈建议贴墙)

不要按"表"拆 Service,要按"业务主体"拆 Service。

不要为了 JOIN 方便,把查询塞进主体 Dao。

2. 标准包结构

下面是一份按真实中大型 Java / Spring 项目整理的,不是教学 Demo。

目标:

  • 一眼能看懂分层
  • 新人不容易写错
  • Code Review 时"看路径就能发现问题"

标准包结构示例(可直接照抄)

示例以 订单 + 用户 + 库存 场景为例

包名你可以换,公司名不重要

java 复制代码
com.xxx.project

2.1. 整体结构总览(先看全貌)

java 复制代码
com.xxx.project
├─ controller
├─ application        // Facade / 流程编排
├─ domain             // 业务主体
│   ├─ order
│   ├─ user
│   └─ stock
├─ query               // 只读查询模型(JOIN 主要在这)
├─ statistics          // 统计 / 报表
├─ infrastructure      // 基础设施(Dao / Mapper / MQ / Cache)
└─ common

2.2. Controller 层(入口)

java 复制代码
controller
├─ OrderController.java
├─ UserController.java
├─ StockController.java
├─ OrderStockController.java     // 对应 Facade
└─ StatisticsController.java

规则

  • Controller 只依赖:

    • domain.*.service
    • application.*
  • ❌ 不允许出现 Dao / Query

2.3. Application 层(Facade / Flow Service)

所有"跨业务主体写"的逻辑都在这里

java 复制代码
application
├─ orderstock
│   ├─ OrderStockFacade.java
│   └─ OrderStockFacadeImpl.java
└─ payment
    ├─ OrderPaymentFacade.java
    └─ OrderPaymentFacadeImpl.java

规则

  • 只做:

    • 调用顺序
    • 事务
    • 失败处理
  • ❌ 不写 SQL

  • ❌ 不访问 Dao

2.4. Domain 层(业务主体,最核心)

2.4.1. Order 领域

java 复制代码
domain/order
├─ service
│   ├─ OrderService.java
│   └─ OrderServiceImpl.java
├─ model
│   ├─ Order.java
│   └─ OrderStatus.java
└─ event
    └─ OrderCreatedEvent.java

OrderService 可以:

  • 写 OrderDao
  • 写 OrderLogDao
  • 读 Query

2.4.2. User 领域

java 复制代码
domain/user
├─ service
│   ├─ UserService.java
│   └─ UserServiceImpl.java
├─ model
│   └─ User.java
└─ validator
    └─ UserValidator.java

2.4.3. Stock 领域

java 复制代码
domain/stock
├─ service
│   ├─ StockService.java
│   └─ StockServiceImpl.java
├─ model
│   └─ Stock.java
└─ policy
    └─ StockDeductPolicy.java

2.5. Query 层(只读模型,JOIN 全在这里)

这是架构里最关键的一层

java 复制代码
query
├─ order
│   ├─ OrderOnlyQuery.java
│   ├─ OrderOnlyQueryImpl.java
│   └─ dto
│       └─ OrderView.java
├─ orderlog
│   ├─ OrderLogOnlyQuery.java
│   └─ OrderLogOnlyQueryImpl.java
├─ user
│   ├─ UserOnlyQuery.java
│   └─ UserOnlyQueryImpl.java
└─ statistics
    ├─ OrderStatisticsQuery.java
    └─ StatisticsView.java

规则

  • ✅ 允许 JOIN
  • ✅ 允许跨主体
  • ❌ 禁止写操作
  • ❌ 禁止业务判断

2.6. Statistics 层(横向统计)

java 复制代码
statistics
├─ StatisticsService.java
└─ StatisticsServiceImpl.java

依赖方向

java 复制代码
StatisticsService
   ↓
Query(多个)

❌ 不允许:

  • 直接访问 Dao
  • 调用 Domain Service

2.7. Infrastructure 层(Dao / Mapper)

java 复制代码
infrastructure
├─ dao
│   ├─ order
│   │   ├─ OrderDao.java
│   │   └─ OrderLogDao.java
│   ├─ user
│   │   └─ UserDao.java
│   └─ stock
│       └─ StockDao.java
├─ mapper
│   └─ *.xml
├─ cache
│   └─ RedisClient.java
└─ mq
    └─ MessageProducer.java

规则

  • Dao = 单表
  • ❌ 不写 JOIN
  • ❌ 不写业务逻辑

2.8. Common 层(通用能力)

java 复制代码
common
├─ exception
│   └─ BizException.java
├─ util
│   └─ IdGenerator.java
├─ constant
│   └─ ErrorCode.java
└─ base
    └─ BaseEntity.java

2.9. 可以直接用的「包级约束口诀」

可以在团队里这样要求:

  • domain 不准依赖 application
  • query 不准依赖 domain.service
  • controller 不准依赖 dao
  • statistics 只准依赖 query

2.10. Code Review 一眼判断法(非常好用)

看到一个类,只看路径就能判断对不对:

  • 📂 domain/order/service/OrderService
    只能有订单逻辑
  • 📂 query/order/OrderOnlyQuery
    一定是 JOIN + 只读
  • 📂 application/orderstock/OrderStockFacade
    一定是跨域流程
  • 📂 statistics/StatisticsService
    一定不写业务

这套包结构 不是最简单的 ,但它是复杂业务下最不容易"写歪"的结构之一

3. 多模块项目

Gradle(尤其是多模块) 做这个分层非常顺手。下面是一套可直接照抄、跑得起来的模板:

  • webopen 都能访问数据库
  • Entity / Mapper 接口 / mapper.xml 统一复用
  • 不放 common ,而是放到 persistence 模块(共享数据访问层)
  • Spring Boot + MyBatis

3.1. 推荐的 Gradle 多模块目录结构

java 复制代码
project-root
├─ settings.gradle
├─ build.gradle
├─ common
│  └─ build.gradle
├─ persistence
│  ├─ build.gradle
│  └─ src/main
│     ├─ java/com/xxx/persistence
│     │  ├─ entity
│     │  ├─ mapper
│     │  └─ config
│     └─ resources
│        └─ mapper
│           ├─ UserMapper.xml
│           └─ OrderMapper.xml
├─ web
│  └─ build.gradle
├─ open
│  └─ build.gradle
└─ bootstrap
   └─ build.gradle

bootstrap 是启动模块(可选,但强烈推荐),避免 web/open 同时作为启动入口导致配置乱。

3.2. settings.gradle(必须)

java 复制代码
rootProject.name = "demo-project"

include(
        "common",
        "persistence",
        "web",
        "open",
        "bootstrap"
)

3.3. 根 build.gradle(统一版本、插件管理)

java 复制代码
plugins {
    id 'org.springframework.boot' version '3.2.3' apply false
    id 'io.spring.dependency-management' version '1.1.4' apply false
    id 'java' apply false
}

allprojects {
    group = 'com.xxx'
    version = '1.0.0'
}

subprojects {
    apply plugin: 'java'
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(17)
        }
    }

    repositories {
        mavenCentral()
    }
}

3.4. common/build.gradle(纯通用,无数据库依赖)

java 复制代码
dependencies {
    // 放工具类、异常、返回结构等
    implementation "org.apache.commons:commons-lang3:3.14.0"
}

common 模块是通用模块。主要放一些其它各模块公用的一些代码:

✅ common 保持"干净",放:

  • BaseResponse / PageRequest / PageResult
  • BizException / ErrorCode(基础类)
  • 工具类 util
  • 公共注解
  • 通用枚举基类 BaseEnum
  • ID生成器
  • 常量 GlobalConstants

❌ 不放:

  • UserDO / OrderDO
  • UserMapper
  • mapper.xml
  • 数据库依赖与配置

3.5. persistence/build.gradle(共享数据库访问层)

这里放:DO/Entity、Mapper接口、mapper.xml

让 web/open 都 implementation(project(":persistence"))

java 复制代码
plugins {
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(":common")

    // MyBatis Spring Boot Starter
    implementation "org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3"

    // 数据库驱动(你用什么换什么,这里以 MySQL 为例)
    runtimeOnly "com.mysql:mysql-connector-j:8.3.0"

    // 可选:Lombok
    compileOnly "org.projectlombok:lombok:1.18.30"
    annotationProcessor "org.projectlombok:lombok:1.18.30"
}

tasks.withType(Test).configureEach {
    useJUnitPlatform()
}

3.5.1. persistence 模块里 MyBatis 扫描与 XML 路径(非常关键)

mapper.xml 放的位置(推荐)

java 复制代码
persistence/src/main/resources/mapper/*.xml

persistence/config/MybatisConfig.java(可选,但建议)

java 复制代码
package com.xxx.persistence.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.xxx.persistence.mapper")
public class MybatisConfig {
}

这样就不怕扫描不到 Mapper。
web/open/bootstrap 引入 persistence 后会自动生效。

3.5.2. persistence 模块

persistence 是持久层模块。persistence 模块内容建议:

java 复制代码
persistence
├─ entity                 // DB 实体(DO / PO)
│  ├─ UserDO.java
│  └─ OrderDO.java
├─ mapper                 // MyBatis Mapper 接口(Dao)
│  ├─ UserMapper.java
│  └─ OrderMapper.java
├─ xml                    // mapper.xml(或 resources/mapper)
│  ├─ UserMapper.xml
│  └─ OrderMapper.xml
└─ config                 // MyBatis 配置 / 扫描配置(可选)

注意:这里的实体最好叫 DO/PO,不要叫 Domain Entity(避免把领域模型和表强绑定)。

Entity / Dao / XML 统一放到 persistence 模块:

示例:

java 复制代码
persistence/src/main/java/com/xxx/persistence/entity/UserDO.java
persistence/src/main/java/com/xxx/persistence/mapper/UserMapper.java
persistence/src/main/resources/mapper/UserMapper.xml

web/open 直接注入 Mapper 使用:

java 复制代码
@Autowired
private UserMapper userMapper;

可能会担心的问题:web/open 同时访问 DB,会不会乱?

不会,只要你遵守:

✅ DB 相关东西只在 persistence

✅ 启动配置统一放 bootstrap

✅ web/open 不重复配置数据源

3.6. web/build.gradle(依赖 persistence)

这里建议 web 不直接引数据库驱动、mybatis 依赖,都从 persistence 来。

java 复制代码
plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(":common")
    implementation project(":persistence")

    implementation "org.springframework.boot:spring-boot-starter-web"
    implementation "org.springframework.boot:spring-boot-starter-validation"
}

3.7. open/build.gradle(依赖 persistence)

java 复制代码
plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(":common")
    implementation project(":persistence")

    implementation "org.springframework.boot:spring-boot-starter-web"
}

3.8. bootstrap 模块的启动类(Spring Boot main)

java 复制代码
package com.xxx.bootstrap;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.xxx")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

scanBasePackages="com.xxx" 确保扫描到 persistence/web/open 的 bean。

3.8.1. bootstrap/build.gradle(推荐:唯一启动模块)

让 bootstrap 作为唯一入口,web/open 只是功能模块。

java 复制代码
plugins {
    id 'org.springframework.boot'
    id 'io.spring.dependency-management'
}

dependencies {
    implementation project(":common")
    implementation project(":persistence")
    implementation project(":web")
    implementation project(":open")

    implementation "org.springframework.boot:spring-boot-starter"
}

3.8.2. application.yml(放 bootstrap 模块)

bootstrap/src/main/resources/application.yml

java 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
    username: root
    password: root

mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.xxx.persistence.entity

classpath*: 非常重要,保证多模块 resources 能被找到

✅ mapper-locations 匹配 persistence/resources/mapper

3.8.3. 如果不设 bootstrap

如果不设 bootstrap,而是 webopen 各自启动:

  • web/open 都要有自己的启动类 @SpringBootApplication(scanBasePackages="com.xxx")
  • application.yml 放各自模块里

缺点:配置容易重复;优点:模块独立运行。

相关推荐
糯诺诺米团14 小时前
C++多线程打包成so给JAVA后端(Ubuntu)<2>
java·开发语言·c++
南屿欣风14 小时前
Maven 聚合工程打包报错:Unable to find main class 快速解决
java·maven
洛_尘14 小时前
Java EE进阶7:Spring Boot 日志
java·spring boot·java-ee
耘田14 小时前
 macOS Launch Agent 定时任务实践指南
java·开发语言·macos
_loehuang_14 小时前
Docker Compose 部署 Maven 私有库 nexus3
java·docker·maven·nexus·maven私有库
汪小成1 天前
Go 项目结构总是写乱?这个 50 行代码的 Demo 教你标准姿势
后端·go
Piper蛋窝1 天前
AI 有你想不到,也它有做不到 | 2025 年深度使用 Cursor/Trae/CodeX 所得十条经验
前端·后端·代码规范
韩立学长1 天前
【开题答辩实录分享】以《在线作业标准流程指导系统的设计与实现》为例进行选题答辩实录分享
java·javascript
一直都在5721 天前
Spring框架:AOP
java·后端·spring