114 lines
3.8 KiB
Markdown
114 lines
3.8 KiB
Markdown
### 依赖注意
|
||
|
||
DDD 中允许 “外层依赖内层”(交付层依赖领域层),但不允许 “内层依赖外层”。
|
||
|
||
- 正确:features(交付层) → domains(领域层)
|
||
- 错误:domains(领域层) → features(交付层)
|
||
|
||
#### 避免类型重复定义
|
||
|
||
如果不在视图中直接使用领域类型,就需要在交付层重新定义一套类似的类型(如 “视图专用商品类型”),这会导致:
|
||
|
||
- 代码冗余和不一致风险
|
||
- 类型转换的额外成本
|
||
- 业务规则分散(领域层的类型约束可能在视图层被忽略)
|
||
|
||
### 横切关注点
|
||
|
||
- 特征:横向,影响多个模块,那些无法放入任何一个业务模块,而是会"横着"贯穿多个甚至所有模块的技术性需求。
|
||
- 案例:日志记录、身份认证、授权、事务管理、异常处理、缓存、性能监控
|
||
|
||
## 业务关注点
|
||
|
||
- 特征:通常局限于一个业务模块内
|
||
- 案例:计算订单总额、验证用户邮箱格式、处理库存扣减
|
||
|
||
### 对象职责的设计理念对比:
|
||
|
||
#### 贫血模型
|
||
|
||
- 特征:对象仅仅是数据的容器(一堆属性的集合),没有任何业务逻辑。所有操作这些数据的逻辑都放在外部的“服务”或“工具”类中。
|
||
- 比喻:像一个没有灵魂的空壳,或者一个数据结构。它只负责装数据,至于数据怎么用、怎么变,它不管。
|
||
|
||
```typescript
|
||
// 1. 贫血的Order实体:只有数据,没有行为
|
||
class Order {
|
||
public id: string;
|
||
public items: OrderItem[];
|
||
public total: number; // 总额需要外部来计算和设置
|
||
public status: string;
|
||
}
|
||
|
||
// 2. 所有业务逻辑都在外部的“服务”里
|
||
class OrderService {
|
||
calculateTotal(order: Order): void {
|
||
let total = 0;
|
||
for (const item of order.items) {
|
||
total += item.price * item.quantity;
|
||
}
|
||
order.total = total; // 从外部修改对象的数据
|
||
}
|
||
|
||
markAsPaid(order: Order): void {
|
||
if (order.total <= 0) {
|
||
throw new Error('订单金额无效');
|
||
}
|
||
order.status = 'paid'; // 从外部修改对象的状态
|
||
}
|
||
}
|
||
|
||
// 使用方式
|
||
const order = new Order();
|
||
order.items = [ ... ];
|
||
const orderService = new OrderService();
|
||
orderService.calculateTotal(order); // 服务来算总额
|
||
orderService.markAsPaid(order); // 服务来改状态
|
||
```
|
||
|
||
#### 充血模型
|
||
|
||
- 特征:对象不仅包含数据,还包含与这些数据紧密相关的业务逻辑。它是有行为和责任的。
|
||
- 比喻:像一个有智慧的专家。它不仅拥有数据,还知道如何操作和处理自己的数据。
|
||
|
||
```typescript
|
||
// 充血的Order实体:数据 + 行为
|
||
class Order {
|
||
public id: string;
|
||
private _items: OrderItem[];
|
||
private _total: number; // 总额是内部计算的结果
|
||
public status: string;
|
||
|
||
constructor(items: OrderItem[]) {
|
||
this._items = items;
|
||
this._total = this.calculateTotal(); // 构造时自己计算总额
|
||
this.status = 'created';
|
||
}
|
||
|
||
// 业务逻辑封装在实体内部
|
||
private calculateTotal(): number {
|
||
return this._items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
||
}
|
||
|
||
// 一个公开的业务方法
|
||
public markAsPaid(): void {
|
||
// 它自己 knows 自己的业务规则
|
||
if (this._total <= 0) {
|
||
throw new Error('订单金额无效,无法支付');
|
||
}
|
||
this.status = 'paid';
|
||
}
|
||
|
||
// 提供访问内部数据的方法(如果需要)
|
||
get total(): number {
|
||
return this._total;
|
||
}
|
||
|
||
get items(): ReadonlyArray<OrderItem> {
|
||
return [ ...this._items ]; // 返回副本,保护内部数据
|
||
}
|
||
}
|
||
|
||
// 使用方式:对象是聪明的,告诉它做什么就行,而不是一步步操作它
|
||
const order = new Order([ ... ]);
|
||
order.markAsPaid(); // 对象自己处理状态变更
|
||
``` |