值对象作为服务调用参数

时间:2013-09-03 15:10:02

标签: domain-driven-design

在实现DDD一书中,提到了创建TenantId值对象。这对我来说很有意义,因为GUID可能是空的而不是有效的TenantId,所以通过创建TenantId值对象我可以防止这种情况(我还有其他值) NamePhoneNumberEmailAddress等对象:

public class TenantId
{
    public TenantId(Guid id)
    {
        this.SetId(id);
    }

    public Guid Id { get; private set; }

    private void SetId(Guid id)
    {
        if (id == Guid.Empty)
        {
            throw new InvalidOperationException("Id must not be an empty GUID.");
        }

        this.Id = id;
    }
}

我感兴趣的是,我应该或不应该在服务方法上使用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.

这本书似乎有时会以某种方式,有时却是另一种方式。人们对这些方法的利弊有何看法?

2 个答案:

答案 0 :(得分:3)

如果服务不需要对实体的限制访问,请传递值对象,因为它是shared identifier,但我会将其编码为:

public class TenantId : IEquatable<TentantId>
{
    public TenantId(Guid id)
    {
        if (id == Guid.Empty)
        {
            throw new InvalidOperationException("TenantId must not be an empty GUID.");
        }
        _id = id;
    }

    private readonly Guid _id;

    public Equals(TentantId other)
    {
        if (null == other) return false;
        return _id.Equals(other._id);
    }

    public GetHashcode()
    {
        return _id.GetHashcode();
    }

    public override Equals(object other)
    {
        return Equals(other as TenantId);
    }

    public ToString()
    {
        return _id.ToString();
    }
}

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

答案 1 :(得分:1)

我更喜欢使用自定义值对象作为聚合的标识。我认为当其他开发人员想要调用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的答案。我认为“直接使用实体”更有意义。