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

时间:2017-02-20 11:37:25

标签: java entity domain-driven-design identity value-objects

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

这是一个例子:

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 - 在这种情况下也是一个实体)。

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

如果我将其视为Value对象,并使其成为不可变的,那么每次我在Bill上绘制一些东西时,我都要制作新的账单 - 在这种情况下,这有点奇怪,在代码中也很难做到。 / p>

如果我将此视为全球唯一的实体,我必须拥有Bill的ID,这实际上是来自[Wallet.Id&amp; Bill.billType。但在这种情况下,Wallet.id并不适合Bill类。

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

这是常见的情况吗?

3 个答案:

答案 0 :(得分:2)

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

但是,对可变VO的需求可能是一个强有力的指标,即您尝试建模的概念实际上是一个实体。问自己一个很好的问题是你是否对这个实例的生命周期感兴趣。

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

  

如果我将此视为全球唯一的实体,我将不得不这样做   对于Bill来说,这个ID实际上来自[Wallet.Id&amp;   Bill.billType。但在这种情况下,Wallet.id并不适合Bill类。

不要忘记,从域模型的角度来看,实体只能在其聚合根(AR)中唯一标识。这意味着 $currentPage = $this->uri->segment(3); # Pagina atual $sizePage = $this->uri->segment(4); # Tamanho da pagina (num de itens que serão listados) $searchText = $this->uri->segment(5); # Argumento de pesquisa $offset = ($currentPage - 1) * $sizePage; // Obter o offeset da pagina para obter os resultados paginados $r = $this->cursos->obterCursos(0,$nomeCurso=null,"array",$sizePage,$offset); $array = array('DadosDosCursos' => $r, 'totalCount' => count($r)); header('Content-Type: application/json'); return $this->output ->set_content_type('application/json') ->set_status_header(200) ->set_output(json_encode($array)); exit(json_encode($array)); 可以作为钱包中的帐单ID。

另请注意,如果需要,帐单可以从数据库透视图或(walletId,billType)复合ID中具有代理标识。

答案 1 :(得分:0)

几个笔记:

  1. 确定哪些对象是实体以及哪些是VO是很重要的。 VO身份由其内容(它代表的值)决定 从你描述的情况来看,它不是那么清楚 一般来说,10美元是10美元而Money是所有书籍在谈论VO时所展示的第一个例子 但是,帐单可能会有所不同,可能会被视为实体。我可以拿10美元的钞票,你可以买一个。它们的值是相同的(它们在该术语中是相同的),但它们仍然是两个不同的实体,因此应该有一些身份字段。但是,这可能不是你的情况 如果你认为账单是相同的,如果它们具有相同的billTypedrawing,那么它就是一个VO。

  2. 对于讨论的第二部分,VO通常是按性质和定义不变的。如果您有复杂的VO,那么您可以考虑使用着名的Builder模式。

答案 2 :(得分:0)

一般的想法是使用下一个规则:

1)DDD是关于商业和概念/概念之间的关系。实施没有严格定义。因此,最好编写详细的故事,然后展示你如何在域名地图上看到它。

2)实体是:

  

许多对象的属性并没有从根本上定义,但是   而是通过连续性和身份的线索。 (埃文斯)

例如,订单可以包含OrderItems,Price,ShippingAddress和其他属性。如果某人更改了ShippingAddress,则订单保持由其OrderNumber标识的同一对象,它不是新订单

即使系统中有两个订单,所有相同的属性(OrderItems,Price,ShippingAddress),它们仍然是不同的实体。唯一的区别是身份:OrderNumber。

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

明显的例子是Price:

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,它的属性绝对可以识别,那么它就是Value Object。

所以,“可以通过所有属性来识别实体吗?” - 不,这是价值对象的概念。