Files
DDDMicroFrontend/doc/杂项必看.md
2025-09-24 23:56:55 +08:00

3.8 KiB
Raw Blame History

依赖注意

DDD 中允许 “外层依赖内层”(交付层依赖领域层),但不允许 “内层依赖外层”。

  • 正确features交付层 → domains领域层
  • 错误domains领域层 → features交付层

避免类型重复定义

如果不在视图中直接使用领域类型,就需要在交付层重新定义一套类似的类型(如 “视图专用商品类型”),这会导致:

  • 代码冗余和不一致风险
  • 类型转换的额外成本
  • 业务规则分散(领域层的类型约束可能在视图层被忽略)

横切关注点

  • 特征:横向,影响多个模块,那些无法放入任何一个业务模块,而是会"横着"贯穿多个甚至所有模块的技术性需求。
  • 案例:日志记录、身份认证、授权、事务管理、异常处理、缓存、性能监控

业务关注点

  • 特征:通常局限于一个业务模块内
  • 案例:计算订单总额、验证用户邮箱格式、处理库存扣减

对象职责的设计理念对比:

贫血模型

  • 特征:对象仅仅是数据的容器(一堆属性的集合),没有任何业务逻辑。所有操作这些数据的逻辑都放在外部的“服务”或“工具”类中。
  • 比喻:像一个没有灵魂的空壳,或者一个数据结构。它只负责装数据,至于数据怎么用、怎么变,它不管。
// 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); // 服务来改状态

充血模型

  • 特征:对象不仅包含数据,还包含与这些数据紧密相关的业务逻辑。它是有行为和责任的。
  • 比喻:像一个有智慧的专家。它不仅拥有数据,还知道如何操作和处理自己的数据。
// 充血的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(); // 对象自己处理状态变更