Domain driven design 值对象作为服务调用参数

Domain driven design 值对象作为服务调用参数,domain-driven-design,Domain Driven Design,在实现DDD的书中,提到了创建一个TenantIdvalue对象。这对我来说很有意义,因为GUID可能是空的,而它不是有效的TenantId,因此通过创建TenantId值对象,我可以防止这种情况(我还有其他值对象,如名称,电话号码,电子邮件地址,等等): 我感兴趣的是,我是否应该在服务方法上使用此TenantId,例如: TenantId tenantId = new TenantId(model.TenantId); // model.TenantId being a GUID. this

在实现DDD的书中,提到了创建一个
TenantId
value对象。这对我来说很有意义,因为
GUID
可能是空的,而它不是有效的
TenantId
,因此通过创建
TenantId
值对象,我可以防止这种情况(我还有其他值对象,如
名称
电话号码
电子邮件地址
,等等):

我感兴趣的是,我是否应该在服务方法上使用此
TenantId
,例如:

TenantId tenantId = new TenantId(model.TenantId); // model.TenantId being a GUID.

this.tenantService.GetTenant(tenantId);
或者我应该在服务方法参数中使用更原始的形式:

this.tenantService.GetTenant(model.TenantId); // again model.TenantId is a GUID.

这本书似乎有时用一种方式,有时又用另一种方式。人们对这些方法的优缺点有什么看法?

我更喜欢使用自定义值对象作为聚合的标识。我认为当其他开发人员想要调用api时,它提供了更多的线索。考虑这一点:

Tenant findBy(String identity); //others may pass an String instance but not id and the compliler can't check it out for you
您不能在下面添加此方法:

Tenant findBy(String name);//assuming name unique but it failed for compiling
如果已经有了特定类型,为什么要使用原始类型

编辑


对于存储库和应用程序服务,我通常使用自定义类型,对于域服务,请参考@Giacomo Tesio'的回答。我认为“直接使用实体”更有意义。

我更喜欢使用自定义值对象作为聚合的标识。我认为当其他开发人员想要调用api时,它提供了更多的线索。考虑这一点:

Tenant findBy(String identity); //others may pass an String instance but not id and the compliler can't check it out for you
您不能在下面添加此方法:

Tenant findBy(String name);//assuming name unique but it failed for compiling
如果已经有了特定类型,为什么要使用原始类型

编辑


对于存储库和应用程序服务,我通常使用自定义类型,对于域服务,请参考@Giacomo Tesio'的回答。我认为“直接使用实体”更有意义。

如果服务不需要对实体进行受限访问,请传递value对象,因为它是一个,但我会将其编码为以下内容:

公共类租户ID:IEquatable
{
公共租户id(Guid id)
{
if(id==Guid.Empty)
{
抛出新的InvalidOperationException(“TenantId不能是空GUID”);
}
_id=id;
}
专用只读Guid\u id;
公共平等(其他)
{
if(null==其他)返回false;
返回_id.Equals(其他_id);
}
公共GetHashcode()
{
返回_id.GetHashcode();
}
公共覆盖等于(对象其他)
{
收益等于(其他为租户);
}
公共ToString()
{
返回_id.ToString();
}
}

但是,在某些情况下,域服务应仅与用户可以在存储库中访问的实体一起调用:在这些情况下,域服务的公共接口需要实体(即使实现仅使用标识符)。

如果服务不需要对实体的受限访问,传递值对象,因为它是一个,但我会将其编码如下:

公共类租户ID:IEquatable
{
公共租户id(Guid id)
{
if(id==Guid.Empty)
{
抛出新的InvalidOperationException(“TenantId不能是空GUID”);
}
_id=id;
}
专用只读Guid\u id;
公共平等(其他)
{
if(null==其他)返回false;
返回_id.Equals(其他_id);
}
公共GetHashcode()
{
返回_id.GetHashcode();
}
公共覆盖等于(对象其他)
{
收益等于(其他为租户);
}
公共ToString()
{
返回_id.ToString();
}
}

但是,在某些情况下,域服务应仅与用户可以在存储库中访问的实体一起调用:在这些情况下,域服务的公共接口需要该实体(即使实现仅使用标识符)。

非常感谢,您是否还会重载==和!=运营商作为标准?+1表示“域服务的公共接口需要实体”。@AdrianThompsonPhillips如果您的客户习惯了它们,我会覆盖它们。非常感谢,您是否还会使==和!=过载运营商作为标准?+1对于“域服务的公共接口需要实体”的优秀观点。@AdrianThompsonPhillips如果你的客户习惯了他们,是的,我会覆盖他们。谢谢,似乎大家一致认为,如果你对某件事有一个不变的值对象,那么在可能的情况下使用它是很好的(除了在实体上操作更有意义的域服务)。谢谢,大家似乎一致认为,如果您对某些东西有一个不可变的值对象,那么在可能的情况下使用它是很好的(在实体上操作更有意义的域服务除外)。