是否可以在不改变作为框架一部分的DDD模型的情况下使用NHibernate

时间:2009-10-03 17:02:30

标签: nhibernate fluent-nhibernate frameworks domain-driven-design

我挖掘了很多关于 DDD方法(无处不在的语言,聚合,存储库等)的东西,我认为,与我经常阅读的内容相反,实体应该有行为而不是不可知论者。我看到的所有示例都倾向于使用虚拟自动属性空构造函数(受保护或最差,公共)呈现实体,就是这样。我认为这种对象更像是DTO然后是实体。

我正在使用其特定的API 创建框架,我不想绑定到ORM 。所以我首先构建了域(不考虑持久性),现在我想使用NHibernate作为持久性工具,所以我在当前的解决方案中添加了一个新项目,以帮助确保我的模型不会被改变以支持NHibernate。该项目应该是我域内的抽象存储库的实现。现在出现了困难。

因为这是我第一次使用NHibernate (我也尝试使用Fluent Nhibernate,但似乎更有限制)我想知道:

  1. 是否可以在不改变作为框架一部分的DDD模型的情况下使用NHibernate
  2. NHibernate按预期和有效工作所必需的东西(约束)(虚拟属性,空构造函数等)我认为这个列表对很多开始学习NHibernate的人都有帮助。
  3. 请记住,我正在构建一个框架,因此Open/Closed Principle对我来说非常重要。

    P.S。:对不起,如果我的英语不好,我来自蒙特利尔,我说法语。

    编辑1:以下是我对NHibernate的一个问题 - How to map Type with Nhibernate (and Fluent NHibernate)

4 个答案:

答案 0 :(得分:9)

  • 对于NHibernate:
    • 所有映射类都需要默认(无参数)构造函数。默认构造函数不必是公共的(它可以是私有的,因此它不是API的一部分),但它必须存在。这是因为NHibernate必须能够创建映射类的实例而不传递任何参数。 (有解决方法,但不要这样做。)
    • 所有需要延迟加载的映射属性必须标记为virtual。这包括所有引用属性和所有集合属性。这是因为NHibernate必须能够生成派生映射类并覆盖映射属性的代理类。
    • 所有映射的集合属性都应使用接口作为属性类型。例如,使用IList<T>而不是List<T>。这是因为.NET Framework中的集合类型往往是密封的,并且NHibernate必须能够用它自己的集合类型实例替换集合类型的默认实例,并且NHibernate有自己的集合类型的内部实现
    • 对于NHibernate,请更喜欢Iesi.Collections.Generic.ISet<T>System.Collections.Generic.IList<T>,除非您确定所需内容实际上是列表而不是 set 。这需要熟悉 list set 的理论定义以及您的域模型所需的内容。当您知道元素必须按特定顺序排列时,请使用列表。

另请注意,交换对象关系映射框架通常并不容易,并且在很多情况下,当您有超出简单域模型的任何内容时,这是不可能的。

答案 1 :(得分:4)

对你的问题的简短回答是,这是不可能的,但如果不需要延迟加载,则所需的修改是微不足道的。

无论如何,您都会将默认构造函数添加到尚未拥有它们的类中。如果您愿意放弃延迟加载,那些默认构造函数可以是私有的,并且您不必对域模型进行任何其他更改即可使用NHibernate。

这非常接近持久性的无知。

话虽如此,如果你想要延迟加载,你需要进行一些更改(在这个问题的其他答案中概述),以便NHibernate可以创建聚合实体的代理。我个人仍然试图决定延迟加载是否是DDD的支持技术,或者是否是过早的优化需要对我的POCO进行太多的侵入式更改。我倾向于前者,但我真的希望NHibernate可以配置为使用特定的构造函数。

你也可以看看Davy Brion的博客(我特别喜欢Implementing A Value Object With NHibernate),如果你对领域驱动设计和避免贫血领域模型感兴趣,这真的很有启发性。

答案 2 :(得分:4)

根据我的经验,NHibernate对域的唯一要求是虚拟属性和方法以及默认的无参数构造函数,如Jeff所述,如果需要,可以将其标记为私有或受保护。而已。 NHibernate是我的首选OR / M,我发现整个NHibernate堆栈(NHibernate,NHibernate Validator,Fluent NHibernate,LINQ to NHibernate)是持久存储POCO域的最引人注目的框架。

使用NHibernate可以做的一些事情:

  • 使用NHV属性装饰您的域模型。这些constaints允许您做三件事:验证您的对象,确保无法通过NHibernate持久化实体,并在使用NHibernate的SchemaExport或SchemaUpdate工具时帮助自动生成模式。
  • 使用Fluent NHibernate将您的域模型映射到持久存储。对我来说,使用FNH的主要优点是能够根据您设置的约定自动映射您的实体。另外,您可以在必要时覆盖这些自动化,手动编写类映射以完全控制映射,并在需要时使用xml hbm文件。
  • 购买NH后,您可以轻松使用SchemaExport或SchemaUpdate工具为数据库创建和执行DDL,从而允许您在启动NH会话工厂时自动将域更改迁移到数据库。这允许您忘记数据库,出于所有意图和目的,而是集中精力于您的域。请注意,这在许多情况下可能没有用或理想,但为了快速,本地开发以域为中心的应用程序,我觉得很方便。
  • 此外,我喜欢使用通用存储库来处理CRUD方案。例如,我通常有一个IRepository,它定义了将所有entites作为IQueryable,通过id获取单个实体,用于保存实体和删除实体的方法。对于其他任何事情,NH提供了丰富的查询机制 - 如果需要,您可以使用LINQ to NHibernate,HQL,Criteria查询和直接SQL。

您必须做出的唯一妥协是在您的域中使用NHV属性。这对我来说不是一个交易破坏者,因为NHV是一个独立的框架,如果你选择使用NHibernate,它会增加额外的功能。

我已经使用NH构建了一些应用程序,每个应用程序都有一个持久性无知域,所有持久性问题都分成了自己的程序集。这意味着您的域的一个程序集,另一个用于流畅的映射,会话管理和验证集成。它非常干净整洁,能很好地完成工作。

顺便说一句:你的英语非常好,我希望我的法语达到标准杆;-)。

答案 3 :(得分:0)

只是为了把我的两个位置,我曾经用同样的东西挣扎但是我克服了这个:

  1. 将保护的默认构造函数添加到每个实体。
  2. 让Id虚拟
  3. 让我们在我的实验网站上为投票实体提供upvote和downvote: http://chucknorrisfacts.co.uk/(NHibernate + MySQL和Mono)

    public class Vote : Entity
    {
        private User _user;
        private Fact _fact;
        // true: upvote, false: downvote
        private bool _isupvoted;
    
        // for nHibernate
        protected Vote() { }
    
        public Vote(User user, Fact fact, bool is_upvoted)
        {
            Validator.NotNull(user, "user is required.");
            Validator.NotNull(fact, "fact is required.");
    
            _fact= fact;
            _user = user;
            _isupvoted = is_upvoted;
        }
    
        public User User
        {
            get { return _user; }
        }
        public Fact Fact
        {
            get { return _fact; }
        }       
        public bool Isupvoted
        {
            get { return _isupvoted; }
        }
    }
    

    这个类继承自 Entity ,其中我们坚持Nhibernate所需的所有最小值。

    public abstract class Entity
    {
       protected int _id;
       public virtual int Id { get {return _id;} }
    }
    

    和Fluent映射您显示私有财产的位置。

    public class VoteMap : ClassMap<Vote>
    {
        public VoteMap()
        {
           DynamicUpdate();
    
           Table("vote");           
           Id(x => x.Id).Column("id");
    
           Map(Reveal.Member<Vote>("_isupvoted")).Column("vote_up_down");
           References(x => x.Fact).Column("fact_id").Not.Nullable();
           References(x => x.User).Column("user_id").Not.Nullable();
        }
    }
    

    您可以将受保护的默认构造函数放在Entity类中,并将nHibernate配置为使用它,但我还没有查看它。