域实体应该作为接口还是作为普通对象公开?

Yoa*_*. B 31 c# domain-driven-design

域实体应该作为接口还是作为普通对象公开?

用户界面:

public interface IUser
{
    string FirstName { get; set; }
    string LastName { get; set; }
    string Email { get; set; }
    Role Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

用户实现(实现到LinqToSql数据访问层):

public class User : IUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public Role Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

用户实现(实现到NHibernate数据访问层):

[NHibernate.Mapping.Attributes.Class]
public class User : IUser
{
    [NHibernate.Mapping.Attributes.Property]
    public string FirstName { get; set; }

    [NHibernate.Mapping.Attributes.Property]
    public string LastName { get; set; }

    [NHibernate.Mapping.Attributes.Property]
    public string Email { get; set; }

    [NHibernate.Mapping.Attributes.Property]
    public Role Role { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这仅说明了一些DAL特定的实现,此时没有更好的样本.

Aar*_*ght 27

我对此的感觉是域对象(不是域实体,因为该标题暗示与数据库有关)不应该是接口,除非你有一个非常令人信服的理由相信你需要在某些时候支持多个实现.未来.

考虑域模型是人体模型.业务/服务/文档实际上是域.我们大多数人都在为单一业务或目的开发软件.如果域模型发生更改,那是因为业务规则已更改,因此旧域模型不再有效 - 没有理由保留旧域模型,与新模型一起运行.

辩论显然不是黑白分明的.您可能正在开发在多个客户端站点上大量定制的软件.您可能真的需要同时实现不同的业务规则集,同时真正需要将它们融入统一的体系结构中.但是,根据我的经验,至少,这些案例是例外而不是规则,虽然我一般不喜欢这个词,但这可能是你应该自己思考的情况,YAGNI.

数据访问是一个常见的领域,您需要更好的抽象(持久性无知).在您的示例中,您的模型类具有NHibernate属性.但是添加持久性属性使它不再是真正的域类,因为它引入了对NHibernate的依赖.NHibernate和Fluent NHibernate支持使用外部映射声明而不是数据类上的属性来映射POCO,这在使用诸如NHibernate或EF4之类的ORM时往往是首选方法,因为它打破了持久性模型和域模型之间的依赖关系.

如果不支持这些映射方法,并且你必须使用属性,那么我可能确实建议使用接口,但今天的ORM比这更复杂,使用反射和动态代理和方法拦截来完成大部分繁重的工作,所以你不需要在这里创建自己的抽象.

有些类型的对象,你希望公开为接口包括:

  • 存储库,负责加载/保存域对象;
  • 程序的插件/扩展;
  • 查看/演示者模型,以便可以插入不同的UI;
  • 具有许多实现的抽象数据类型(数组,列表,字典,记录集和数据表都是序列AKA IEnumerable);
  • 具有许多可能算法的抽象操作(排序,搜索,比较);
  • 通信模型(通过TCP/IP进行相同操作,命名管道,RS-232);
  • 任何特定于平台的内容,如果您计划部署到多个(Mac/Windows/*nix).

这绝不是一个完整的列表,但它应该阐明这里的基本原则,即最适合接口抽象的东西是:

  1. 取决于可能无法控制的因素;
  2. 将来可能会发生变化; 和
  3. 是水平功能(用于应用程序/架构的许多部分).

域类将被广泛使用,但不适用于前两个类别中的任何一个; 它不太可能改变,你几乎可以完全控制设计.因此,除非类本身将采用间接依赖(这是您应尽可能避免的情况),否则我不会为域模型中的每个类创建一个接口.


Cyl*_*Cat 7

接口通常被认为是"合同",因此将定义行为.另一个主要用途是用于模拟,以便您可以提供模型化的域实体,而不是来自特定数据源(并且依赖于该源).

对于一个简单的数据传输对象,我看不到很多用于定义接口的东西,但我愿意被证明是错误的.我会选择简单的对象.