Оптимальный способ восстановления объекта домена

Это такой простой и распространенный сценарий, что мне интересно, как я справлялся до сих пор и почему у меня сейчас проблемы.

У меня есть этот объект (часть сборки Infrastructure)

public class Queue {}

public class QueueItem
{
    public QueueItem(int blogId,string name,Type command,object data)
    {
        if (name == null) throw new ArgumentNullException("name");
        if (command == null) throw new ArgumentNullException("command");
        BlogId = blogId;
        CommandType = command;
        ParamValue = data;
        CommandName = name;
        AddedOn = DateTime.UtcNow;
    }


    public Guid Id { get; internal set; }
    public int BlogId { get; private set; }
    public string CommandName { get; set; }
    public Type CommandType { get; private set; }
    public object ParamValue { get; private set; }
    public DateTime AddedOn { get; private set; }
    public DateTime? ExecutedOn { get; private set; }
    public void ExecuteIn(ILifetimeScope ioc)
    {
        throw new NotImplementedException();
    }
}

Это будет создано в другой сборке, подобной этой

 var qi = new QueueItem(1,"myname",typeof(MyCommand),null);

Здесь нет ничего необычного. Однако этот объект будет отправлен в репозиторий, где он будет сохранен. Объект Queue будет запрашивать у репозитория элементы. Репозиторий должен заново создавать объекты QueueItem.

Однако, как видите, свойства QueueItem неизменны, свойство AddedOn должно быть задано только один раз при создании элемента. Свойство Id будет установлено объектом Queue (это не важно).

Вопрос в том, как мне воссоздать QueueItem в репозитории? У меня может быть другой конструктор, который потребует каждое значение для ВСЕХ свойств, но я не хочу, чтобы этот конструктор был доступен для сборки, которая первоначально создаст элемент очереди. Репозиторий является частью другой сборки, поэтому внутренний не будет работать.

Я подумал о предоставлении фабричного метода class QueueItem { /* ..остальные определения.. */

     public static QueueItem Restore(/* list of params*/){}
   }

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

По сути, мой вопрос заключается в следующем: какой оптимальный способ воссоздать объект в репозитории, не подвергая эту конкретную функцию создания другому потребительскому объекту.

Обновить

Важно отметить, что под репозиторием я подразумеваю сам шаблон как абстракцию, а не оболочку над ORM. Неважно, как и где сохраняются объекты домена. Имеет значение, как может быть воссоздан репозиторий. Еще одна важная вещь заключается в том, что моя модель домена отличается от модели постоянства. Я использую СУБД, но я думаю, что это просто деталь реализации, которая не должна иметь никакого значения, так как я ищу способ, который не зависит от конкретного доступа к хранилищу.

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

Обновление 2

Хорошо, я не знаю, как я мог забыть об AutoMapper. У меня сложилось неправильное впечатление, что он не может отображать частные поля/установщики, но может, и я думаю, что это лучшее решение.

На самом деле я могу сказать, что оптимальные решения (IMO) в порядке:

  1. Прямая десериализация, если она доступна.
  2. Автокарта.
  3. Фабричный метод самого объекта домена.

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

Ответ Обновлен

Чтобы ответить себе, в этом случае оптимальным способом является использование фабричного метода. Первоначально я выбрал Automapper, но чаще использовал фабричный метод. Иногда Automapper может быть полезен, но во многих случаях этого недостаточно.


person MikeSW    schedule 06.04.2012    source источник
comment
Немного не по теме, но если это часть вашего домена, вы можете рассмотреть такой инструмент, как NServiceBus или MassTransit.   -  person Josh Kodroff    schedule 28.12.2012


Ответы (2)


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

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

Вы можете ознакомиться с этим руководством: Создайте свой собственный уровень доступа к данным, хотя в большинстве случаев я бы не рекомендовал изобретать велосипед.

person guillaume31    schedule 06.04.2012
comment
Проблема не зависит от базы данных. Когда я сказал репозиторий, я имел в виду именно абстракцию. И даже с ORM мне все еще нужно сопоставить объект формы с объектом домена. Я обновил свой первоначальный вопрос с этим разъяснением - person MikeSW; 06.04.2012
comment
Тогда, я думаю, у вас есть только эти варианты: внутреннее ключевое слово + [InternalsVisibleTo(YourReposAssembly)] или Reflection или метод Restore() или подкласс QueueItem в Ваша сборка репозитория... - person guillaume31; 06.04.2012
comment
Спасибо за ваши предложения, но я думаю, что нашел лучший способ для меня. Я обновил вопрос. - person MikeSW; 06.04.2012

Вы говорили о фабричном методе самого объекта. Но DDD утверждает, что сущности должны создаваться фабрикой. Таким образом, у вас должна быть QueueItemFactory, которая может создавать новые QueueItems и восстанавливать существующие QueueItems.

Хорошо, я не знаю, как я мог забыть об AutoMapper.

Хотел бы я забыть об AutoMapper. От одного взгляда на отвратительный API у меня мурашки по спине.

person Jeroen    schedule 23.07.2012
comment
В этом случае фабрика является хорошим вариантом для СОЗДАНИЯ сущностей. Но здесь я хочу ВОССТАНОВИТЬ, и это ответственность репозитория. Я не думаю, что фабрика должна восстанавливать сущность. Существует несколько способов восстановления, и в настоящее время я предпочитаю метод статической фабрики из самой сущности. - person MikeSW; 23.07.2012
comment
Фактически репозиторий делегирует работу фабрике (инстанс тоже нужен, если вы хотите восстановить объект). Если вы будете выполнять CQRS, репозиторий, вероятно, делегирует работу чему-то, что воспроизводит события предметной области для этого объекта. Репозитории — это просто коллекция для хранения списка корней вашего агрегата. В вашем случае репозиторий будет иметь несколько разных обязанностей (т.е. причин для изменения). При этом большинство людей уже используют NHibernate; что делает ненужным репозиторий - person Jeroen; 24.07.2012
comment
Я не согласен. Репо может делегировать работу фабрике, но я не понимаю, почему фабрика должна обрабатывать восстановление, которое является концепцией сохранения. В лучшем случае фабричный метод объекта (или события в конструкторе), который повторно создаст объект с учетом состояния или потока событий. Репозиторий ПРОСМОТРЕН как коллекция, это фасад, и они делают гораздо больше, чем просто список. ORM — это деталь реализации DAL, которую может использовать репозиторий, но не более того. NH обрабатывает модель постоянства, репозиторий возвращает объекты домена. РАЗНЫЕ обязанности. - person MikeSW; 24.07.2012
comment
Роль фабрики — заменить вызов конструктора. Это не имеет ничего общего с тем, было ли оно уже сохранено или нет. Я не согласен с вашим утверждением, что ORM — это деталь реализации. Это не легкая библиотека, а очень тяжелая, которая может сильно повлиять на ваше приложение. Вы должны делать то, что имеет смысл для вас и вашей команды. Лично я считаю, что DDD в любом случае переоценен. - person Jeroen; 25.07.2012