我正在构建我的域模型并继续重构它。像我一样,我发现我喜欢接口,因为它允许我根据接口为具体类型创建可重用的方法/控制器/视图。但是,我发现每次向其中一个域实体添加新属性时都会创建一个接口。
例如,我有一个 MemberStatus 对象,该对象继承自一个抽象的 Entity 对象,后者又实现 IIdentifiableEntity 接口,意味着它具有Id属性。 MemberStatus还实现 INamedEntity 接口,这意味着它具有Name属性, IOrderedEntity 接口意味着它具有DisplayOrder属性和 IHasMembers 接口含义它有一个集合成员对象。这是代码:
public class MemberStatus : Entity, INamedEntity, IOrderedEntity, IHasMembers
{
public string Name { get; set; }
public float DisplayOrder { get; set; }
public ICollection<Member> Members { get; set; }
}
public abstract class Entity : IIdentifiableEntity
{
public int Id { get; set; }
}
public interface IIdentifiableEntity
{
int Id { get; set; }
}
public interface INamedEntity
{
string Name { get; set; }
}
public interface IOrderedEntity
{
float DisplayOrder { get; set; }
}
public interface IHasMembers
{
ICollection<Member> Members { get; set; }
}
现在,这似乎与其他类似的对象一样正常,例如 MemberPosition 和 MemberTeam ,它们都实现了这些相同的接口,我可以使用我的存储库方法和控制器操作使用泛型来实现这些接口并重复使用大量代码。
但是,我担心的是每次向具体对象添加新属性时是否继续添加简单的单属性接口是合适的。例如,假设我要添加bool Enabled
属性...我应该继续创建 IEnabled 界面吗?我问的原因是一些使用泛型的控制器“初始化器”变得非常长,如下面的代码行所示。这是正常的最佳做法吗?
public abstract class OrderedCrudController<TEntity> : CrudController<TEntity> where TEntity : Entity, INamedEntity, IOrderedEntity, IHasMembers, new()
答案 0 :(得分:19)
您使用接口这一事实是一件好事。但是,您应该问问自己,如果我创建一个IEnabled
接口,我是否会仅通过该接口引用我的类?即,是否存在我与我的班级互动的上下文,纯粹是通过界面公开的单一属性?
另外,您是否可以考虑将与此IEnabled
接口的多个实现进行交互的上下文?
如果这两个问题的答案都是“否”,则界面的用途很少。
话虽如此,请不要过于担心!它的伤害很小。
答案 1 :(得分:15)
不要创建您不希望迫切需要的接口。观察YAGNI(你不需要它)的原则。否则你会遇到不必要复杂的代码。
答案 2 :(得分:3)
我认为你的问题在于你试图将你的领域模型用于你正在显示数据的任何gui。
相反,请考虑您的域对象具有接近数据及其c'tor的行为的事物,给它一个Action<DomainEvent>
。现在,请确保您只能通过此操作从域对象传递数据OUT。
现在,你听。每当实际想要对您的域进行更改时,请在其上调用方法。通过获取这些事件并将其保存到您感兴趣的任何读取模型,让您的GUI通过Action<DomainEvent>
进行更新。
查看http://www.infoq.com/presentations/ddd-eric-evans并考虑他关于域名事件的观点。
现在您不必再将与技术域相关的奇怪接口添加到您的业务域中。记住;如果你像你的例子那样做CRUD,那么你不是在做域驱动设计。你有一个贫血的领域。
最后一点:将接口用于实际需要互换的东西。您是否在应用程序中携带了许多可以互换的INamed
内容?
让我也链接这个,供您考虑: