问题是 - 实体可以由其所有属性定义,还是仅由其定义。
这是一个例子:
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类中)。
这是常见的情况吗?
答案 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)
几个笔记:
确定哪些对象是实体以及哪些是VO是很重要的。 VO身份由其内容(它代表的值)决定
从你描述的情况来看,它不是那么清楚
一般来说,10美元是10美元而Money
是所有书籍在谈论VO时所展示的第一个例子
但是,帐单可能会有所不同,可能会被视为实体。我可以拿10美元的钞票,你可以买一个。它们的值是相同的(它们在该术语中是相同的),但它们仍然是两个不同的实体,因此应该有一些身份字段。但是,这可能不是你的情况
如果你做认为账单是相同的,如果它们具有相同的billType
和drawing
,那么它就是一个VO。
对于讨论的第二部分,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。
所以,“可以通过所有属性来识别实体吗?” - 不,这是价值对象的概念。