# 后端分层架构:Controller / Service / Repository / Domain
> 💡 **学习指南**:分层架构就像组织一家餐厅——每个人都有明确的职责,前厅接待(Controller)、厨师做菜(Service)、仓管取货(Repository)、菜谱标准(Domain)。本文将带你从零理解后端代码的"层"到底是怎么回事,以及为什么要这样分层。
在开始之前,建议你先有简单的后端开发经验,至少写过几个接口,踩过一些坑。
---
## 0. 引言:为什么代码越写越乱?
很多初学者在刚开始写后端代码时,都会遇到这样的困惑:
- **刚开始**:写一个用户注册接口,100 行代码搞定,感觉挺简单
- **三个月后**:业务越来越复杂,一个文件 500 行,改一行代码怕影响其他地方
- **半年后**:来了新同事,看着代码发愁:"这个接口到底干了多少事?"
**问题的本质**:代码没有"章法",所有的逻辑都堆在一起,就像把食材、厨具、调料都扔在一个抽屉里。
### 分层的思想:把抽屉换成橱柜
想象一下厨房的组织方式:
| 区域 | 存放物品 | 特点 |
|------|---------|------|
| **吊柜** | 不常用的锅具、囤货 | 取用最不方便 |
| **台面** | 正在处理的食材 | 临时操作区 |
| **抽屉** | 分类摆放的餐具 | 按需取用 |
| **冰箱** | 生鲜食材 | 有保鲜条件 |
**分层架构**就是把代码也这样组织:每一层只关心自己的职责,层与层之间通过明确的"接口"交互,而不是随意互相调用。
---
## 1. 核心概念:四层架构的职责划分
### 1.1 四层架构概览
典型的后端分层架构包含四个核心层次:
```
┌─────────────────────────────────────┐
│ Controller 层(控制器层) │ ← 接待员:接收请求,初步检查
│ - 接收 HTTP 请求 │
│ - 参数校验 │
│ - 调用 Service │
│ - 返回响应 │
├─────────────────────────────────────┤
│ Service 层(业务逻辑层) │ ← 厨师:处理核心业务
│ - 业务逻辑编排 │
│ - 事务管理 │
│ - 调用 Repository │
│ - 跨模块协调 │
├─────────────────────────────────────┤
│ Repository 层(数据访问层) │ ← 仓管员:管理数据存取
│ - 数据库操作 │
│ - ORM 映射 │
│ - 查询封装 │
├─────────────────────────────────────┤
│ Domain 层(领域模型层) │ ← 菜谱标准:定义业务概念
│ - 实体(Entity) │
│ - 值对象(Value Object) │
│ - 业务规则 │
└─────────────────────────────────────┘
```
### 1.2 Controller 层:请求的"接待员"
**职责**:
- 接收 HTTP 请求,解析参数
- 进行基础的参数校验(格式、必填等)
- 调用 Service 层执行业务逻辑
- 封装响应,返回给客户端
**不该做的事**:
- 不要在这里写业务逻辑
- 不要直接操作数据库
- 不要处理事务
**类比**:就像餐厅的门童,负责迎接客人、检查预约、引导入座,但不负责做菜。
### 1.3 Service 层:业务逻辑的"厨师"
**职责**:
- 实现核心业务逻辑
- 编排多个 Repository 的操作
- 管理事务边界(@Transactional)
- 处理跨模块的业务协调
**不该做的事**:
- 不要直接写 SQL(交给 Repository)
- 不要处理 HTTP 相关的事情
- 不要返回数据库实体给 Controller
**类比**:就像厨师按照菜谱做菜,需要协调各种食材(数据),把控菜品质量(业务正确性)。
### 1.4 Repository 层:数据的"仓管员"
**职责**:
- 封装所有数据访问逻辑
- 执行 CRUD 操作
- 处理 ORM 映射
- 封装查询条件
**不该做的事**:
- 不要写业务逻辑
- 不要处理事务(Service 层管理)
- 不要依赖上层模块
**类比**:就像餐厅的仓管员,负责从仓库取食材、存放剩余食材。厨师只需要告诉仓管员要什么,不需要知道仓库在哪、怎么取。
### 1.5 Domain 层:领域模型的"蓝图"
**职责**:
- 定义业务实体(Entity)
- 定义值对象(Value Object)
- 封装业务规则
- 作为所有层的共同依赖
**重要特性**:
- Domain 层不依赖任何其他层
- 所有层都依赖 Domain 层
- 是分层架构的基础
**类比**:就像餐厅的菜单和菜品标准,定义了什么是"宫保鸡丁"、用什么食材、什么口味。所有厨师都要按照这个标准来做。
---
## 2. DTO:层与层之间的"翻译官"
### 2.1 为什么需要 DTO?
想象一下:如果 Controller 直接把数据库实体(Entity)返回给前端,会发生什么?
```java
// ❌ 错误的做法
@Entity
public class User {
@Id
private Long id;
private String username;
private String password; // 敏感信息!
private String phone;
private String email;
private LocalDateTime createdAt;
private Boolean isDeleted; // 内部字段!
}
// 如果直接返回这个实体...
// 前端会收到 password、isDeleted 等不应该暴露的字段
```
**DTO 的作用**:
- **解耦**:隔离数据库实体和 API 契约
- **安全**:控制暴露的字段,避免泄露敏感信息
- **灵活**:可以为不同场景定义不同的 DTO
- **性能**:避免加载不必要的数据
### 2.2 不同层的 DTO 职责
| 层级 | DTO 类型 | 职责 | 示例 |
|------|---------|------|------|
| **Controller** | Request / Response DTO | 定义 API 契约、参数校验、序列化 | `UserCreateRequest` |
| **Service** | Param / Result DTO | 封装业务方法参数,解耦 Controller 与 Service | `UserCreateParam` |
| **Repository** | Entity / DO | 映射数据库表结构,ORM 映射 | `UserEntity` |
### 2.3 DTO 转换实战
```java
// ========== Controller 层:Request DTO ==========
@Data
public class UserCreateRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度3-20")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码至少6位")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
}
// ========== Controller 层:Response DTO ==========
@Data
@Builder
public class UserDTO {
private Long id;
private String username;
private String email;
private LocalDateTime createdAt;
// ❌ 注意:不包含 password 字段!
}
// ========== Service 层:Param DTO ==========
@Data
@Builder
public class UserCreateParam {
private String username;
private String password; // 已加密的密码
private String email;
}
// ========== 转换逻辑 ==========
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity createUser(
@RequestBody @Valid UserCreateRequest request) {
// 1. Request DTO -> Param DTO
UserCreateParam param = UserCreateParam.builder()
.username(request.getUsername())
.password(encryptPassword(request.getPassword()))
.email(request.getEmail())
.build();
// 2. 调用 Service
User user = userService.createUser(param);
// 3. Entity -> Response DTO
UserDTO response = UserDTO.builder()
.id(user.getId())
.username(user.getUsername())
.email(user.getEmail())
.createdAt(user.getCreatedAt())
.build();
return ResponseEntity.ok(response);
}
}
```
---
## 3. 依赖方向:分层架构的铁律
### 3.1 依赖倒置原则(DIP)
分层架构的核心规则:**上层模块不应该依赖下层模块的具体实现,而应该依赖于抽象。**
```
❌ 错误的依赖方式(直接依赖实现):
Controller -> UserServiceImpl -> UserDaoImpl -> UserEntity
问题:
1. 每层都耦合了具体实现
2. 换实现要改很多代码
3. 测试困难
✅ 正确的依赖方式(依赖抽象):
Controller -> IUserService (接口) -> IUserDao (接口) -> UserEntity
实现:
UserServiceImpl -> UserDaoImpl
好处:
1. 上层只依赖接口
2. 换实现只需改配置
3. 容易 Mock 测试
```
### 3.2 正确的依赖方向
```
┌─────────────────────────────────────────────────────────────┐
│ Controller 层 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ UserController │ │
│ │ - @Autowired private IUserService userService; │ │
│ │ ✅ 依赖接口,不依赖实现 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ 依赖(Dependency) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Service 层 │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ UserServiceImpl │ │ │
│ │ │ - @Autowired private UserRepository repository; │ │ │
│ │ │ ✅ 依赖 Repository 接口 │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ 依赖 │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ Repository 层 │ │ │
│ │ │ ┌──────────────────────────────────────────┐ │ │ │
│ │ │ │ UserRepository │ │ │ │
│ │ │ │ - extends JpaRepository │ │ │ │
│ │ │ └──────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │ │
│ │ │ ▼ 依赖 │ │ │
│ │ │ ┌──────────────────────────────────────────┐ │ │ │
│ │ │ │ Domain 层 (核心领域) │ │ │ │
│ │ │ │ ┌────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ User (Entity) │ │ │ │ │
│ │ │ │ │ - 不包含任何层依赖 │ │ │ │ │
│ │ │ │ │ - 被所有层依赖 │ │ │ │ │
│ │ │ │ └────────────────────────────────────┘ │ │ │ │
│ │ │ └──────────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 4. 实战案例:电商订单系统的分层实现
### 4.1 需求场景
实现一个电商订单创建功能:
- 用户选择商品,确认订单信息
- 系统检查库存
- 计算订单金额(商品价格 + 运费 - 优惠)
- 创建订单记录
- 扣减库存
- 返回订单信息
### 4.2 完整的分层代码
```java
// =====================================================
// 1. Domain 层:领域模型
// =====================================================
// 订单实体
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_id")
private Long userId;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "order_id")
private List items = new ArrayList<>();
@Embedded
private Money totalAmount;
@Embedded
private Address shippingAddress;
@Enumerated(EnumType.STRING)
private OrderStatus status = OrderStatus.PENDING_PAYMENT;
@Column(name = "created_at")
private LocalDateTime createdAt = LocalDateTime.now();
// 业务方法:计算订单总金额
public void calculateTotal() {
Money total = Money.zero();
for (OrderItem item : items) {
total = total.add(item.getSubTotal());
}
this.totalAmount = total;
}
// 业务方法:添加订单项
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
// 业务方法:取消订单
public void cancel() {
if (this.status != OrderStatus.PENDING_PAYMENT) {
throw new IllegalStateException("只有待支付订单可以取消");
}
this.status = OrderStatus.CANCELLED;
}
// Getters...
}
// 订单项实体
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_id")
private Long productId;
@Column(name = "product_name")
private String productName;
@Embedded
private Money unitPrice;
@Column(name = "quantity")
private Integer quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
// 计算小计
public Money getSubTotal() {
return unitPrice.multiply(quantity);
}
// Getters and Setters...
}
// 值对象:金钱
@Embeddable
public class Money {
@Column(name = "amount")
private BigDecimal amount;
@Column(name = "currency")
private String currency;
public static Money zero() {
return new Money(BigDecimal.ZERO, "CNY");
}
public Money add(Money other) {
return new Money(this.amount.add(other.amount), this.currency);
}
public Money multiply(int factor) {
return new Money(this.amount.multiply(BigDecimal.valueOf(factor)), this.currency);
}
// Constructor, Getters...
}
// 枚举:订单状态
public enum OrderStatus {
PENDING_PAYMENT, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
COMPLETED, // 已完成
CANCELLED // 已取消
}
// =====================================================
// 2. Repository 层:数据访问
// =====================================================
@Repository
public interface OrderRepository extends JpaRepository {
// 根据用户查询订单
List findByUserIdOrderByCreatedAtDesc(Long userId);
// 根据状态查询订单
List findByStatus(OrderStatus status);
// 复杂的 JPQL 查询
@Query("""
SELECT o FROM Order o
WHERE o.userId = :userId
AND o.status IN :statuses
AND o.createdAt BETWEEN :startDate AND :endDate
ORDER BY o.createdAt DESC
""")
List findUserOrdersWithConditions(
@Param("userId") Long userId,
@Param("statuses") List statuses,
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate
);
}
// =====================================================
// 3. Service 层:业务逻辑
// =====================================================
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final ProductService productService;
private final InventoryService inventoryService;
private final IdGenerator idGenerator;
/**
* 创建订单
*/
@Transactional
public OrderDTO createOrder(OrderCreateParam param) {
// 1. 验证商品信息并扣减库存
List items = new ArrayList<>();
for (OrderItemParam itemParam : param.getItems()) {
// 查询商品
Product product = productService.getProduct(itemParam.getProductId());
// 检查库存
boolean reserved = inventoryService.reserveStock(
itemParam.getProductId(),
itemParam.getQuantity()
);
if (!reserved) {
throw new InsufficientStockException("商品库存不足: " + product.getName());
}
// 创建订单项
OrderItem item = new OrderItem();
item.setProductId(product.getId());
item.setProductName(product.getName());
item.setUnitPrice(product.getPrice());
item.setQuantity(itemParam.getQuantity());
items.add(item);
}
// 2. 创建订单
Order order = new Order();
order.setUserId(param.getUserId());
order.setShippingAddress(param.getAddress());
// 添加订单项
for (OrderItem item : items) {
order.addItem(item);
}
// 计算总价
order.calculateTotal();
// 3. 保存订单
orderRepository.save(order);
// 4. 转换为 DTO 返回
return OrderDTO.from(order);
}
/**
* 取消订单
*/
@Transactional
public void cancelOrder(Long orderId, Long userId) {
// 1. 查询订单
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
// 2. 验证权限
if (!order.getUserId().equals(userId)) {
throw new AccessDeniedException("无权操作此订单");
}
// 3. 执行业务逻辑(在领域对象中封装)
order.cancel(); // 这会检查状态是否允许取消
// 4. 恢复库存
for (OrderItem item : order.getItems()) {
inventoryService.releaseStock(item.getProductId(), item.getQuantity());
}
// 5. 保存
orderRepository.save(order);
}
}
// =====================================================
// 4. Controller 层:API 入口
// =====================================================
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
/**
* 创建订单
*/
@PostMapping
public ResponseEntity createOrder(
@RequestBody @Valid OrderCreateRequest request,
@AuthenticationPrincipal UserPrincipal user) {
// 1. Request -> Param 转换
OrderCreateParam param = OrderCreateParam.builder()
.userId(user.getId())
.address(request.getAddress())
.items(request.getItems().stream()
.map(item -> OrderItemParam.builder()
.productId(item.getProductId())
.quantity(item.getQuantity())
.build())
.collect(Collectors.toList()))
.build();
// 2. 调用 Service
OrderDTO order = orderService.createOrder(param);
// 3. 返回
return ResponseEntity.status(HttpStatus.CREATED).body(order);
}
/**
* 取消订单
*/
@PostMapping("/{orderId}/cancel")
public ResponseEntity cancelOrder(
@PathVariable Long orderId,
@AuthenticationPrincipal UserPrincipal user) {
orderService.cancelOrder(orderId, user.getId());
return ResponseEntity.noContent().build();
}
}
```
---
## 5. 分层架构的演进:从混乱到整洁
### 5.1 初学者常犯的错误
**错误一:Controller 里写业务逻辑**
```java
// ❌ 错误:Controller 里写了太多业务逻辑
@RestController
public class OrderController {
@Autowired private OrderRepository orderRepository;
@Autowired private ProductRepository productRepository;
@Autowired private InventoryRepository inventoryRepository;
@PostMapping("/orders")
public Order createOrder(@RequestBody CreateOrderRequest request) {
// 太多的业务逻辑在这里...
// 检查库存
for (ItemRequest item : request.getItems()) {
Product product = productRepository.findById(item.getProductId())
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (product.getStock() < item.getQuantity()) {
throw new RuntimeException("库存不足");
}
}
// 扣减库存
for (ItemRequest item : request.getItems()) {
Product product = productRepository.findById(item.getProductId()).get();
product.setStock(product.getStock() - item.getQuantity());
productRepository.save(product);
}
// 创建订单...
Order order = new Order();
// ... 更多逻辑
return orderRepository.save(order);
}
}
```
**错误二:Service 层直接操作数据库**
```java
// ❌ 错误:Service 里直接写 SQL
@Service
public class OrderService {
@Autowired
private JdbcTemplate jdbcTemplate; // 直接依赖底层 JDBC
public List getUserOrders(Long userId) {
// SQL 硬编码在 Service 里
String sql = "SELECT * FROM orders WHERE user_id = ? AND deleted = 0";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
Order order = new Order();
order.setId(rs.getLong("id"));
// ... 更多字段映射
return order;
}, userId);
}
}
```
**错误三:循环依赖**
```java
// ❌ 错误:Service 之间相互调用,形成循环依赖
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // A 依赖 B
}
@Service
public class PaymentService {
@Autowired
private OrderService orderService; // B 又依赖 A - 循环!
}
```
### 5.2 如何重构?
**重构一:提取 Service 层**
```java
// ✅ Controller 只负责接收请求和返回响应
@RestController
public class OrderController {
@Autowired
private OrderService orderService; // 只依赖 Service 接口
@PostMapping("/orders")
public OrderDTO createOrder(@RequestBody @Valid CreateOrderRequest request) {
// 1. Request -> Param
CreateOrderParam param = CreateOrderParam.builder()
.userId(getCurrentUserId())
.items(request.getItems())
.address(request.getAddress())
.build();
// 2. 调用 Service
Order order = orderService.createOrder(param);
// 3. Entity -> DTO
return OrderDTO.from(order);
}
}
// ✅ Service 封装业务逻辑
@Service
@Transactional
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductService productService;
@Autowired
private InventoryService inventoryService;
public Order createOrder(CreateOrderParam param) {
// 1. 检查库存并扣减
for (ItemParam item : param.getItems()) {
boolean reserved = inventoryService.reserveStock(
item.getProductId(),
item.getQuantity()
);
if (!reserved) {
throw new InsufficientStockException("库存不足");
}
}
// 2. 创建订单
Order order = new Order();
order.setUserId(param.getUserId());
// ... 设置其他属性
// 3. 保存
return orderRepository.save(order);
}
}
```
**重构二:提取 Repository 层**
```java
// ✅ Repository 接口:定义数据访问契约
@Repository
public interface OrderRepository extends JpaRepository {
// Spring Data JPA 自动生成实现
List findByUserIdOrderByCreatedAtDesc(Long userId);
@Query("SELECT o FROM Order o WHERE o.status = :status AND o.createdAt < :date")
List findByStatusAndCreatedAtBefore(
@Param("status") OrderStatus status,
@Param("date") LocalDateTime date
);
}
// ✅ Service 只依赖 Repository 接口
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository; // 依赖接口,不依赖实现
public Order getOrder(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new OrderNotFoundException(id));
}
}
```
**重构三:打破循环依赖**
```java
// ✅ 方案一:抽取共同的依赖到 Domain 层
// 在 Domain 层定义领域事件
public class OrderPaidEvent {
private final Long orderId;
private final Long userId;
private final Money amount;
private final LocalDateTime paidAt;
// ...
}
// OrderService 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void payOrder(Long orderId, PaymentParam param) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.pay(param.getPaymentMethod());
orderRepository.save(order);
// 发布事件,而不是直接调用 PaymentService
eventPublisher.publishEvent(new OrderPaidEvent(
order.getId(),
order.getUserId(),
order.getTotalAmount(),
LocalDateTime.now()
));
}
}
// PaymentService 监听事件
@Service
public class PaymentService {
@EventListener
@Transactional
public void handleOrderPaid(OrderPaidEvent event) {
// 处理支付相关逻辑
createPaymentRecord(event);
// ...
}
}
// ✅ 方案二:使用中间抽象
// 定义接口
public interface PaymentGateway {
PaymentResult processPayment(PaymentRequest request);
}
// OrderService 依赖接口
@Service
public class OrderService {
@Autowired
private PaymentGateway paymentGateway; // 依赖接口
}
// 实现类
@Service
public class AlipayGateway implements PaymentGateway {
// 实现...
}
```
---
## 5. 分层架构 vs 整洁架构
### 5.1 两种架构的对比
| 特性 | 传统分层架构 | 整洁架构 |
|------|-------------|----------|
| **依赖方向** | 从上到下 | 从外到内 |
| **核心业务位置** | Service 层 | Domain 层(中心) |
| **框架依赖** | 较深(如 Spring) | 较浅(通过接口隔离) |
| **可测试性** | 需要集成测试 | 核心可单元测试 |
| **学习曲线** | 平缓 | 较陡 |
| **适用场景** | 中小型项目、快速迭代 | 大型复杂业务、长期维护 |
### 5.2 如何选择?
**选择传统分层架构当...**
- 项目规模较小,业务相对简单
- 团队对 DDD 不熟悉
- 需要快速上线,验证市场
- 技术栈相对固定
**选择整洁架构当...**
- 业务复杂,领域模型丰富
- 需要长期维护和演进
- 需要频繁切换技术栈
- 团队有较强的设计能力
---
## 6. 总结:分层架构的核心要点
### 6.1 四层职责速查表
| 层级 | 主要职责 | 不该做的事 |
|------|---------|-----------|
| **Controller** | 接收请求、参数校验、调用 Service、返回响应 | 写业务逻辑、操作数据库、处理事务 |
| **Service** | 业务逻辑编排、事务管理、协调 Repository | 直接写 SQL、处理 HTTP 细节、返回实体给 Controller |
| **Repository** | 数据访问、ORM 映射、查询封装 | 写业务逻辑、管理事务、依赖上层 |
| **Domain** | 实体定义、业务规则、值对象 | 依赖其他层、处理持久化、处理 HTTP |
### 6.2 依赖方向铁律
```
✅ 正确的依赖方向:
Controller → Service 接口 → Repository 接口 → Domain
↑ ↑ ↑ ↑
└-----------└----------------└--------------┘
所有层都依赖 Domain,Domain 不依赖任何层
❌ 禁止的做法:
- Service 直接依赖 Repository 实现
- Controller 直接操作数据库
- Domain 依赖 Service 或 Repository
- 层与层之间形成循环依赖
```
### 6.3 编码最佳实践
1. **接口优先**:Service 和 Repository 都定义接口,实现类通过 Spring 注入
2. **DTO 隔离**:每层使用自己的 DTO,不要直接传递 Entity
3. **事务在 Service**:使用 `@Transactional` 在 Service 方法上控制事务
4. **异常处理**:Controller 统一处理异常,不要 try-catch 后吞掉异常
5. **贫血模型 vs 充血模型**:根据团队熟悉程度选择,但建议 Domain 有基本的行为方法
### 6.4 常见面试问题
**Q1: 为什么要分层?不分层可以吗?**
> A: 分层的目的是解耦和关注点分离。小项目可以不分层,但随着业务复杂度的增加,不分层会导致代码难以维护、测试困难、团队协作效率低下。
**Q2: Controller 层可以写业务逻辑吗?**
> A: 不可以。Controller 应该只负责接收请求、调用 Service、返回响应。业务逻辑应该封装在 Service 层,这样代码可以被复用,也更容易测试。
**Q3: 什么是贫血模型和充血模型?**
> A: 贫血模型是指 Entity 只有 getter/setter,业务逻辑都在 Service 层。充血模型是指 Entity 包含业务方法(如 `order.cancel()`),封装了业务规则。DDD 推荐充血模型,但贫血模型更简单易懂。
**Q4: 如何处理跨多个 Service 的事务?**
> A: 可以在上层 Service 中使用 `@Transactional`,调用多个下层 Service。或者使用分布式事务方案(如 Seata),但会增加系统复杂度。
---
## 7. 名词对照表
| 英文术语 | 中文对照 | 解释 |
|------|---------|------|
| **Layered Architecture** | 分层架构 | 将系统划分为多个层次,每层有明确的职责 |
| **Controller** | 控制器 | 接收 HTTP 请求,调用 Service,返回响应 |
| **Service** | 服务 | 封装业务逻辑,协调多个 Repository |
| **Repository** | 仓储 | 封装数据访问逻辑,执行 CRUD 操作 |
| **Domain** | 领域 | 定义业务实体、值对象和业务规则 |
| **DTO** | 数据传输对象 | 层与层之间传递数据的载体 |
| **Entity** | 实体 | 有唯一标识的领域对象,对应数据库表 |
| **Value Object** | 值对象 | 没有唯一标识,通过属性值判断相等的对象 |
| **Dependency Inversion** | 依赖倒置 | 高层模块不应依赖低层模块,都应依赖抽象 |
| **Transaction** | 事务 | 保证一组操作原子性的机制 |
| **Clean Architecture** | 整洁架构 | 以领域为核心的架构风格,强调依赖方向 |
| **Anemic Domain Model** | 贫血模型 | 实体只有数据没有行为的模型 |
| **Rich Domain Model** | 充血模型 | 实体包含数据和业务行为的模型 |
---
*本文档示例代码基于 Java + Spring Boot,但分层架构的思想适用于任何后端技术栈(Node.js、Python、Go 等)。*