### 依赖注意 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 { return [...this._items]; // 返回副本,保护内部数据 } } // 使用方式:对象是聪明的,告诉它做什么就行,而不是一步步操作它 const order = new Order([...]); order.markAsPaid(); // 对象自己处理状态变更 ```