Architecture 实体标识-使用类作为标识而不是简单类型
Vaughn Vernon在C#()中实现了域驱动设计示例,其中有一种标识类型,所有专用于标识的类都是从该类型构建的:Architecture 实体标识-使用类作为标识而不是简单类型,architecture,domain-driven-design,software-design,Architecture,Domain Driven Design,Software Design,Vaughn Vernon在C#()中实现了域驱动设计示例,其中有一种标识类型,所有专用于标识的类都是从该类型构建的: public class CalendarId : Identity { public CalendarId() { } public CalendarId(string id) : base(id) { } } 身份类别: public abstract class Identity : IEquatable<Iden
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; }
}
日历类使用的标识如下:
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; }
}
}
公共类日历
{
公共日历(租户租户、日历ID日历ID、字符串名称、字符串描述、所有者、IEnumerable sharedWith=null)
{
//…存储传递的参数,包括标识
this.id=id;
}
CalendarId CalendarId;
公共日历ID日历ID
{
获取{返回this.calendarId;}
}
}
与使用更简单的类型(例如GUID或标识字符串)相比,上面的标识类提供了什么好处?我想这取决于设计偏好 不久前,我做了差不多相同的事情。我有一个
Identity
概念,因为密钥类型可能会有所不同。主要是Guid
,但有时是int
或string
我最终放弃了这种做法,因为它增加了比价值更多的开销,而且我的存储库总是知道它们处理的是什么类型和键,因此没有必要对其进行抽象
但是,如果您发现自己拥有一些通用位,例如,接受存储库并为您调用Get
,那么您可能会发现它很有用。例如:
public class IllGetYourEntity
{
public TEntity Get<TEntity>(IRepository<TKey> repository, Identity id)
{
return repository.Get(id);
}
}
长话短说:它不会给你买太多东西,除非有真正的需要,否则我会放弃它。我想这可以归结为设计偏好 不久前,我做了差不多相同的事情。我有一个
Identity
概念,因为密钥类型可能会有所不同。主要是Guid
,但有时是int
或string
我最终放弃了这种做法,因为它增加了比价值更多的开销,而且我的存储库总是知道它们处理的是什么类型和键,因此没有必要对其进行抽象
但是,如果您发现自己拥有一些通用位,例如,接受存储库并为您调用Get
,那么您可能会发现它很有用。例如:
public class IllGetYourEntity
{
public TEntity Get<TEntity>(IRepository<TKey> repository, Identity id)
{
return repository.Get(id);
}
}
长话短说:它不会给你带来太多的好处,除非有真正的需要,否则我会放弃它。一个原因可能是,当你在有界上下文之间传递标识对象时,标识对象本身带有标识(以及实体)创建的日期,并且可能还包含有关它起源的上下文的信息。这在第177页的书中有解释。一个原因可能是,当您在有界上下文之间传递标识对象时,标识对象本身携带标识(以及实体)创建的日期,并且还可能包含有关其起源上下文的信息。这在第177页的书中有解释。如果您有一个方法签名,它将多个
Guid
s作为参数,例如somethod(Guid tenantId,Guid customerId)
,它可以帮助防止将它们混合在一起,从而具有更具体的类型somethod(tenantId tenantId,customerId,customerId)
CustomerId
也比Guid
更符合普遍使用的语言。如果您有一个方法签名,将多个Guid
s作为参数,例如someMethod(Guid tenantId,Guid CustomerId)
,它可以帮助防止将它们混合在一起,从而具有更具体的类型someMethod>(TenantId、TenantId、CustomerId、CustomerId)
CustomerId
也比Guid
更符合无处不在的语言。
public interface ICustomerRepository:
ICanGet<Customer, Guid),
ICanAdd<Customer>
{
}
public interface ICustomerRepository
{
Customer Get(Guid id);
void Add(Customer customer);
}