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

Това е толкова прост и често срещан сценарий, че се чудя как успях досега и защо имам проблеми сега.

Имам този обект (част от сглобката на инфраструктурата)

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);

Нищо необичайно тук. Този обект обаче ще бъде изпратен в хранилище, където ще бъде запазен. Обектът Опашка ще поиска от хранилището елементи. Хранилището трябва да създаде отново обекти QueueItem.

Въпреки това, както виждате, свойствата на QueueItem са непроменливи, свойството AddedOn трябва да бъде зададено само веднъж, когато елементът е създаден. Свойството Id ще бъде зададено от обекта Queue (това не е важно).

Въпросът е как трябва да пресъздам QueueItem в хранилището? Мога да имам друг конструктор, който ще изисква всяка стойност за ВСИЧКИ свойства, но не искам този конструктор да е наличен за сборката, която първоначално ще създаде елемента на опашката. Хранилището е част от различна сборка, така че вътрешното няма да работи.

Мислех да осигуря фабричен метод class QueueItem { /* ..останалите дефиниции.. */

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

което поне изчиства намерението, но не знам защо не ми харесва този подход. Бих могъл също да наложа създаването на елемент само от Queue, но това означава да предам Queue като зависимост към repo, което отново не е нещо, което бих искал. Да имаш конкретен фабричен обект за това също изглежда прекалено много.

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

Актуализация

Важно е да се отбележи, че под хранилище имам предвид самия модел като абстракция, а не обвивка върху ORM. Няма значение как или къде се запазват обектите на домейна. Има значение как може да бъде пресъздадено от хранилището. Друго важно нещо е, че моят модел на домейн е различен от модела на постоянство. Използвам RDBMS, но мисля, че това е просто детайл на изпълнението, който не трябва да има никакво значение, тъй като търся начин, който не зависи от конкретен достъп до съхранение.

Въпреки че това е конкретен сценарий, той може да се приложи основно към всеки обект, който ще бъде възстановен от репото.

Актуализация 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 все още трябва да картографирам обекта orm към обекта на домейна. Актуализирах първоначалния си въпрос с това пояснение - person MikeSW; 06.04.2012
comment
Тогава предполагам, че имате само тези опции: вътрешна ключова дума + [InternalsVisibleTo(YourReposAssembly)] или Отражение или Restore() метод или подклас QueueItem в YourReposAssembly... - 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