第三课:后端工程结构怎么设计?——从“能跑”到“能活三年”
《后端工程结构决定系统寿命》摘要:优秀的后端工程结构是系统长期健康运行的关键。文章指出,大多数项目失败源于结构混乱导致的模块耦合、改动恐惧等问题。作者提出"五层架构"解决方案:接口适配层、业务编排层、业务核心层、技术实现层和通用规范层,强调每层应严格遵循单一职责原则。特别区分了DTO、Entity和Domain三种模型,防止数据与业务逻辑混杂。文章还详细阐述了HTTP、RPC、
很多项目能跑,但活不久。
很多系统上线很快,但三个月后就开始痛苦。
真正拉开差距的,不是技术选型,而是工程结构。
如果你学后端只是为了“写接口”,那结构不重要。
但如果你的目标是“构建系统”,那结构就是一切。
这一课讲的是:
👉 一个后端工程,第一天结构不对,后面全是灾难。
一、为什么工程结构决定系统寿命
大多数后端项目,不是死于并发,不是死于性能,而是死于:
- 业务膨胀
- 模块互咬
- 改动恐惧
- 新人不敢动
- 重构成本极高
这些问题,几乎都指向一个根因:
❌ 结构承载不了复杂度
工程结构的唯一使命:
👉 让复杂度有秩序,而不是扩散。
二、从“写接口”到“建系统”:结构设计的核心问题
一个真正的后端系统,第一天必须回答清楚四个问题:
- 谁负责对外沟通?
- 谁负责业务规则?
- 谁负责数据世界?
- 谁负责系统能力?
如果这些边界模糊,项目一定会变成:
Controller 写业务
Service 写 SQL
Entity 当返回对象
工具类到处飞
👉 结果一定是系统不可控。
三、推荐工程结构(架构型后端起手式)
不是“三层”,而是“五层结构”:
com.xxx.project
├─ interfaces / controller 接口适配层
├─ application / service 业务编排层
├─ domain 业务核心层 ⭐
├─ infrastructure 技术实现层
│ ├─ persistence 数据库实现
│ ├─ cache Redis
│ ├─ mq 消息
│ └─ external 三方系统
└─ common 通用规范/工具
这是架构分层,不是文件分类。
四、每一层“只干一类事”(这是工程纪律)
✅ 1. interfaces / controller —— 协议适配层
只负责:
-
HTTP 参数接收
-
参数校验
-
DTO 转换
-
返回结构
禁止:
-
业务规则
-
SQL
-
状态机
-
复杂逻辑
它解决的是:
👉 外部世界如何进入系统
类比:
Android Activity / Flutter Widget
✅ 2. application / service —— 业务编排层
负责:
- 业务流程组织
- 权限控制
- 事务边界
- 多 Domain 协作
不关心:
- HTTP
- ORM
- 框架细节
它解决的是:
👉 一次业务“怎么完成”
✅ 3. domain —— 业务核心层(系统灵魂)
这里放:
- 业务对象
- 业务规则
- 状态变化
- 行为封装
示例:
order.cancel();
user.upgradeVip();
account.freeze();
禁止出现:
- SQL
- HTTP
- DTO
- 框架注解
👉 Domain 表达的是业务语义,不是数据结构。
有没有这一层,决定你是:
❌ CRUD 工程师
✅ 架构型后端
✅ 4. infrastructure —— 技术实现层
这里放:
- 数据库实现
- Redis 实现
- MQ 实现
- 三方 SDK
它的职责:
👉 给业务层提供“技术能力”
它可以变,但不应该撼动业务结构。
✅ 5. common —— 系统公约层
- 统一返回
- 异常体系
- 日志规范
- 工具能力
👉 这是系统的“宪法层”。
五、DTO / Entity / Domain 怎么拆(90% 混乱源头)
❌ 灾难模型
一个 User 同时是:
- 数据库表
- 接口返回
- 业务对象
结果一定是:全系统强耦合。
✅ 正确拆法:三个世界,三套模型
1️⃣ DTO(接口世界)
DTO = Data Transfer Object
👉 中文:数据传输对象
它的本意非常直白:
专门用来“在边界之间传输数据的对象”。
UserCreateRequest
UserResponse
特点:
- 为接口而生
- 快速变化
- 表达协议
2️⃣ Entity / PO(数据世界)
UserEntity
特点:
- ORM 映射
- 对应表结构
- 技术对象
3️⃣ Domain(业务世界)
User
特点:
- 有行为
- 有规则
- 有状态约束
👉 三个世界,绝不混用。
六、一个“下单业务”的标准工程落位
Controller
→ OrderCreateRequest DTO
→ OrderApplicationService
→ OrderFactory.create()
→ order.submit()
→ stock.freeze()
→ orderRepository.save(order)
你会发现:
- Controller 不懂业务
- Repository 不懂流程
- Domain 不懂 HTTP
👉 每一层都“很傻”,系统才会“很稳”。
七、为什么这种结构能“活三年”
因为它天然支持:
✔ 业务膨胀
✔ 模块拆分
✔ 技术替换
✔ 多端接入
✔ 微服务演进
当你要:
- 加 Redis
- 拆服务
- 换 ORM
- 接新客户端
你会发现:
👉 大部分修改,不会触碰 Domain。
================================================
补充:为什么要把 interfaces 拆成 http / rpc / mq / job / webhook / admin?
很多同学看到分层结构里出现 interfaces 会误解成“Java interface 的地方”。
这里的 interfaces 不是语法关键字,而是架构概念——系统对外的入口层(Inbound Adapters / Interface Adapters)。
一句话定义:
interfaces= 外部世界进入系统的所有触发点
(HTTP、RPC、消息、定时任务、三方回调、管理后台……)
当你的系统不止有 HTTP 接口时,这一层就必须拆细,否则会出现典型混乱:
- MQ 消费逻辑写进 Service
- Job 直接操作 Repository
- Webhook 回调与普通 API 混在一起
- Admin 接口和用户接口混权限、混语义
所以我推荐把 interfaces 拆成下面这套结构:
interfaces
├─ http
│ ├─ controller
│ ├─ dto
│ └─ assembler
├─ rpc
├─ mq
├─ job
├─ webhook
└─ admin
这套结构的核心价值:按“触发源”划分入口边界。
1)interfaces/http:普通 HTTP API 入口
适用场景:App/Web/小程序调用的 REST API。
建议再细分三个子包:
controller:只处理协议(路由、参数、校验、返回)dto:请求/响应模型(Request/Response)assembler:DTO ↔ Command/Domain 的转换器(隔离协议世界与业务世界)
✅ 命名规范
- Controller:
OrderController - Request:
CreateOrderRequest - Response:
OrderResponse - Assembler:
OrderAssembler(或OrderConverter)
✅ 核心纪律
- Controller 禁止写业务规则
- DTO 禁止进入 domain / application
- Assembler 负责“翻译”,不是写业务
2)interfaces/rpc:服务间调用入口(Dubbo/gRPC/Feign)
适用场景:系统被其他系统调用(内部服务间)。
为什么单独分一层?
- RPC 的协议、鉴权、幂等、版本管理,和 HTTP 不一样
- RPC 是内部合同,稳定性要求更高
✅ 命名规范
OrderRpcService- 若区分读写:
OrderQueryRpcService/OrderCommandRpcService
核心原则仍然一样:RPC 层只做入站适配,调用 application。
3)interfaces/mq:消息消费入口(Kafka/RabbitMQ/RocketMQ)
适用场景:事件驱动架构(支付成功、库存冻结、用户注册等事件)。
MQ 消费本质是“外部事件触发系统”,因此必须属于 interfaces。
✅ 命名规范(推荐 Consumer)
OrderPaidConsumerUserCreatedConsumerInventoryReservedConsumer
✅ 核心纪律
- Consumer 只负责:反序列化/校验/幂等入口处理 → 调 application
- 禁止:Consumer 里写业务流程、写 SQL、写复杂判断
4)interfaces/job:定时任务入口(xxl-job/quartz/schedule)
适用场景:超时关单、对账、定时同步、日报生成等。
Job 是“时间触发系统”,依然是入口,不属于业务层。
✅ 命名规范
OrderTimeoutJobDailyReportJobSyncCatalogJob
✅ 核心纪律
- Job 不直接操作 Repository / DB
- Job 只触发 application 的用例流程
5)interfaces/webhook:三方回调入口(支付/物流/开放平台)
适用场景:微信支付回调、Stripe webhook、物流状态回调、OAuth 回调等。
为什么不放在 http?
因为 webhook 的语义更像“外部系统事件”,它往往有:
- 签名验签
- 重试/幂等
- 特殊返回格式约束
- 安全策略与普通 API 不同
✅ 命名规范
WechatPayWebhookControllerStripeWebhookController
核心原则:验签/解析/去重 → 调 application。
6)interfaces/admin:后台/运营入口(内部人员使用)
适用场景:运营后台、管理后台、内部管理接口。
为什么要单独分?
- 权限模型不同(admin 往往更高权限)
- 返回字段不同(更详细、更偏运维/管理)
- 防止“误用/误暴露”接口
✅ 命名规范
AdminOrderControllerAdminUserController
强烈建议 admin 的路由前缀统一:
/admin/**
7)一张“入口层的统一原则”总结
无论是 HTTP / RPC / MQ / Job / Webhook / Admin,它们都必须遵守同一条架构约束:
✅ interfaces 只做入站适配,不做业务
标准调用路径必须是:
interfaces → application → domain → infrastructure
interfaces 层负责:
- 接收输入(协议适配)
- 校验(格式与基本合法性)
- 组装/转换(DTO ↔ Command/Domain)
- 调用 application 用例
真正的业务规则必须沉到:
- application(流程编排、事务边界)
- domain(业务规则、状态变化)
更多推荐


所有评论(0)