实体标识 - 使用类作为标识而不是简单类型

时间:2014-11-18 15:21:31

标签: architecture domain-driven-design software-design

使用Vaughn Vernon在C#(https://github.com/VaughnVernon/IDDD_Samples)中实现域驱动设计示例,有一个标识类型,所有专用于身份的类都是从这个类型构建的:

public class CalendarId : Identity
{
    public CalendarId() { }

    public CalendarId(string id)
        : base(id)
    {
    }
}

身份等级:

public abstract class Identity : IEquatable<Identity>, IIdentity
{
    public Identity()
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public Identity(string id)
    {
        this.Id = id;
    }

    // currently for Entity Framework, set must be protected, not private.
    // will be fixed in EF 6.
    public string Id { get; protected set; }

    public bool Equals(Identity id)
    {
        if (object.ReferenceEquals(this, id)) return true;
        if (object.ReferenceEquals(null, id)) return false;
        return this.Id.Equals(id.Id);
    }

    public override bool Equals(object anotherObject)
    {
        return Equals(anotherObject as Identity);
    }

    public override int GetHashCode()
    {
        return (this.GetType().GetHashCode() * 907) + this.Id.GetHashCode();
    }

    public override string ToString()
    {
        return this.GetType().Name + " [Id=" + Id + "]";
    }
}

身份界面:

public interface IIdentity
{
    string Id { get; }
}

使用标识的Calender类:

public class Calendar
{
    public Calendar(Tenant tenant, CalendarId calendarId, string name, string description, Owner owner, IEnumerable<CalendarSharer> sharedWith = null)
    {
         // ... store passed parameters, including identity
         this.id = id;
    }

    CalendarId calendarId;

    public CalendarId CalendarId
    {
        get { return this.calendarId; }
    }
}

上述身份类提供了什么好处,比如使用更简单的类型(如GUID)或字符串作为标识?

2 个答案:

答案 0 :(得分:3)

这归结为我猜想的设计偏好。

前段时间我做了或多或少的同样的事情。我有一个Identity<T>概念,因为密钥类型可能会有所不同。主要是Guid,但有时为intstring

我最终放弃了这种做法,因为它增加了比价值更多的开销,因为我的存储库总是知道他们正在处理什么类型和密钥,所以没有必要抽象它。

但是,如果您发现自己有一些泛型位,例如,它们会接受存储库并为您进行Get调用,那么您可能会发现它很有用。例如:

public class IllGetYourEntity
{
    public TEntity Get<TEntity>(IRepository<TKey> repository, Identity id)
    {
        return repository.Get(id);
    }
}

但我甚至使用了更细粒度,特定于角色的存储库接口结构。我宁愿拥有这个:

public interface ICanGet<TEntity, TId)
{
    TEntity Get(TId id);
}

public interface ICanAdd<TEntity>
{
    void Add(TEntity entity);
}

然后可以由另一个存储库接口使用它们:

public interface ICustomerRepository: 
    ICanGet<Customer, Guid), 
    ICanAdd<Customer> 
{
}

但如果您不需要,那么一个好的实体特定存储库接口将会:

public interface ICustomerRepository
{
    Customer Get(Guid id);
    void Add(Customer customer);
} 

长话短说:它不会给你太多买,除非真正需要,否则我会放弃它。

答案 1 :(得分:2)

一个原因可能是,当您在有界上下文之间传递标识对象时,标识对象本身会携带创建标识(以及实体)时的日期,并且可能还包含有关其源自的上下文的信息。这在第177页的书中有解释。