Add Milky note: [Milky] 为您整理《第08课:DDD领域事件:系统解耦神技》笔记 | BV1PFVj6sEi2
This commit is contained in:
542
InBox/_08__DDD____________BV1PFV_6_E_2___.__
Normal file
542
InBox/_08__DDD____________BV1PFV_6_E_2___.__
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
# DDD领域事件:系统解耦神技
|
||||||
|
|
||||||
|
## 一、问题场景:串行业务的致命缺陷
|
||||||
|
|
||||||
|
### 典型错误写法
|
||||||
|
|
||||||
|
在日常开发中,很多工程师会写出这样的代码:一个核心业务后面跟着一大串同步操作。
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 创建订单流程(错误示范)
|
||||||
|
public void createOrder() {
|
||||||
|
// 第一步:创建订单 - 成功
|
||||||
|
Order order = orderRepository.save(new Order());
|
||||||
|
|
||||||
|
// 第二步:扣减库存 - 成功
|
||||||
|
inventoryService.decreaseStock(order.getProductId());
|
||||||
|
|
||||||
|
// 第三步:发送短信 - 失败!
|
||||||
|
smsService.sendNotification(order.getUserId());
|
||||||
|
|
||||||
|
// 第四步:增加积分 - 被堵死
|
||||||
|
pointsService.addPoints(order.getUserId());
|
||||||
|
|
||||||
|
// 第五步:通知商家 - 被堵死
|
||||||
|
merchantService.notify(order.getMerchantId());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 问题分析
|
||||||
|
|
||||||
|
当第三步发送短信失败时,会导致的后果:
|
||||||
|
|
||||||
|
| 步骤 | 结果 | 影响 |
|
||||||
|
|------|------|------|
|
||||||
|
| 创建订单 | ✓ 成功 | 订单已入库 |
|
||||||
|
| 扣减库存 | ✓ 成功 | 库存已扣 |
|
||||||
|
| 发送短信 | ✗ 失败 | 事务回滚 |
|
||||||
|
| 增加积分 | 被堵死 | 无法执行 |
|
||||||
|
| 通知商家 | 被堵死 | 无法执行 |
|
||||||
|
|
||||||
|
**最终结果**:整个订单被迫回滚,用户看到"下单失败"。就因为短信服务商抖动了一下,一单生意就黄了。
|
||||||
|
|
||||||
|
## 二、核心思想转变:从铁链到广播
|
||||||
|
|
||||||
|
### 两种思维模式对比
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 铁链子思维(串行耦合) │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ [创建订单] → [扣减库存] → [发短信] → [加积分] → [通知商家] │
|
||||||
|
│ ↓ ↓ ↓ ↓ ↓ │
|
||||||
|
│ └────────────┴────────────┴────────────┴───────────┘ │
|
||||||
|
│ ↓ │
|
||||||
|
│ 任一环节失败 = 整条链断裂 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 广播站思维(发布订阅) │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ [订单已创建] │
|
||||||
|
│ ╱ ╲ ╲ │
|
||||||
|
│ ╱ ╲ ╲ │
|
||||||
|
│ ↓ ↓ ↓ │
|
||||||
|
│ [短信服务] [积分服务] [商家服务] │
|
||||||
|
│ (收音机) (收音机) (收音机) │
|
||||||
|
│ ↓ ↓ ↓ │
|
||||||
|
│ 发短信 加积分 通知商家 │
|
||||||
|
│ │
|
||||||
|
│ 谁也不影响谁,互相独立,可单独扩展 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心理念
|
||||||
|
|
||||||
|
> **核心业务只管把自己最核心的事做完,剩下的让它们自己去听消息,然后干活。**
|
||||||
|
|
||||||
|
## 三、领域事件(Domain Event)定义
|
||||||
|
|
||||||
|
### 什么是领域事件
|
||||||
|
|
||||||
|
**定义**:领域事件描述的是一件已经发生的事,它是一个**事实陈述**,告诉系统"发生了什么",而不是"要去做什么"。
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 领域事件 = 已发生的事实 = 不可更改的历史记录 │
|
||||||
|
│ │
|
||||||
|
│ ✗ 不是命令:"去支付订单" (orderPay - 将来时) │
|
||||||
|
│ ✓ 是事实:"订单已支付" (orderPaid - 过去时) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 命名规范:必须用过去时态
|
||||||
|
|
||||||
|
这个命名习惯背后是整个思维模式的巨大转变:
|
||||||
|
|
||||||
|
| 类型 | 错误写法(命令式) | 正确写法(事实式) |
|
||||||
|
|------|-------------------|-------------------|
|
||||||
|
| 订单 | `orderPay` | `orderPaid` |
|
||||||
|
| 用户 | `userRegister` | `userRegistered` |
|
||||||
|
| 支付 | `doPayment` | `paymentCompleted` |
|
||||||
|
| 发货 | `shipOrder` | `orderShipped` |
|
||||||
|
|
||||||
|
### 领域事件的比喻
|
||||||
|
|
||||||
|
可以把领域事件想象成一封信:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ 📧 领域事件信件 │
|
||||||
|
├──────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 核心业务忙活完了,写了一封信: │
|
||||||
|
│ │
|
||||||
|
│ "嘿,我这完事儿了,你们看着办吧。" │
|
||||||
|
│ │
|
||||||
|
│ 然后把信往邮桶里一扔,就啥也不管了。 │
|
||||||
|
│ │
|
||||||
|
│ 他不关心: │
|
||||||
|
│ ✗ 谁会收到这封信 │
|
||||||
|
│ ✗ 收到信的人会拿他干嘛 │
|
||||||
|
│ │
|
||||||
|
│ 他只关心: │
|
||||||
|
│ ✓ 把这封信发出去 │
|
||||||
|
│ │
|
||||||
|
└──────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、发布订阅模式实现
|
||||||
|
|
||||||
|
### 三步走流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 发布订阅模式三步走 │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ 第一步 │ │ 第二步 │ │ 第三步 │ │
|
||||||
|
│ │ 发 生 │ → │ 送 信 │ → │ 干 活 │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ 在内存中创建 事务提交成功后 订阅者收到事件 │
|
||||||
|
│ event对象 消息总线发出 各自执行任务 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 具体代码实现
|
||||||
|
|
||||||
|
#### 第一步:在核心业务中发布事件
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 订单服务 - 只负责核心逻辑
|
||||||
|
public class OrderService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderRepository orderRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DomainEventPublisher eventPublisher; // 事件发布器
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Order createOrder(OrderCreateCommand command) {
|
||||||
|
// 核心业务:创建订单
|
||||||
|
Order order = new Order();
|
||||||
|
order.setUserId(command.getUserId());
|
||||||
|
order.setProductId(command.getProductId());
|
||||||
|
order.setAmount(command.getAmount());
|
||||||
|
|
||||||
|
// 核心业务:扣减库存(这个也是核心)
|
||||||
|
inventoryService.decreaseStock(command.getProductId());
|
||||||
|
|
||||||
|
// 保存订单
|
||||||
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
|
// 发布领域事件 - 订单已创建
|
||||||
|
OrderCreatedEvent event = new OrderCreatedEvent(order);
|
||||||
|
eventPublisher.publish(event);
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第二步:事务提交后发送事件
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 事件发布器实现
|
||||||
|
@Component
|
||||||
|
public class DomainEventPublisherImpl implements DomainEventPublisher {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationEventPublisher springPublisher;
|
||||||
|
|
||||||
|
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||||
|
public void handleAfterCommit(DomainEvent event) {
|
||||||
|
// 只有事务成功提交后,才真正发送事件
|
||||||
|
springPublisher.publishEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第三步:订阅者各自干活
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 短信服务 - 订阅者
|
||||||
|
@Component
|
||||||
|
public class SmsNotificationService {
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void handleOrderCreated(OrderCreatedEvent event) {
|
||||||
|
// 收到订单创建事件,发短信
|
||||||
|
smsService.send(event.getOrder().getUserId(), "订单创建成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 积分服务 - 订阅者
|
||||||
|
@Component
|
||||||
|
public class PointsService {
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void handleOrderCreated(OrderCreatedEvent event) {
|
||||||
|
// 收到订单创建事件,增加积分
|
||||||
|
addPoints(event.getOrder().getUserId(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商家服务 - 订阅者
|
||||||
|
@Component
|
||||||
|
public class MerchantService {
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void handleOrderCreated(OrderCreatedEvent event) {
|
||||||
|
// 收到订单创建事件,通知商家备货
|
||||||
|
notifyMerchant(event.getOrder().getMerchantId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、解耦带来的巨大好处
|
||||||
|
|
||||||
|
### 订单服务代码变化
|
||||||
|
|
||||||
|
**重构前**(订单服务知道所有下游服务):
|
||||||
|
|
||||||
|
```java
|
||||||
|
public void createOrder() {
|
||||||
|
// 创建订单...
|
||||||
|
// 扣减库存...
|
||||||
|
smsService.send(); // 订单服务需要知道短信服务
|
||||||
|
pointsService.add(); // 订单服务需要知道积分服务
|
||||||
|
merchantService.notify(); // 订单服务需要知道商家服务
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**重构后**(订单服务只关心自己):
|
||||||
|
|
||||||
|
```java
|
||||||
|
public void createOrder() {
|
||||||
|
// 创建订单... ✓
|
||||||
|
// 扣减库存... ✓
|
||||||
|
// 发布事件... ✓
|
||||||
|
// 就这么简单!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 扩展新功能示例
|
||||||
|
|
||||||
|
**场景**:产品经理要求增加"发送欢迎邮件"功能
|
||||||
|
|
||||||
|
| 传统方式 | 领域事件方式 |
|
||||||
|
|----------|--------------|
|
||||||
|
| 修改订单服务代码 | 不碰订单服务 |
|
||||||
|
| 添加邮件发送逻辑 | 开发新的邮件服务 |
|
||||||
|
| 回归测试所有功能 | 单独测试邮件服务 |
|
||||||
|
| 风险:可能引入bug | 完全解耦,安全 |
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 新增邮件服务 - 完全独立,不碰任何老代码
|
||||||
|
@Component
|
||||||
|
public class WelcomeEmailService {
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void handleOrderCreated(OrderCreatedEvent event) {
|
||||||
|
sendWelcomeEmail(event.getOrder().getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 六、消息丢失问题与解决方案
|
||||||
|
|
||||||
|
### 致命问题
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 消息丢失场景 │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 1. 业务数据(订单)成功写入数据库 │
|
||||||
|
│ 2. 事务成功提交 │
|
||||||
|
│ 3. 程序准备发送事件到消息队列 │
|
||||||
|
│ 4. 💥 服务器断电! │
|
||||||
|
│ 5. 事件永远丢失了 │
|
||||||
|
│ │
|
||||||
|
│ 结果:订单已创建,但没有人知道,短信没发、积分没加... │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 解决方案:发件箱模式(Outbox Pattern)
|
||||||
|
|
||||||
|
#### 核心思想
|
||||||
|
|
||||||
|
> **不要在业务事务里直接调用外部消息队列,而是把业务数据和事件消息打包放在同一个事务里。**
|
||||||
|
|
||||||
|
#### 表结构设计
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 订单表
|
||||||
|
CREATE TABLE orders (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
product_id BIGINT NOT NULL,
|
||||||
|
amount DECIMAL(10,2),
|
||||||
|
status VARCHAR(20),
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 发件箱表
|
||||||
|
CREATE TABLE outbox (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
aggregate_type VARCHAR(50), -- 聚合根类型,如 'Order'
|
||||||
|
aggregate_id BIGINT, -- 聚合根ID
|
||||||
|
event_type VARCHAR(100), -- 事件类型,如 'OrderCreatedEvent'
|
||||||
|
payload JSON, -- 事件内容(JSON格式)
|
||||||
|
status VARCHAR(20) DEFAULT 'PENDING', -- PENDING / SENT
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
sent_at TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 实现代码
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 使用发件箱模式的订单服务
|
||||||
|
@Service
|
||||||
|
public class OrderServiceWithOutbox {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OrderRepository orderRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OutboxRepository outboxRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Order createOrder(OrderCreateCommand command) {
|
||||||
|
// 1. 创建并保存订单
|
||||||
|
Order order = new Order();
|
||||||
|
order.setUserId(command.getUserId());
|
||||||
|
order.setProductId(command.getProductId());
|
||||||
|
order = orderRepository.save(order);
|
||||||
|
|
||||||
|
// 2. 扣减库存
|
||||||
|
inventoryService.decreaseStock(command.getProductId());
|
||||||
|
|
||||||
|
// 3. 创建事件消息并写入发件箱表
|
||||||
|
// 注意:和订单表在同一个事务里!
|
||||||
|
OrderCreatedEvent event = new OrderCreatedEvent(order);
|
||||||
|
OutboxMessage message = new OutboxMessage();
|
||||||
|
message.setAggregateType("Order");
|
||||||
|
message.setAggregateId(order.getId());
|
||||||
|
message.setEventType("OrderCreatedEvent");
|
||||||
|
message.setPayload(JSON.toJSONString(event));
|
||||||
|
message.setStatus("PENDING");
|
||||||
|
outboxRepository.save(message);
|
||||||
|
|
||||||
|
// 4. 事务提交时,订单和消息都会被写入数据库
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 后台进程可靠发送
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 后台任务:扫描发件箱并发送消息
|
||||||
|
@Component
|
||||||
|
public class OutboxProcessor {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OutboxRepository outboxRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageBroker messageBroker; // Kafka/RabbitMQ等
|
||||||
|
|
||||||
|
@Scheduled(fixedDelay = 100) // 每100毫秒执行一次
|
||||||
|
public void processOutbox() {
|
||||||
|
// 1. 取出所有待发送的消息
|
||||||
|
List<OutboxMessage> messages = outboxRepository
|
||||||
|
.findByStatus("PENDING")
|
||||||
|
.stream()
|
||||||
|
.limit(100) // 批量处理,最多100条
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (OutboxMessage message : messages) {
|
||||||
|
try {
|
||||||
|
// 2. 发送到消息队列
|
||||||
|
messageBroker.send(message.getTopic(), message.getPayload());
|
||||||
|
|
||||||
|
// 3. 更新状态为已发送
|
||||||
|
message.setStatus("SENT");
|
||||||
|
message.setSentAt(LocalDateTime.now());
|
||||||
|
outboxRepository.save(message);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 发送失败,保持PENDING状态,下次重试
|
||||||
|
log.error("发送消息失败: {}", message.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 原子性保证
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 原子性保证 │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────┐ │
|
||||||
|
│ │ 数据库事务(原子操作) │ │
|
||||||
|
│ │ ┌─────────────┐ ┌─────────────┐ │ │
|
||||||
|
│ │ │ 订单表 │ │ 发件箱表 │ │ │
|
||||||
|
│ │ │ (orders) │ │ (outbox) │ │ │
|
||||||
|
│ │ └─────────────┘ └─────────────┘ │ │
|
||||||
|
│ │ ✓ 全部成功 或 ✗ 全部回滚 │ │
|
||||||
|
│ └─────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ 绝对不会出现:订单入库了,但事件没写入 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 最终一致性
|
||||||
|
|
||||||
|
发件箱模式带来的取舍:
|
||||||
|
|
||||||
|
| 特性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **一致性类型** | 最终一致性(Eventually Consistent) |
|
||||||
|
| **即时性** | 发短信、加积分不会瞬间完成 |
|
||||||
|
| **保证** | 系统保证这些操作最终一定会被处理 |
|
||||||
|
| **延迟** | 通常在毫秒到秒级,取决于后台进程频率 |
|
||||||
|
|
||||||
|
## 七、核心要点总结
|
||||||
|
|
||||||
|
### 四大黄金法则
|
||||||
|
|
||||||
|
| 序号 | 要点 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 1 | **事件命名用过去时** | 因为它代表一个既定事实,不是命令 |
|
||||||
|
| 2 | **发布订阅模式** | 让主业务和次要业务彻底分家 |
|
||||||
|
| 3 | **真正的解耦** | 扩展功能时,只需增加新的订阅者,不动老代码 |
|
||||||
|
| 4 | **关注点分离** | 核心代码保持纯粹,只关心业务规则 |
|
||||||
|
|
||||||
|
### 思维模式对比
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 传统方式 vs 领域事件 │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ 传统方式: │
|
||||||
|
│ ┌─────────┐ │
|
||||||
|
│ │ 订单服务 │ → 短信服务 → 积分服务 → 商家服务 → ... │
|
||||||
|
│ └─────────┘ ↓ ↓ ↓ │
|
||||||
|
│ 耦合 耦合 耦合 │
|
||||||
|
│ 一改全改,风险极高 │
|
||||||
|
│ │
|
||||||
|
│ ───────────────────────────────────────────────────────────── │
|
||||||
|
│ │
|
||||||
|
│ 领域事件: │
|
||||||
|
│ ┌─────────┐ 发布 │
|
||||||
|
│ │ 订单服务 │ ───────────→ [订单已创建事件] │
|
||||||
|
│ └─────────┘ ↙ ↘ ↘ │
|
||||||
|
│ 短信 积分 商家 │
|
||||||
|
│ 订阅 订阅 订阅 │
|
||||||
|
│ 互不干扰,可独立扩展 │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 八、架构思想延伸
|
||||||
|
|
||||||
|
### 六边形架构(端口与适配器)
|
||||||
|
|
||||||
|
视频最后引出了一个重要的架构思想:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 六边形架构 │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌───────────────┐ │
|
||||||
|
│ │ 核心业务逻辑 │ ← 市中心 │
|
||||||
|
│ │ (Domain) │ 最宝贵的部分 │
|
||||||
|
│ └───────────────┘ │
|
||||||
|
│ ↙ ↘ ↘ │
|
||||||
|
│ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||||||
|
│ │ 端口1 │ │ 端口2 │ │ 端口3 │ │
|
||||||
|
│ └────────┘ └────────┘ └────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ═══════════════════════════════════════════════════════════ │
|
||||||
|
│ │
|
||||||
|
│ 市中心:业务逻辑 │
|
||||||
|
│ 郊区:技术实现(数据库、UI、外部服务) │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**核心理念**:
|
||||||
|
- 把数据库、用户界面等**技术实现细节**看作应用的"郊区"
|
||||||
|
- 让**最宝贵的纯粹业务逻辑**住在最核心的"市中心"
|
||||||
|
- 这样业务逻辑可以独立于技术细节进行测试和演化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、观众反馈补充
|
||||||
|
|
||||||
|
| 反馈内容 | 备注 |
|
||||||
|
|----------|------|
|
||||||
|
| "这种可读性不好吧" | 可能是指代码示例的格式,建议实际项目中使用更规范的代码格式 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **下节预告**:六边形架构思想的详细揭秘,如何组织应用程序结构最合理。
|
||||||
Reference in New Issue
Block a user