实体可以通过其所有属性来识别吗?

Can Entity be identified by all of its properties?

问题是 - 实体可以由其所有属性定义还是仅由其 ID 定义。

这是示例:

class Wallet{
    int id;

    Map<BillType, Bill> bills;  //can have only 1 bill per bill type

    void addBill(BillType billType, Bill bill){
        this.bills.put(billType, bill);
    }
}

//this is probably an Entity, since it is mutable, but it has no global Id (only local bound to wallet)
//and equality is based on all of the properties
class Bill{
    BillType billType;
    Map<TypeOfBillSide, SideOfBill> billSides;  //can have only front or back

    Bill(BillType billType){
        this.billType = billType;
    }

    void drawWithPenOnBillSide(TypeOfBillSide typeOfBillSide, String drawing){
        this.billSides.get(typeOfBillSide).drawWithPenOnBillSide(drawing);
    }

    void burn(){
        System.out.println("I burned this bill");
    }
}

//this is probably an Entity, since it is mutable, but it has no global Id (only local bound to Bill)
//and equality is based on all of the properties
class SideOfBill{
    TypeOfBillSide typeOfBillSide;
    String drawing;

    public SideOfBill(TypeOfBillSide typeOfBillSide) {
        this.typeOfBillSide = typeOfBillSide;
    }

    void drawWithPenOnBillSide(String drawing){
        this.drawing = drawing;
        System.out.println("I draw on this side " + this.drawing);
    }
}

enum BillType{
    DOLLAR_10,
    DOLLAR_20,
    DOLLAR_50;
}

enum TypeOfBillSide{
    FRONT_SIDE,
    BACK_SIDE
}

这里我有全球唯一的钱包 - 即聚合根。它有账单,我认为在这种情况下是实体(因为我可以改变账单的状态,它仍然是钱包中的账单)。可以通过在账单的任何一侧绘制一些字符串来改变状态(SideOfBill - 在这种情况下也是一个实体)。

账单本身只有作为钱包的一部分才有意义,而且在钱包里我只能有一种账单(我不能有 2 张 10 美元的账单,例如只有一张)。

如果我将其视为 Value 对象,并使其不可变,那么每次我在 Bill 上绘制一些东西时,我都必须创建新的 bill - 在这种情况下这有点奇怪并且在代码中也很难做到.

如果我将其视为全局唯一的实体,我将必须拥有 Bill 的 ID,它实际上是 [Wallet.Id & Bill.billType] 的组合。但是 Wallet.id 在这种情况下并不自然地适合 Bill class。

最自然的事情是我将 Bill 视为实体并使用 equals 方法来测试所有 Bill 属性(同时测试 SideOfBill 的所有属性,因为它包含在 Bill class 中)。

这种常见情况有吗?

虽然这不是常见的做法,但值对象 (VO) 肯定可以是可变的(例如出于性能原因)。但是,您需要确保不共享可变 VO

不过,对可变 VO 的需求或许是一个强有力的指标,表明您尝试建模的概念实际上是一个实体。问自己一个很好的问题是您是否对此实例的生命周期感兴趣。

例如,在您的情况下,保留对帐单所做更改的历史记录是否重要?如果是,则应将账单建模为实体。

If I treat this as the entity that is globally unique, I would have to have id for Bill that is actually composite from [Wallet.Id & Bill.billType]. But Wallet.id does not fits naturally to Bill class in this case.

不要忘记,从领域模型的角度来看,实体只能在其聚合根 (AR) 中唯一标识。这意味着 billType 可以作为钱包内的账单 ID。

另请注意,如果需要,帐单可以具有数据库角度的代理身份或(walletId、billType)复合 ID。

一些注意事项:

  1. 正确决定哪些对象是实体,哪些是 VO 很重要。 VO的身份是由它的内容决定的(它代表的价值)。
    从你描述的情况看不太清楚。
    一般10$就是10$,Money是所有书上讲VO的第一个例子
    但是,bill 可能不同并且可能被视为实体。我可以有一张 10 美元的钞票,你也可以有一张。它们的值相同(在该术语中它们是相等的),但它们仍然是两个不同的实体,因此应该有一些身份字段。然而,这可能不是你的情况。
    如果你认为账单是一样的,如果他们有相同的billTypedrawing那么它就是一个VO。

  2. 对于讨论的第二部分,VO 通常在本质上和定义上都是不可变的。如果你有一个复杂的 VO 那么你可以考虑使用著名的 Builder 模式。

大体思路是使用下一条规则:

1) DDD 是关于 concepts/notions 之间的业务和关系的。实现没有严格定义。因此,最好写详细的故事,然后在领域地图上展示你的看法。

2) 一个实体是:

Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity. (Evans)

例如,订单可以具有 OrderItems、Price、ShippingAddress 和其他属性。如果有人更改了 ShippingAddress,订单将保持由其 OrderNumber 标识的同一对象,这不是新订单

即使您在系统中有两个具有所有相同属性(OrderItems、Price、ShippingAddress)的订单,它们仍然是不同的实体.唯一的区别是标识:OrderNumber.

3) 值对象由其所有属性定义。因此,将其作为不可变的是常见且方便的。

最明显的例子是价格:

Price{
  readonly Currency Currency;
  readonly Decimal Amount;
}
value1 = new Price(Currency.USD, 1);
value2 = new Price(Currency.USD, 1);

Assert.IsTrue(value1 == value2);

不太明显的例子是聚合根中 VO 的用法,这似乎适用于您的例子。

订单有 OrderItems,其中

OrderItem{
  string ProductSKU;
  int Amount;
}

OrderItem作为实体并添加OrderItemId属性可能会很方便,例如,用于数据库编辑。从商业角度来看,大多数时候根本没有 OrderItemId 的概念。订单项目位于其聚合根 Order 内,并在外部严格标识为一对 {Order, OrderItem}。在这种情况下,您甚至无法在不首先访问其聚合根的情况下触摸 OrderItem。

现在,如果我们查看 OrderItem,它完全由其属性标识,那么它就是值对象。


所以,"can Entity be identified by all of its properties?" - 不,这是值对象的概念。