企业级Java架构分层实践
作者: shura | 日期: 2025-01-14
说明
本文介绍的是4层架构+充血模型的分层方案,不是传统三层,也不是标准DDD。
国内使用这种方案的公司约10-15%,大多数公司(85%)还在用传统的贫血模型+Service。
适合场景: 有复杂业务规则、Service已经臃肿(>500行)、需要长期维护的项目
不适合场景: 纯CRUD、快速开发、小团队(<3人)
本文作为一种思路参考,不是唯一方案。
问题
传统三层架构在实际项目中的问题:
Controller → Service → Mapper
Service层成为"垃圾桶",一个方法做所有事情:
public void createOrder(Long userId, Long productId, int quantity) {
// 1. 查用户
User user = userMapper.selectById(userId);
if (user.getStatus() != 1) throw...
// 2. 查商品
Product product = productMapper.selectById(productId);
if (product.getStock() < quantity) throw...
// 3. 计算价格(业务规则散落在这里)
BigDecimal price = product.getPrice() * quantity;
if ("VIP".equals(user.getLevel())) {
price = price * 0.9; // VIP打9折
}
if (price > 100) {
price = price - 10; // 满100减10
}
// 4. 扣库存
product.setStock(product.getStock() - quantity);
productMapper.update(product);
// 5. 创建订单
orderMapper.insert(order);
// 800行代码...
}
问题:
- 职责不清
- 业务规则散落
- 难以测试
- 难以复用
方案
4层架构:
Controller → 接收请求,调用Service
Service → 编排流程,管理事务
Domain → 业务规则,业务计算
Repository → 数据库操作
包结构:
src/main/java/com/company/order/
├── controller/
├── service/
├── domain/
│ ├── model/
│ └── service/
└── infrastructure/
├── repository/
└── mapper/
各层职责:
| 层 | 职责 | 禁止 |
|---|---|---|
| Controller | 接收请求,调用Service | 写业务逻辑 |
| Service | 编排流程,管理事务 | 写业务规则 |
| Domain | 业务规则,业务计算 | 依赖框架 |
| Infrastructure | 数据库,外部调用 | 业务判断 |
实现
Controller层
只做3件事:接收请求、调用Service、返回结果
@PostMapping("/orders")
public Result create(@RequestBody OrderRequest request) {
OrderDTO dto = orderService.createOrder(
request.getUserId(),
request.getProductId(),
request.getQuantity()
);
return Result.success(dto);
}
Service层
只编排流程,不写业务规则
@Transactional
public OrderDTO createOrder(Long userId, Long productId, int quantity) {
// 1. 查数据
User user = userRepository.getById(userId);
Product product = productRepository.getById(productId);
// 2. 调用Domain层处理业务
Order order = orderDomainService.createOrder(user, product, quantity);
// 3. 保存
orderRepository.save(order);
productRepository.save(product);
// 4. 返回
return toDTO(order);
}
Domain层 - 充血模型
业务方法写在实体内部:
public class User {
private String level;
private Integer status;
// 业务方法
public void checkCanOrder() {
if (this.status != 1) {
throw new BizException("用户状态异常");
}
}
public BigDecimal getDiscount() {
return "VIP".equals(level) ? 0.9 : 1.0;
}
}
public class Product {
private Integer stock;
// 业务方法
public void checkStock(int quantity) {
if (this.stock < quantity) {
throw new BizException("库存不足");
}
}
public void decreaseStock(int quantity) {
checkStock(quantity);
this.stock -= quantity;
}
}
Domain层 - 领域服务
跨对象的业务逻辑:
public class OrderDomainService {
public Order createOrder(User user, Product product, int quantity) {
// 业务规则1: 检查用户
user.checkCanOrder();
// 业务规则2: 检查库存
product.checkStock(quantity);
// 业务规则3: 计算价格
BigDecimal amount = calculateAmount(product, quantity, user);
// 业务规则4: 扣库存
product.decreaseStock(quantity);
// 业务规则5: 创建订单
Order order = new Order();
order.setAmount(amount);
order.setStatus(PENDING);
return order;
}
private BigDecimal calculateAmount(Product product, int quantity, User user) {
BigDecimal base = product.getPrice() * quantity;
BigDecimal afterDiscount = base * user.getDiscount(); // VIP折扣
return afterDiscount > 100 ? afterDiscount - 10 : afterDiscount; // 满减
}
}
Infrastructure层
隔离数据库实现:
public class OrderRepositoryImpl implements OrderRepository {
@Autowired
private OrderMapper mapper;
public void save(Order order) {
OrderPO po = toPO(order); // Domain对象 -> PO
mapper.insert(po);
}
public Order getById(Long id) {
OrderPO po = mapper.selectById(id);
return toDomain(po); // PO -> Domain对象
}
}
对比
代码行数:
传统写法:
├─ OrderService.java 800行 ← 全在这里
└─ Order.java 50行 ← 只有getter/setter
分层写法:
├─ OrderController 20行 ← 只接收请求
├─ OrderService 30行 ← 只编排流程
├─ OrderDomainService 80行 ← 核心业务
├─ Order 40行 ← 有业务方法
├─ User 30行 ← 有业务方法
└─ Product 30行 ← 有业务方法
修改业务规则:
场景: VIP折扣改为8.5折
传统写法:
└─ 在800行Service里找到这一行改
(担心改错,不敢动)
分层写法:
└─ 在User.getDiscount()里改
return "VIP".equals(level) ? 0.85 : 1.0;
(只改一处,清晰明确)
测试:
// 传统写法: 需要Mock数据库
@Test
public void test() {
// Mock: userMapper, productMapper, orderMapper
// 准备: 数据库数据
}
// 分层写法: 不需要数据库
@Test
public void test() {
User user = new User();
user.setLevel("VIP");
Product product = new Product();
product.setPrice(100);
product.setStock(10);
Order order = domainService.createOrder(user, product, 2);
assertEquals(170, order.getAmount()); // 200 * 0.9 - 10
assertEquals(8, product.getStock());
}
原则
-
各司其职 - Controller传话、Service编排、Domain干活、Repository搬砖
-
业务在Domain - 业务判断和计算不写在Service里
-
充血模型 - Entity有业务方法,不是只有getter/setter
示例:
// 错误: 业务判断在Service
if ("VIP".equals(user.getLevel())) {
price = price * 0.9;
}
// 正确: 业务判断在User
BigDecimal discount = user.getDiscount();
price = price * discount;
何时使用
判断信号:
需要分层:
- Service超过200行
- 业务规则散落各处
- if/else嵌套超过3层
- 同样逻辑复制多处
- 测试很难写
不需要分层:
- 纯CRUD,没有业务规则
- 团队只有2-3人
- 项目周期<3个月
项目类型:
简单CRUD
└─ 传统三层即可
Controller → Service → Mapper
有业务规则
└─ 使用4层架构
Controller → Service → Domain → Infrastructure
复杂业务系统
└─ 完整DDD
聚合根、值对象、限界上下文...
落地
新项目:
第1天: 建包结构
├── controller/
├── service/
├── domain/
│ ├── model/
│ └── service/
└── infrastructure/
第2天: 写第一个功能
├── 先写Domain模型和DomainService
├── 再写Service编排
└── 最后写Controller接口
老项目:
第1周: 抽取Domain模型
└─ User/Product/Order从贫血改为充血
第2周: 抽取DomainService
└─ 把业务规则从Service抽到DomainService
第3周: 加Repository
└─ 隔离Mapper,Domain不直接依赖数据库
第4周: 瘦身Service
└─ Service只保留编排代码
Code Review:
不通过:
- Service写了业务判断
- Controller直接调Mapper
- Domain对象只有getter/setter
通过:
- Service只有编排代码
- 业务规则在Domain层
- Domain对象有业务方法
疑问
Q: 转换代码太多?
会有一些转换:
Request → DTO → Domain → PO → Domain → DTO → Response
但这是值得的:每层独立变化、业务不暴露技术细节、方便测试
可以用MapStruct自动生成转换代码。
Q: 小项目有必要吗?
看情况:
| 项目类型 | 建议 |
|---|---|
| 纯CRUD | 传统三层 |
| 有业务规则 | 建议分层 |
| 复杂业务 | 必须分层 |
经验:
- Service < 200行 → 不用分层
- Service > 200行 → 开始分层
- Service > 500行 → 必须分层
Q: Repository和Mapper区别?
Mapper (技术概念):
└─ 直接操作数据库
└─ 返回PO对象
Repository (业务概念):
└─ 领域对象集合
└─ 返回Domain对象
└─ 接口在Domain层,实现在Infrastructure层
总结
核心:
- Service只编排,不写业务
- 业务规则在Domain层
- Domain对象是充血的
- Repository隔离数据库
价值:
- 职责清晰
- 易于测试
- 易于维护
- 逻辑可复用
再次说明:
这不是唯一方案。国内大多数公司用的还是传统三层。
本文的方案适合复杂业务、长期维护、Service臃肿的场景。
简单CRUD、快速开发、小团队不适合。
选择架构要务实。
参考:
- 《领域驱动设计》 - Eric Evans
- 《实现领域驱动设计》 - Vaughn Vernon
- 《架构整洁之道》 - Robert C. Martin
欢迎关注,学习不迷路!