领域驱动设计中,当实体包含值对象时(如订单包含产品详情),如何设计其等值性判断逻辑?请给出具体案例。

在领域驱动设计(DDD)中,实体(Entity)与其包含的值对象(Value Object)的等值性判断逻辑是设计上的一个关键点。实体的等值性通常基于其标识符(ID),而值对象的等值性基于其值。当实体中包含值对象时,需要确保在判断实体等值性时,值对象的值也被考虑进去。以下是一个具体的案例,假设我们有一个订单(Order)实体,该实体中包含产品详情(ProductDetail)的值对象。

实体 - Order

public class Order
{
    public Guid Id { get; private set; }
    public DateTime OrderDate { get; private set; }
    public List<ProductDetail> ProductDetails { get; private set; }

    public Order(DateTime orderDate)
    {
        Id = Guid.NewGuid();
        OrderDate = orderDate;
        ProductDetails = new List<ProductDetail>();
    }

    public void AddProductDetail(ProductDetail productDetail)
    {
        ProductDetails.Add(productDetail);
    }

    public override bool Equals(object obj)
    {
        if (obj is null || GetType() != obj.GetType())
            return false;

        var other = (Order)obj;

        return Id == other.Id &&
               OrderDate == other.OrderDate &&
               ProductDetails.SequenceEqual(other.ProductDetails);
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Id, OrderDate, ProductDetails);
    }
}

值对象 - ProductDetail

public class ProductDetail
{
    public string ProductId { get; private set; }
    public string Name { get; private set; }
    public decimal Price { get; private set; }

    public ProductDetail(string productId, string name, decimal price)
    {
        ProductId = productId;
        Name = name;
        Price = price;
    }

    public override bool Equals(object obj)
    {
        if (obj is null || GetType() != obj.GetType())
            return false;

        var other = (ProductDetail)obj;

        return ProductId == other.ProductId &&
               Name == other.Name &&
               Price == other.Price;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(ProductId, Name, Price);
    }
}

解释

  1. Order 类的 Equals 方法:在 Order 类中,Equals 方法首先检查对象类型,然后比较标识符 Id 和 OrderDate。接着,使用 SequenceEqual 方法比较两个 ProductDetails 列表,确保它们的内容相同。
  2. ProductDetail 类的 Equals 方法:在 ProductDetail 类中,Equals 方法也检查对象类型,然后比较 ProductId、Name 和 Price。这是值对象等值性的核心。
  3. GetHashCode 方法:为了确保哈希代码的一致性,GetHashCode 方法在 Order 和 ProductDetail 类中都实现了,使用 HashCode.Combine 方法组合各个属性的哈希代码。

通过这种方式,我们确保了在比较两个 Order 实体时,不仅考虑了它们的标识符和订单日期,还考虑了包含在订单中的所有产品详情的值。这确保了等值性判断的准确性和完整性。