2

Я экспериментирую с разработкой и созданием событий под управлением домена. Я планирую использовать (создание в C#) NServiceBus, EventStore и NES JOliver для их связывания. У меня уже есть инфраструктура, работающая для простого случая (один агрегированный корень со значениями только объектов).DDD: наследование и транзакции

Я читаю синюю книгу Эванса, и я пытаюсь разработать простую модель домена с примерами, взятыми из моего рабочего поля (ERP и CRM для компаний по обслуживанию HVAC).

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

Первый результат моего анализа состоит в том, что каждый компьютер должен быть совокупным корнем (который наследуется от AggregateBase в NES), поскольку он должен содержать ссылки на конкретную машину (например, для вставки записей восстановления, которые включают в себя одну машину , запись неисправностей и т. д.), а также уменьшить проблемы параллелизма в больших машинных деревьях.

Моя гипотеза состоит в том, таким образом, следующее:

public class Machine : AggregateBase 
{ 
    public DateTime InstallationDate { get; private set; } 

    public Guid ManufacturerId { get; private set; } 

    public Guid ModelId { get; private set; } 
} 

public class Furnace : Machine 
{ 
    public List<Burner> burners { get; private set; } 

    // other furnace properties 

    public void AddBurner(Burner burner) 
    { 
     // perform validation 
     this.Apply<BurnerAdded>(x=> x.burnerAdded = burner); 
    } 

    public void Handle(BurnerAdded @event) 
    { 
     this.burners.Add(@event.burnerAdded); 
    } 
} 

public class Burner : Machine 
{ 
    // burner specific properties/methods 
} 

, но у меня есть некоторые сомнения:

  1. Является ли это правильный путь, чтобы представить свой домен? Я читал, что наследование класса не рекомендуется, но мне кажется, что это идеальный случай для его использования (Burner IS A Machine, также как и печь). Я ограничу только один уровень наследования.

  2. Возможно ли реализовать наследование классов с помощью Event Sourcing? С предлагаемым технологическим стеком, в частности (nServiceBus, EventStore, NES)?

  3. Как я могу выполнить добавление детской машины (например, горелки в печь)? Эту операцию можно разделить на две части:

    1. Добавить новый Burner в репозиторий Burner.
    2. Добавить ссылку на горелку в список горелок родительской печи Но эти две операции глобально изменяют два совокупных корня, поэтому операция должна выполняться двумя отдельными манипуляторами/транзакциями ... но вторая зависит от первого ... является ли это свидетельством некоторой ошибки моделирования? Я мог бы партии nServicebusMessages вместе, чтобы выполнить действия в одной транзакции, но я читал, что это не хорошо ...

    Если я ребенок машина ссылаться на родителя, родитель теряет список детских машин (которые требуется для проверки), я не могу запросить репозиторий источников событий для других свойств, кроме Guid.

Спасибо заранее за любой вклад в обсуждение,

ответ

3

Для # 1, я бы сказал, нет. Совокупный корень в вашем примере должен быть только Печь (только). Горелка должна быть смоделирована как коллекция на Печи, как у вас в настоящее время, но не должна быть совокупным корнем.Я не считаю наследование проблемой как таковой, за исключением того факта, что ваша горелка теперь является совокупным корнем через наследование (поскольку Burner => Machine => AggregateBase).

Если горелки существуют только в контексте Печей, вам, вероятно, не нужен репозиторий горелки - вы всегда будете добавлять горелку в печь. Я не понимаю, интересно ли создание новой горелки и ее собственное событие. Ответ на этот вопрос подскажет, нужны ли вам оба события или просто событие «Добавлено».

+0

благодарит за вашу помощь. Дело в том, что горелка существует независимо от печи, это всего лишь компонент, который может быть переключен между печами (так что он определенно имеет идентичность), и на которые могут ссылаться непосредственно другие объекты домена (например, служебные инциденты) – Max

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

  2. Возможность использования наследования класса с ES ограничена только сериализатором. ES не определяет, как данные сериализуются, но если вы используете что-то вроде Protobuf или Newtonsoft.Json, они поддерживают наследование. Однако в случае JSON использование наследования не содержит атрибутов типа $ в выходе JSON.

  3. Это зависит от того, должна ли Горелка существовать независимо от Печи. Если нет, то это объект стоимости или сущность агрегата Furnace и должен быть полностью сохранен с Печью. Если да, то он должен быть агрегатом и должен быть сначала создан, а затем добавлен в печь. Это может быть реализовано с помощью саги NSerivceBus, в которой AddBurnerToFurnaceCommand обрабатывается путем первого запуска соответствующей саги, которая отправляет команду для создания Burner. После создания Burner создайте связь между Burner и Furnace. Печь будет просто ссылаться на Burner by Guid. В ES все запросы обычно обрабатываются с помощью прогнозов, и только поведение вызывается хранилищем событий для канонического агрегата по идентификатору.

+0

спасибо для вашей помощи. Да, горелка существует независимо от печи, это всего лишь компонент, который может быть переключен между печами (так что он определенно имеет личность), и на которые могут ссылаться непосредственно другие объекты домена (например, служебные инциденты) – Max

Смежные вопросы