Можно ли использовать NHibernate без изменения модели DDD, которая является частью структуры

Я много разбираюсь в подходе DDD (вездесущий язык, агрегаты, репозитории и т.д.), и я думаю, что, вопреки тому, что я читал много, сущности должны иметь поведение, а не будучи агностиком. Все примеры, которые я вижу, как правило, представляют сущности с виртуальными автоматическими свойствами и пустой конструктор (защищенный или худший, общедоступный) и его. Я рассматриваю такие объекты больше как DTO, а затем объекты.

Я выполняю создание фреймворка со своим конкретным API, и я не хочу быть привязан к ORM. Поэтому я сначала создал домен (не думая о постоянстве), и теперь я хотел бы использовать NHibernate в качестве инструмента для сохранения, поэтому я добавил новый проект в свое текущее решение, чтобы гарантировать, что моя модель не изменена для поддержки NHibernate. Этот проект должен быть реализацией абстрактных репозиториев, которые живут внутри моего домена. И теперь возникают трудности.

Так как это мой первый раз с NHibernate (я также пытаюсь использовать Fluent Nhibernate, но это кажется еще более ограничивающим), я хотел бы знать:

  • Можно ли использовать NHibernate без изменения DDD-модели, которая является частью структуры
  • Вещи (ограничения), необходимые для работы NHibernate как ожидаемые и эффективные (виртуальные свойства, пустые конструкторы и т.д.) Я думаю, что этот список был бы полезен для многих людей, которые начинают изучать NHibernate.

Пожалуйста, имейте в виду, что я создаю фреймворк, поэтому Open/Closed Principle для меня очень важен.

P.S.: Извините, если мой английский не очень хорош, я из Монреаля, и я говорю по-французски.

Изменить 1: Ниже приведена одна проблема с NHibernate - Как сопоставить тип с Nhibernate (и Fluent NHibernate) p >

Ответы

Ответ 1

Короткий ответ на ваш вопрос заключается в том, что это невозможно, но если вам не нужна ленивая загрузка, требуемые изменения тривиальны.

Независимо от того, у вас будут добавлены конструкторы по умолчанию для классов, которые их еще не имеют. Если вы готовы отказаться от ленивой загрузки, эти конструкторы по умолчанию могут быть частными, и вам не нужно делать какие-либо изменения в вашей модели домена для использования NHibernate.

Это ужасно близко к невежеству сохранения.

Сказав это, если вам нужна ленивая загрузка, вам нужно внести несколько изменений (в других ответах на этот вопрос), чтобы NHibernate мог создавать прокси-серверы ваших агрегированных объектов. Я лично все еще пытаюсь решить, является ли ленивая загрузка подходящей технологией для DDD или если это преждевременная оптимизация, которая требует слишком много навязчивых изменений в моих POCOs. Я склоняюсь к первому, хотя я действительно хочу, чтобы NHibernate мог быть настроен на использование конкретных конструкторов.

Вы также можете взглянуть на блог Дэви Бриона (мне особенно понравился Реализация объекта Value с NHibernate), который действительно освещает, если вы заинтересованы в разработке доменных технологий и избегаете моделей анемичных доменов.

Ответ 2

  • Для NHibernate:
    • Все сопоставленные классы требуют конструктора по умолчанию (без аргументов). Конструктор по умолчанию не должен быть общедоступным (он может быть закрытым, чтобы он не был частью API), но он должен существовать. Это связано с тем, что NHibernate должен иметь возможность создавать экземпляр отображаемого класса без передачи каких-либо аргументов. (Есть обходные пути, но не делайте этого.)
    • Все отображаемые свойства, для которых требуется ленивая загрузка, должны быть отмечены virtual. Сюда входят все ссылочные свойства и все свойства коллекции. Это связано с тем, что NHibernate должен иметь возможность генерировать прокси-класс, получающий отображаемый класс и переопределяющий сопоставленное свойство.
    • Все сопоставленные свойства коллекции должны использовать интерфейс как тип свойства. Например, используйте IList<T>, а не List<T>. Это связано с тем, что типы коллекций в .NET Framework имеют опечатку, и NHibernate должен иметь возможность заменить экземпляр по умолчанию типа коллекции своим собственным экземпляром типа коллекции, а NHibernate имеет свои собственные внутренние реализации типов коллекций.
    • Для NHibernate предпочитайте Iesi.Collections.Generic.ISet<T> - System.Collections.Generic.IList<T>, если вы не уверены, что то, что вы хотите, на самом деле является списком, а не набором. Для этого требуется знание в теоретических определениях списка и набора и в соответствии с требованиями вашей модели домена. Используйте список, когда знаете, что элементы должны быть в определенном порядке.

Также обратите внимание, что, как правило, не легко подменять объектно-реляционные структуры каркаса, и во многих случаях это невозможно, если у вас есть что-то за пределами тривиальной модели домена.

Ответ 3

По моему опыту, единственное, что NHibernate требует от домена, это виртуальные свойства и методы, а конструктор без аргументов по умолчанию, который, как упоминал Джефф, может быть отмечен как частный, так и защищенный, если потребуется. Это. NHibernate - мой выбор OR/M, и я считаю, что весь стек NHibernate (NHibernate, NHibernate Validator, Fluent NHibernate, LINQ to NHibernate) является наиболее привлекательной основой для сохранения доменов POCO.

Несколько вещей, которые вы можете сделать с NHibernate:

  • Украсьте свою модель домена атрибутами NHV. Эти константы позволяют делать три вещи: проверять свои объекты, гарантировать, что недопустимые объекты не сохраняются через NHibernate, и помочь автогенерировать вашу схему при использовании с помощью инструментов NHibernate SchemaExport или SchemaUpdate.
  • Сопоставьте модель своего домена с постоянным хранилищем с помощью Fluent NHibernate. Для меня главным преимуществом в использовании FNH является способность автоматически отображать объекты на основе установленных вами условностей. Кроме того, вы можете переопределить эти автоматы, где это необходимо, вручную написать карты классов, чтобы полностью контролировать сопоставления и использовать файлы hml hml, если вам нужно.
  • После покупки в NH вы можете легко использовать инструменты SchemaExport или SchemaUpdate для создания и выполнения DDL для вашей базы данных, что позволяет автоматически переносить изменения домена в вашу базу данных при инициализации сеанса NH factory. Это позволяет забыть о базе данных для всех целей и задач и сосредоточиться вместо этого на вашем домене. Обратите внимание, что это может быть не полезно или идеально во многих случаях, но для быстрой локальной разработки приложений, ориентированных на домен, я считаю это удобным.
  • Кроме того, мне нравится использовать общие репозитории для обработки сценариев CRUD. Например, у меня обычно есть IRepository, который определяет методы получения всех entites как IQueryable, единого объекта по id, для сохранения объекта и для удаления объекта. Что-то еще, NH предлагает богатый набор механизмов запросов - вы можете использовать LINQ to NHibernate, HQL, запросы Criteria и прямой SQL, если это необходимо.

Th только компромисс, который вы должны сделать, это использование атрибутов NHV в вашем домене. Для меня это не прерывание сделок, поскольку NHV - это автономная среда, которая добавляет дополнительные возможности, если вы решите использовать NHibernate.

Я создал несколько приложений, использующих NH, и у каждого из них есть постоянный неосведомленный домен со всеми проблемами сохранения, разделенными на собственную сборку. Это означает одну сборку для вашего домена, а другую для ваших плавных сопоставлений, управления сеансом и интеграции валидации. Это очень приятно и чисто и хорошо выполняет свою работу.

Кстати: ваш английский довольно хорош, я бы хотел, чтобы мой французский был в порядке;-).

Ответ 4

Просто чтобы поместить мои два бита, я боролся с одним и тем же, но я преодолел это:

  • Добавление защищенного конструктора по умолчанию для каждого объекта.
  • Создание идентификатора виртуального

Возьмем, например, upvote и downvote для объекта Vote на моем веб-сайте эксперимента: http://chucknorrisfacts.co.uk/ (NHibernate + MySQL с моно)

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 mapping, где вы Показать частную собственность.

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, чтобы использовать его, но я еще не изучил его.