分层原则(Layering Principle)是软件工程中最基础、最广泛应用的架构设计原则之一。它通过将系统划分为多个层次(Layer),每个层次承担特定的职责,从而实现关注点分离、降低复杂度、提高可维护性。
分层原则的核心思想是:将复杂系统分解为多个相互协作但职责明确的层次,上层依赖下层,下层不感知上层。
层是系统中一组相关功能的逻辑分组,具有以下特征:
层与层之间的关系主要表现为:
| 关系类型 | 说明 | 示例 |
|---|---|---|
| 依赖关系 | 上层依赖下层的服务 | Controller 依赖 Service |
| 调用关系 | 上层调用下层的接口 | Service 调用 Repository |
| 数据传递 | 层间通过DTO传递数据 | Controller ↔ Service ↔ DAO |
| 事件传播 | 下层通过事件通知上层 | Domain Event 向上传播 |
| 特性 | 分层(Layering) | 分区(Partitioning) |
|---|---|---|
| 关注点 | 垂直方向,按职责划分 | 水平方向,按业务划分 |
| 依赖方向 | 单向,上层依赖下层 | 可能双向或松散 |
| 典型应用 | 技术架构 | 业务架构 |
| 示例 | 表现层、业务层、数据层 | 订单模块、用户模块、支付模块 |
重要:分层和分区是正交的,实际系统中通常同时使用。例如,微服务架构中,每个服务内部采用分层,服务之间按业务分区。
分层的本质是抽象层次的组织。每一层都是对下一层的抽象,隐藏实现细节,提供更高级别的服务。
┌─────────────────────────────────────┐
│ 第N层:最高抽象,最接近业务概念 │
├─────────────────────────────────────┤
│ 第N-1层:对下层的抽象和组合 │
├─────────────────────────────────────┤
│ ... │
├─────────────────────────────────────┤
│ 第2层:基础服务,提供通用能力 │
├─────────────────────────────────────┤
│ 第1层:最底层,最接近硬件和基础设施 │
└─────────────────────────────────────┘
通过分层,将复杂系统分解为多个相对简单的子系统:
不同层次关注不同的问题:
三层架构是最经典的分层模式:
┌─────────────────────────────────────────┐
│ 表现层(Presentation Layer) │
│ - 用户界面 │
│ - 输入验证 │
│ - 响应格式化 │
├─────────────────────────────────────────┤
│ 业务层(Business Layer) │
│ - 业务规则 │
│ - 业务流程 │
│ - 业务逻辑 │
├─────────────────────────────────────────┤
│ 数据层(Data Layer) │
│ - 数据持久化 │
│ - 数据查询 │
│ - 事务管理 │
└─────────────────────────────────────────┘
N层架构是三层架构的扩展,根据需要将系统划分为更多层次:
┌─────────────────────────────────────────┐
│ 表现层(Presentation Layer) │
├─────────────────────────────────────────┤
│ 控制层(Controller Layer) │
├─────────────────────────────────────────┤
│ 应用层(Application Layer) │
├─────────────────────────────────────────┤
│ 领域层(Domain Layer) │
├─────────────────────────────────────────┤
│ 基础设施层(Infrastructure Layer) │
└─────────────────────────────────────────┘
分层架构可以有不同的物理部署方式:
| 部署模式 | 说明 | 适用场景 |
|---|---|---|
| 单层部署 | 所有层部署在同一进程 | 单体应用、小型系统 |
| 多层部署 | 层部署在不同进程或机器 | 中大型系统、高可用要求 |
| 分布式部署 | 层内组件分布式部署 | 大型系统、云原生架构 |
上层可以依赖下层,下层不能依赖上层。
// ✅ 正确:Controller 依赖 Service
public class OrderController {
private final OrderService orderService; // 依赖下层
}
// ❌ 错误:Service 依赖 Controller
public class OrderService {
private final OrderController controller; // 下层依赖上层!
}
层间交互应通过接口,而非具体实现。
// ✅ 正确:依赖接口
public class OrderService {
private final OrderRepository repository; // 接口
}
// ❌ 错误:依赖具体实现
public class OrderService {
private final JdbcOrderRepository repository; // 具体实现
}
上层只应了解直接下层,不应跨层调用。
// ✅ 正确:Controller → Service → Repository
public class OrderController {
public void createOrder(OrderDTO dto) {
orderService.create(dto); // 只调用直接下层
}
}
// ❌ 错误:Controller 直接调用 Repository
public class OrderController {
public void createOrder(OrderDTO dto) {
orderRepository.save(dto); // 跨层调用!
}
}
每层只应承担一类职责。
| 层次 | 职责 | 不应承担的职责 |
|---|---|---|
| 表现层 | 用户交互 | 业务规则判断 |
| 业务层 | 业务逻辑 | 数据持久化细节 |
| 数据层 | 数据访问 | 业务流程编排 |
| 特性 | 严格分层 | 松散分层 |
|---|---|---|
| 依赖规则 | 只能依赖直接下层 | 可以依赖任何下层 |
| 灵活性 | 低 | 高 |
| 耦合度 | 低 | 较高 |
| 维护性 | 高 | 中等 |
| 性能 | 可能较差(层层转发) | 可能较好 |
在分层架构中应用依赖倒置原则:
┌─────────────────────────────────────────┐
│ 业务层(定义接口) │
│ ┌─────────────────────────────────┐ │
│ │ OrderService(接口) │ │
│ │ OrderRepository(接口) │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ 基础设施层(实现接口) │
│ ┌─────────────────────────────────┐ │
│ │ OrderServiceImpl(实现) │ │
│ │ JpaOrderRepository(实现) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
关键点:业务层定义接口,基础设施层实现接口。这样业务层不依赖基础设施层,而是基础设施层依赖业务层。
表现层
↓
应用层
↓
领域层 ←──── 定义接口
↑
基础设施层 ←── 实现接口
Model-View-Controller 是最经典的分层模式:
┌─────────────────────────────────────────┐
│ View(视图层) │
│ - 负责数据显示 │
│ - 接收用户输入 │
├─────────────────────────────────────────┤
│ Controller(控制层) │
│ - 接收用户请求 │
│ - 调用 Model 处理 │
│ - 选择 View 响应 │
├─────────────────────────────────────────┤
│ Model(模型层) │
│ - 业务逻辑 │
│ - 数据访问 │
└─────────────────────────────────────────┘
Model-View-ViewModel 是前端常用的分层模式:
┌─────────────────────────────────────────┐
│ View(视图) │
│ - UI 组件 │
│ - 数据绑定 │
├─────────────────────────────────────────┤
│ ViewModel(视图模型) │
│ - 视图状态管理 │
│ - 业务逻辑封装 │
├─────────────────────────────────────────┤
│ Model(模型) │
│ - 领域模型 │
│ - 数据访问 │
└─────────────────────────────────────────┘
Clean Architecture 是一种严格的分层架构:
┌─────────────────────────────────────────┐
│ Frameworks & Drivers │
│ (UI, Database, External Interfaces) │
├─────────────────────────────────────────┤
│ Interface Adapters │
│ (Controllers, Presenters, Gateways) │
├─────────────────────────────────────────┤
│ Application Business Rules │
│ (Use Cases) │
├─────────────────────────────────────────┤
│ Enterprise Business Rules │
│ (Entities) │
└─────────────────────────────────────────┘
依赖规则:依赖方向只能向内,外层依赖内层,内层不依赖外层。
六边形架构强调应用核心与外部适配器的分离:
┌─────────────┐
│ UI/API │
└──────┬──────┘
│
┌─────────────┐ ┌────┴────┐ ┌─────────────┐
│ Test Adapter │ ← │ Application │ → │ Database │
└─────────────┘ │ Core │ └─────────────┘
└────┬────┘
│
┌──────┴──────┐
│ Message Queue│
└─────────────┘
问题:上层直接调用下层,跳过中间层。
// ❌ 错误:Controller 直接调用 Repository
@RestController
public class OrderController {
@Autowired
private OrderRepository repository; // 应该通过 Service
@PostMapping("/orders")
public void create(OrderDTO dto) {
repository.save(dto); // 绕过 Service 层!
}
}
影响:
问题:层之间形成循环依赖。
// ❌ 错误:Service A 依赖 Service B,Service B 又依赖 Service A
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
@Service
public class PaymentService {
@Autowired
private OrderService orderService; // 循环依赖!
}
解决方案:
问题:业务逻辑全部放在 Service 层,Domain 层只有数据。
// ❌ 错误:贫血模型
public class Order {
private Long id;
private List<OrderItem> items;
private OrderStatus status;
// 只有 getter/setter,没有业务逻辑
}
@Service
public class OrderService {
public void cancel(Order order) {
if (order.getStatus() == OrderStatus.SHIPPED) {
throw new IllegalStateException("Cannot cancel shipped order");
}
order.setStatus(OrderStatus.CANCELLED);
// 业务逻辑在 Service 中
}
}
改进:
// ✅ 正确:充血模型
public class Order {
private Long id;
private List<OrderItem> items;
private OrderStatus status;
public void cancel() {
if (status == OrderStatus.SHIPPED) {
throw new IllegalStateException("Cannot cancel shipped order");
}
this.status = OrderStatus.CANCELLED;
// 业务逻辑在 Domain 中
}
}
问题:某一层承担了过多职责。
常见表现:
解决方案:
问题:层次过多,导致简单操作层层转发。
// ❌ 错误:过度分层
controller.create(dto)
→ service.create(dto)
→ manager.create(dto)
→ processor.create(dto)
→ repository.save(dto)
影响:
建议:根据系统复杂度选择合适的层数,通常 3-5 层为宜。
每个微服务内部仍然采用分层架构:
┌─────────────────────────────────────────┐
│ 用户服务(User Service) │
│ ┌─────────────────────────────────┐ │
│ │ API Layer │ │
│ ├─────────────────────────────────┤ │
│ │ Application Layer │ │
│ ├─────────────────────────────────┤ │
│ │ Domain Layer │ │
│ ├─────────────────────────────────┤ │
│ │ Infrastructure Layer │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
微服务架构中,服务之间也可以形成层次:
┌─────────────────────────────────────────┐
│ 网关层(Gateway Layer) │
│ - 路由、限流、认证 │
├─────────────────────────────────────────┤
│ 聚合层(Aggregation Layer) │
│ - BFF(Backend for Frontend) │
├─────────────────────────────────────────┤
│ 业务层(Business Layer) │
│ - 核心领域服务 │
├─────────────────────────────────────────┤
│ 基础层(Foundation Layer) │
│ - 通用基础服务 │
└─────────────────────────────────────────┘
| 方面 | 单体分层 | 微服务分层 |
|---|---|---|
| 复杂度 | 代码复杂,部署简单 | 代码简单,部署复杂 |
| 性能 | 内部调用,性能好 | 网络调用,性能损耗 |
| 灵活性 | 技术栈统一 | 技术栈灵活 |
| 扩展性 | 整体扩展 | 独立扩展 |
领域驱动设计提出了经典的分层架构:
┌─────────────────────────────────────────┐
│ 用户界面层(User Interface Layer) │
│ - 负责与用户交互 │
├─────────────────────────────────────────┤
│ 应用层(Application Layer) │
│ - 编排用例 │
│ - 协调领域对象 │
├─────────────────────────────────────────┤
│ 领域层(Domain Layer) │
│ - 核心业务逻辑 │
│ - 领域模型、值对象、实体 │
├─────────────────────────────────────────┤
│ 基础设施层(Infrastructure Layer) │
│ - 技术实现细节 │
│ - 数据库、消息队列、外部服务 │
└─────────────────────────────────────────┘
用户界面层
↓
应用层
↓
领域层 ←──── 定义 Repository 接口
↑
基础设施层 ←── 实现 Repository 接口
确定层次的步骤:
好的层次边界:
不好的层次边界:
场景:某功能需要跨层调用。
解决方案:
Java 项目的典型包结构:
com.example.project/
├── ui/ # 用户界面层
│ ├── controller/ # 控制器
│ ├── dto/ # 数据传输对象
│ └── mapper/ # 对象映射
├── application/ # 应用层
│ ├── service/ # 应用服务
│ ├── event/ # 应用事件
│ └── dto/ # 应用层 DTO
├── domain/ # 领域层
│ ├── model/ # 领域模型
│ ├── repository/ # 仓库接口
│ ├── service/ # 领域服务
│ └── event/ # 领域事件
└── infrastructure/ # 基础设施层
├── persistence/ # 持久化实现
├── messaging/ # 消息实现
└── external/ # 外部服务实现
各层的异常处理策略:
| 层次 | 异常处理 | 说明 |
|---|---|---|
| 表现层 | 捕获所有异常 | 统一错误响应 |
| 应用层 | 捕获业务异常 | 转换为应用异常 |
| 领域层 | 抛出业务异常 | 不捕获技术异常 |
| 基础设施层 | 捕获技术异常 | 转换为领域异常 |
| 收益 | 说明 |
|---|---|
| 降低复杂度 | 将大问题分解为小问题 |
| 提高可维护性 | 修改影响范围可控 |
| 促进复用 | 层可以被多个上层复用 |
| 支持独立演进 | 每层可以独立升级 |
| 便于测试 | 每层可以独立测试 |
| 成本 | 说明 |
|---|---|
| 性能损耗 | 层间调用有开销 |
| 代码冗余 | 需要定义层间接口 |
| 学习成本 | 需要理解分层架构 |
| 过度设计风险 | 简单系统可能不需要复杂分层 |
应该分层的情况:
可以不分层的情况:
粗粒度分层:
细粒度分层:
建议:根据系统复杂度选择合适的粒度,避免过度设计。
分层原则是软件架构的基石,它通过将系统划分为多个职责明确的层次,实现了关注点分离、降低了复杂度、提高了可维护性。
分层原则不是银弹,它需要根据具体场景灵活运用。理解分层的本质和目的,比机械地套用分层模式更重要。