Почему бы не использовать контейнер IoC для разрешения зависимостей для сущностей / бизнес-объектов?


82

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

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

public class Order : IOrder
{

    private string _ShipAddress;
    private IShipQuoter _ShipQuoter;

    public Order(IOrderData OrderData, IShipQuoter ShipQuoter)
    {
        // OrderData comes from a repository and has the data needed 
        // to construct order
        _ShipAddress = OrderData.ShipAddress;  // etc.
        _ShipQuoter = ShipQuoter;

    }

    private decimal GetShippingRate()
    {
        return _ShipQuoter.GetRate(this);
    }
}

Как видите, зависимости введены конструктором. Теперь пара вопросов.

  1. Считается ли плохой практикой, чтобы ваши объекты зависели от внешних классов, таких как ShipQuoter? Если я правильно понимаю определение, устранение этих зависимостей ведет меня к анемичной области.

  2. Является ли плохой практикой использование контейнера IoC для разрешения этих зависимостей и создания объекта при необходимости? Можно ли это сделать?

Спасибо за понимание.


3
просто делайте то, что вам нужно, потому что это облегчает вашу работу, а не потому, что, вероятно, вы должны это делать,
Ому

28
Как программист-самоучка я пошел по этому пути, и это привело к программному обеспечению, которое в настоящее время используется в моей компании. Появилось программное обеспечение, основанное на сценариях процедур / транзакций, отчасти потому, что оно было самым простым, а отчасти потому, что я не знал ничего лучше. Поддерживать и расширять его абсолютно болезненно, поэтому я трачу время на его переписывание и ищу совета у людей, которые уже преодолели эти проблемы, о том, как не делать тех же ошибок.
Кейси Уилкинс

Ответы:


90

На первый вопрос ответить труднее всего. Это плохая практика, когда сущности зависят от внешних классов? Конечно, это не самое обычное дело.

Если, например, вы внедряете репозиторий в свои сущности, у вас фактически есть реализация шаблона Active Record . Некоторым людям нравится этот шаблон за удобство, которое он обеспечивает, в то время как другие (например, я) считают его запахом кода или анти-шаблоном, поскольку он нарушает принцип единой ответственности (SRP).

Вы могли бы возразить, что внедрение других зависимостей в Entities потянет вас в том же направлении (от SRP). С другой стороны, вы определенно правы в том, что, если вы этого не сделаете, тяга будет направлена ​​на модель анемической области .

Я долго боролся со всем этим, пока не наткнулся на (заброшенную) статью Грега Янга о DDDD, где он объясняет, почему стереотипная n-уровневая / n-слойная архитектура всегда будет CRUDy (и, следовательно, довольно анемичной).

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

На второй вопрос ответить легче. Вы всегда можете использовать абстрактную фабрику для создания экземпляров во время выполнения . С Castle Windsor вы даже можете использовать типизированную фабрику, избавляя вас от бремени создания фабрик вручную.


Спасибо, Марк. Я видел типизированную фабрику и читал другие ваши сообщения о методе абстрактной фабрики, но я никогда не видел примеров их использования для разрешения сущностей. Это потому, что большинство людей проектируют свои сущности без каких-либо зависимостей, кроме репозитория? Могу ли я столкнуться с проблемами в будущем, если буду строго использовать что-то вроде Typed Factory для разрешения своих сущностей, имеющих внешние зависимости?
Кейси Уилкинс

Я пытался сказать, что если ваши Сущности содержат других соавторов, которые могут получить доступ к другим Сущностям и т. Д., Вы можете столкнуться со всевозможными проблемами обслуживания - не говоря уже о нарушениях SRP и проблемах N + 1. Вот почему Эванс рекомендует рассматривать каждую сущность как совокупный корень.
Марк Зееманн

В моем примере ShipQuoter извлекает стоимость доставки для заказа из веб-службы (например, UPS). Сделали бы вы эту службу, которая принимает IOrder, или сделаете ее частью объекта домена, такого как Order.GetRates?
Кейси Уилкинс,

Я бы потратил много времени, выясняя, как вообще избежать синхронного натяжения. Чем больше вы извлекаете данные синхронно, блокирующим образом, тем более хрупким становится ваш дизайн. Вот почему CQRS - такая привлекательная альтернатива.
Марк Зееманн

4
Ваша ссылка на статью Грега мертва. Но он все еще доступен здесь . И похоже, что это более новая версия.
BornToCode

1

Я знаю, что это старый пост, но хотел добавить. Сущность домена не должна сохраняться, даже если вы передаете абстрактный репозиторий в ctor. Причина, по которой я предлагаю это, заключается не только в том, что это нарушает SRP, это также противоречит агрегированию DDD. Позвольте мне пояснить, что DDD подходит для сложных приложений с глубокими графами, поэтому мы используем агрегированные или составные корни для сохранения изменений в базовых «дочерних элементах», поэтому, когда мы вводим настойчивость в отдельные дочерние элементы, мы нарушаем отношения, которые дети имеют с составной или совокупный корень, который должен «отвечать» за жизненный цикл или агрегацию. Конечно, составной корень или агрегат также не сохраняет свой собственный граф. Другой - с внедрением зависимостей объектов DDD, заключается в том, что внедренный объект домена фактически не имеет состояния, пока не произойдет какое-либо другое событие для гидратации его состояния. Любой потребитель кода будет вынужден сначала инициализировать или настроить объект домена, прежде чем он сможет вызвать бизнес-поведение, которое нарушает инкапсуляцию.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.