DDD – правило, что объекты не могут напрямую обращаться к репозиториям

В Domain Driven Design, похоже, существует много соглашений о том, что Entities не должны напрямую обращаться к репозиториям.

Это было сделано из книги Эрика Эванса по управлению доменами , или она исходила из других источников?

Где есть хорошие объяснения причин этого?

edit: Чтобы уточнить: я не говорю о classической практике OO для разделения доступа к данным на отдельный уровень из бизнес-логики – я говорю о конкретной договоренности, в которой в DDD сущности не должны разговаривать с данными (т.е. они не должны содержать ссылки на объекты репозитория)

Обновление: я дал щедрость BacceSR, потому что его ответ казался самым близким, но я все еще в темноте об этом. Если это такой важный принцип, должны быть какие-то хорошие статьи об этом в Интернете, конечно?

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

    Здесь немного путаницы. Репозитории доступа к совокупным корням. Совокупные корни – это сущности. Причиной этого является разделение проблем и хорошее расслоение. Это маловероятно для небольших проектов, но если вы работаете в большой команде, вы хотите сказать: «Вы получаете доступ к продукту через repository продуктов. Продукт представляет собой совокупный корень для коллекции объектов, включая объект ProductCatalog. Если вы хотите обновить ProductCatalog, вы должны пройти через ProductRepository. ”

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

    Но ждать! Репозиторий также ссылается на уровень персистентности, как в шаблоне репозитория. В лучшем мире repository Эрика Эванса и шаблон хранилища будут иметь отдельные имена, потому что они имеют тенденцию пересекаться совсем немного. Чтобы получить шаблон репозитория, вы контрастируете с другими способами доступа к данным, с служебной шиной или системой модели событий. Обычно, когда вы достигаете этого уровня, определение репозитория Эрика Эванса идет по пути, и вы начинаете говорить о ограниченном контексте. Каждый ограниченный контекст, по сути, является его собственным приложением. У вас может быть сложная система одобрения для получения информации в каталоге продуктов. В вашем оригинальном дизайне продукт был центральным элементом, но в этом ограниченном контексте каталог продукции. Вы по-прежнему можете получить доступ к информации о продукте и обновить продукт через служебную шину, но вы должны понимать, что каталог продуктов за пределами ограниченного контекста может означать нечто совершенно иное.

    Вернемся к исходному вопросу. Если вы обращаетесь к репозиторию изнутри сущности, это означает, что сущность действительно не является бизнес-объектом, а, вероятно, тем, что должно существовать на уровне службы. Это связано с тем, что объекты являются бизнес-объектом и должны заботиться о том, чтобы быть максимально похожим на DSL (доменный язык). Имеет только бизнес-информацию на этом уровне. Если вы устраняете проблему с производительностью, вы будете знать, что смотрите в другом месте, поскольку здесь должна быть только информация о бизнесе. Если вдруг у вас возникнут проблемы с приложениями, вам очень сложно расширить и поддерживать приложение, которое действительно является сердцем DDD: создание поддерживающего программного обеспечения.

    Ответ на комментарий 1 : Правильный, хороший вопрос. Поэтому не все проверки выполняются в доменном слое. У Sharp есть атрибут «DomainSignature», который делает то, что вы хотите. Это постоянство, но атрибут сохраняет уровень домена чистым. Это гарантирует, что у вас нет дубликата сущности, в вашем примере с тем же именем.

    Но давайте поговорим о более сложных правилах проверки. Допустим, вы Amazon.com. Вы когда-нибудь заказывали что-то с истекшей кредитной картой? У меня есть, где я не обновил карту и что-то купил. Он принимает заказ, и пользовательский интерфейс сообщает мне, что все персиковое. Примерно через 15 минут я получу сообщение по электронной почте, в котором говорится о проблеме с моим заказом, моя кредитная карта недействительна. Что здесь происходит, так это, в идеале, есть некоторая проверка регулярного выражения на уровне домена. Это правильный номер кредитной карты? Если да, сохраните заказ. Тем не менее, есть дополнительная проверка на уровне задач приложений, где запрашивается внешняя служба, чтобы узнать, можно ли произвести платеж на кредитной карте. Если нет, не отправляйте ничего, приостанавливайте заказ и ждите клиента. Все это должно происходить на сервисном уровне.

    Не бойтесь создавать объекты проверки на уровне службы, которые могут обращаться к репозиториям. Просто держите его вне домена.

    Это очень хороший вопрос. Я буду с нетерпением ждать обсуждения этого вопроса. Но я думаю, что это упоминается в нескольких книгах DDD и Jimmy nilssons и Eric Evans. Я думаю, это также видно из примеров, как использовать шаблон репозитория.

    НО позволяет обсуждать. Я думаю, что очень важная мысль заключается в том, почему организация должна знать, как сохранить другую сущность? Важно с DDD, что каждый субъект несет ответственность за управление собственной «областью знаний» и не должен знать ничего о том, как читать или писать другие объекты. Конечно, вы можете просто добавить интерфейс репозитория к Entity A для чтения Entities B. Но риск в том, что вы раскрываете знания о том, как сохранить B. Будет ли сущность A также выполнять валидацию на B до того, как останется B в db?

    Как вы можете видеть, сущность A может более активно участвовать в жизненном цикле объекта B и может добавить к модели большую сложность.

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

    Но я уверен, что всегда будут сценарии, когда вы соблазняетесь использовать репозитории через сущности. Вы должны посмотреть на каждый сценарий, чтобы принять правильное решение. За и против. Но решение репозитория-сущности, на мой взгляд, начинается с множества недостатков. Это должен быть очень особенный сценарий с профессионалами, которые балансируют минусы ….

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

    1. Мы должны знать наши намерения в запросе и то, что мы хотим от домена, поэтому мы можем делать вызовы репозитория перед конструированием или вызовом поведения Aggregate. Это также помогает избежать проблемы несогласованного состояния в памяти и необходимости ленивой загрузки (см. Эту статью ). Запах заключается в том, что вы больше не можете создавать экземпляр в памяти вашего объекта, не беспокоясь о доступе к данным.
    2. CQS (разделение командной строки) может помочь уменьшить необходимость в том, чтобы вызывать repository для вещей в наших сущностях.
    3. Мы можем использовать спецификацию для инкапсуляции и передачи запросов логики домена и передачи их в хранилище (услуга может организовать для нас эти вещи). Спецификация может исходить от объекта, который отвечает за сохранение этого инварианта. Репозиторий интерпретирует части спецификации в своей собственной реализации запроса и применяет правила из спецификации результатов запроса. Это делается для сохранения логики домена в доменном слое. Он также лучше обслуживает Вездесущий язык и общение. Представьте себе «просроченную спецификацию заказа» и произнесите «порядок фильтрации от tbl_order, где place_at меньше, чем за 30 минут до sysdate» (см. Этот ответ ).
    4. Это приводит к тому, что поведение сущностей более сложно, поскольку принцип единой ответственности нарушен. Если вам нужно решить проблемы хранения / сохранения, вы знаете, куда идти и куда не идти.
    5. Это позволяет избежать угрозы двунаправленного доступа объекта к глобальному состоянию (через repository и службы домена). Вы также не хотите нарушать границу транзакции.

    Вернон Вон в красной книге Реализация проекта, управляемого доменом, относится к этой проблеме в двух местах, о которых я знаю (примечание: эта книга полностью одобрена Эвансом, как вы можете прочитать в предисловии). В главе 7 «Службы» он использует службу домена и спецификацию, чтобы обойти необходимость того, чтобы агрегат использовал repository и другой агрегат для определения подлинности пользователя. Он цитирует:

    Как правило, мы должны стараться избегать использования репозиториев (12) изнутри Агрегатов, если это вообще возможно.

    Вернон, Вон (2013-02-06). Внедрение управления доменом (Kindle Location 6089). Образование Пирсона. Kindle Edition.

    И в главе 10 «Агрегаты» в разделе «Модельная навигация» он говорит (сразу после того, как он рекомендует использовать глобальные уникальные идентификаторы для ссылок на другие совокупные корни):

    Ссылка на личность не полностью предотвращает навигацию по модели. Некоторые из них будут использовать repository (12) изнутри совокупности для поиска. Этот метод называется Disconnected Domain Model, и на самом деле это форма ленивой загрузки. Однако существует другой рекомендуемый подход: используйте repository или службу домена (7) для поиска зависимых объектов перед вызовом поведения агрегата. Клиентская служба приложений может контролировать это, а затем отправлять в агрегат:

    Он показывает пример этого в коде:

    public class ProductBacklogItemService ... { ... @Transactional public void assignTeamMemberToTask( String aTenantId, String aBacklogItemId, String aTaskId, String aTeamMemberId) { BacklogItem backlogItem = backlogItemRepository.backlogItemOfId( new TenantId( aTenantId), new BacklogItemId( aBacklogItemId)); Team ofTeam = teamRepository.teamOfId( backlogItem.tenantId(), backlogItem.teamId()); backlogItem.assignTeamMemberToTask( new TeamMemberId( aTeamMemberId), ofTeam, new TaskId( aTaskId)); } ... } 

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

    Затем я провел некоторое обсуждение с всегда благодатным Марко Пиветтой @ Окрамиус, который показал мне немного кода, вытаскивая спецификацию из домена и используя это:

    1) Это не рекомендуется:

     $user->mountFriends(); // <-- has a repository call inside that loads friends? 

    2) В службе домена это хорошо:

     public function mountYourFriends(MountFriendsCommand $mount) { /* see http://store.steampowered.com/app/296470/ */ $user = $this->users->get($mount->userId()); $friends = $this->users->findBySpecification($user->getFriendsSpecification()); array_map([$user, 'mount'], $friends); } 

    Я нашел в этом блоге неплохие аргументы против инкапсуляции репозиториев внутри Entities:

    http://thinkbeforecoding.com/post/2009/03/04/How-not-to-inject-services-in-entities

    Зачем выделять доступ к данным?

    Из книги я думаю, что первые две страницы главы Model Driven Design дают некоторое обоснование, почему вы хотите абстрагировать детали технической реализации от реализации модели домена.

    • Вы хотите поддерживать тесное соединение между моделью домена и кодом
    • Разделение технических проблем помогает доказать, что модель практична для реализации
    • Вы хотите, чтобы вездесущий язык проникал в дизайн системы

    Кажется, это все с целью избежать отдельной «модели анализа», которая оторвалась от фактической реализации системы.

    Из того, что я понимаю в книге, говорится, что эта «модель анализа» может быть разработана без учета внедрения программного обеспечения. Когда разработчики пытаются внедрить модель, понятную бизнес-стороне, они создают свои собственные абстракции из-за необходимости, вызывая стену в общении и понимании.

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

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

    Цитирование:

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

    То, как я интерпретирую это, если у вас больше строк кода, связанных с вещами, такими как доступ к базе данных, вы теряете эту коммуникативность.

    Если необходимость в доступе к базе данных для таких вещей, как проверка уникальности, взгляните на:

    Udi Dahan: самые большие ошибки команды при применении DDD

    http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/

    в разделе «Все правила не созданы равными»

    а также

    Использование шаблона модели домена

    http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119

    в разделе «Сценарии не использования модели домена», в котором затрагивается один и тот же вопрос.

    Как отделить доступ к данным

    Загрузка данных через интерфейс

    «Уровень доступа к данным» был абстрагирован через интерфейс, который вы вызываете для извлечения необходимых данных:

     var orderLines = OrderRepository.GetOrderLines(orderId); foreach (var line in orderLines) { total += line.Price; } 

    Плюсы: интерфейс разделяет «сантехнический» код доступа к данным, позволяя вам писать тесты. Доступ к данным можно обрабатывать в каждом конкретном случае, обеспечивая лучшую производительность, чем общая страtagsя.

    Минусы: код вызова должен предполагать, что было загружено, а что нет.

    Скажем, GetOrderLines возвращает объекты OrderLine с нулевым свойством ProductInfo по соображениям производительности. Разработчик должен иметь глубокое знание кода, лежащего за интерфейсом.

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

    Теперь разделение проблем должно позволить разработчику сосредоточиться на одном аспекте кода за один раз, насколько это возможно. Методика интерфейса удаляет HOW, загружаются ли эти данные, но не КАК MUCH данные загружаются, КОГДА загружается, и WHERE загружается.

    Заключение: Довольно слабое разделение!

    Ленивая загрузка

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

     foreach (var line in order.OrderLines) { total += line.Price; } 

    Плюсы: «КОГДА, ГДЕ И КАК» доступа к данным скрывается от разработчика, ориентированного на логику домена. В агрегате нет кода, связанного с загрузкой данных. Объем загружаемых данных может быть точной величиной, требуемой кодом.

    Минусы: когда вы сталкиваетесь с проблемой производительности, ее трудно исправить, если у вас есть общее решение «один размер подходит всем». Ленивая загрузка может привести к ухудшению производительности в целом, и реализация ленивой загрузки может быть сложной.

    Ролевой интерфейс / нетерпеливый выбор

    Каждый случай использования становится явным через интерфейс ролей, реализованный агрегированным classом, позволяя обрабатывать страtagsи загрузки данных в каждом случае.

    Страtagsя выбора может выглядеть так:

     public class BillOrderFetchingStrategy : ILoadDataFor { Order Load(string aggregateId) { var order = new Order(); order.Data = GetOrderLinesWithPrice(aggregateId); return order; } } 

    Тогда ваш агрегат может выглядеть так:

     public class Order : IBillOrder { void BillOrder(BillOrderCommand command) { foreach (var line in this.Data.OrderLines) { total += line.Price; } etc... } } 

    BillOrderFetchingStrategy используется для создания агрегата, а затем агрегат выполняет свою работу.

    Плюсы: позволяет настраивать код для одного варианта использования, обеспечивая оптимальную производительность. Является встроенным с принципом разделения интерфейса . Нет сложных требований к коду. Агрегаты единичных тестов не должны имитировать страtagsю загрузки. Общая страtagsя загрузки может использоваться в большинстве случаев (например, страtagsя «загрузить все»), и при необходимости могут быть реализованы специальные страtagsи загрузки.

    Минусы: разработчику по-прежнему приходится корректировать / пересматривать страtagsю выбора после изменения кода домена.

    С помощью страtagsи выборки вы все равно можете изменить свой собственный код для изменения бизнес-правил. Это не идеальное разделение проблем, но в конечном итоге будет более ремонтопригодным и лучше, чем первый вариант. Страtagsя выборки инкапсулирует данные HOW, WHEN и WHERE. Он имеет лучшее разделение проблем, не теряя гибкости, как один размер, подходит для всего ленивого подхода к загрузке.

    Какой прекрасный вопрос. Я нахожусь на том же пути открытия, и большинство ответов по всему Интернету, похоже, приносят столько же проблем, сколько они приносят решения.

    Так что (рискуя написать что-то, с чем я не согласен с годом позже), вот мои открытия до сих пор.

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

     // Entity public class Invoice { ... public void SetStatus(StatusCode statusCode, DateTime dateTime) { ... } public void CreateCreditNote(decimal amount) { ... } ... } 

    Мы хотим достичь этого, не вставляя какие-либо службы в конструктор объекта, потому что:

    • Внедрение нового поведения (использующего новую службу) может привести к изменению конструктора, что означает, что изменение влияет на каждую строку, которая создает экземпляр объекта !
    • Эти службы не являются частью модели , но конструктор-инъекция предполагает, что они были.
    • Часто служба (даже ее интерфейс) представляет собой деталь реализации, а не часть домена. Модель домена будет иметь внешнюю зависимость .
    • Это может сбить с толку, почему сущность не может существовать без этих зависимостей. (Слушайте кредитную заметку, вы говорите? Я даже не собираюсь ничего делать с кредитными нотами …)
    • Это затруднило бы его создание, поэтому его трудно было бы проверить .
    • Проблема распространяется легко, потому что другие объекты, содержащие этот, получат те же зависимости, которые на них могут выглядеть как очень неестественные зависимости .

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

     public class Invoice { ... // Simple method injection public void SetStatus(IInvoiceLogger logger, StatusCode statusCode, DateTime dateTime) { ... } // Double dispatch public void CreateCreditNote(ICreditNoteService creditNoteService, decimal amount) { creditNoteService.CreateCreditNote(this, amount); } ... } 

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

    SetStatus() теперь имеет простую зависимость от регистратора, который, очевидно, выполнит часть работы .

    Для последнего, чтобы упростить работу с клиентским кодом, мы могли бы зарегистрироваться через IInvoiceService . В конце концов, регистрация счетов-фактур кажется довольно неотъемлемой частью счета-фактуры. Такой единый IInvoiceService помогает избежать необходимости использования всех видов мини-сервисов для различных операций. Недостатком является то, что становится неясным, что именно будет делать эта услуга. Это может даже начать выглядеть как двойная отправка, в то время как большая часть работы действительно выполняется в SetStatus() .

    Мы все еще можем назвать параметр «logger», в надежде выявить наши намерения. Кажется немного слабым.

    Вместо этого я бы предпочел попросить IInvoiceLogger (как мы уже делали в примере кода) и реализовал интерфейс IInvoiceService . Клиентский код может просто использовать свой единственный IInvoiceService для всех IInvoiceService который запрашивает какой-либо такой специфический «мини-сервис» с инвойсом-фактурой, в то время как подписи методов все еще четко показывают, что они просят.

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

     public class Invoice { public IEnumerable GetCreditNotes(ICreditNoteRepository repository) { ... } } 

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

    Обновление: я оставил текст ниже для исторических целей, но я предлагаю избегать ленивых нагрузок на 100%.

    Для истинных, основанных на свойствах ленивых нагрузок, в настоящее время я использую инсталляцию конструктора, но с непреодолимым упорством.

     public class Invoice { // Lazy could use an interface (for contravariance if nothing else), but I digress public Lazy> CreditNotes { get; } // Give me something that will provide my credit notes public Invoice(Func> lazyCreditNotes) { this.CreditNotes = new Lazy>() => lazyCreditNotes(this)); } } 

    С одной стороны, repository, который загружает Invoice из базы данных, может иметь свободный доступ к функции, которая будет загружать соответствующие кредитные заметки, и вводить эту функцию в Invoice .

    С другой стороны, код, который создает фактический новый Invoice , просто передаст функцию, которая возвращает пустой список:

     new Invoice(inv => new List() as IEnumerable) 

    (Пользовательский ILazy может избавить нас от уродливого приведения к IEnumerable , но это усложнит обсуждение.)

     // Or just an empty IEnumerable new Invoice(inv => IEnumerable.Empty()) 

    Я был бы рад услышать ваши мнения, предпочтения и улучшения!

    Для меня это, как представляется, является общей хорошей практикой, связанной с ООП, а не спецификой DDD.

    Причины, о которых я могу думать, это:

    • Разделение проблем (сущности должны быть отделены от того, как они сохраняются, поскольку могут существовать несколько страtagsй, в которых один и тот же объект будет сохраняться в зависимости от сценария использования)
    • Логически, сущности можно увидеть на уровне ниже уровня, в котором работают репозитории. Компоненты нижнего уровня не должны иметь знаний о компонентах более высокого уровня. Поэтому записи не должны иметь знаний о репозиториях.

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

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

    Я думаю, что разделение доступа к данным (aka «Репозиторий») из вашей бизнес-логики является одной из тех вещей, которые были заново изобрещены несколько раз, задумались над книгой Design Driven Design, сделавшей ее «шумной».

    В настоящее время я использую 3 уровня (GUI, Logic, Data Access), как и многие разработчики, потому что это хорошая техника.

    Разделение данных на уровень Repository (например, уровень Data Access ) можно рассматривать как хорошую технику программирования, а не просто правило.

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

    Цитата: «Илиада» не была полностью изобретена Гомером, Кармина Бурана не была полностью изобретена Карлом Орфом, и в обоих случаях человек, который заставлял других работать, все торгал, получил кредит 😉

    Это было сделано из книги Эрика Эванса по управлению доменами, или она исходила из других источников?

    Это старый материал. Книга Эрика просто заставляла его гудеть еще немного.

    Где есть хорошие объяснения причин этого?

    Причина проста: человеческий ум становится слабым, когда сталкивается с неопределенно связанными множественными контекстами. Они приводят к двусмысленности (Америка в Южной / Северной Америке означает Юг / Северная Америка), двусмысленность приводит к постоянному отображению информации всякий раз, когда разум «затрагивает ее» и подводит итог как плохую производительность и ошибки.

    Бизнес-логика должна отражаться как можно яснее. Внешние ключи, нормализация, реляционное сопоставление объектов происходит из совершенно другого домена – эти вещи являются техническими, связанными с компьютером.

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

    edit: Чтобы уточнить: я не говорю о classической практике OO для разделения доступа к данным на отдельный уровень из бизнес-логики – я говорю о конкретной договоренности, в которой в DDD сущности не должны разговаривать с данными (т.е. они не должны содержать ссылки на объекты репозитория)

    Разум все тот же, о котором я упоминал выше. Здесь это еще один шаг вперед. Почему объекты должны быть частично персидскими, если они могут быть (по крайней мере, близки) полностью? Меньше проблем, связанных с доменом, относится к нашей модели – больше дышащей комнаты, которую получает наш ум, когда ей приходится переучитывать ее.

    просто Вернон Вон дает решение:

    Используйте repository или службу домена для поиска зависимых объектов перед вызовом совокупного поведения. Служба клиентских приложений может контролировать это.

    В идеальном мире DDD предлагает, чтобы сущности не имели ссылки на слои данных. но мы не живем в идеальном мире. Домены могут потребоваться ссылаться на другие объекты домена для бизнес-логики, с которыми у них может не быть зависимости. Логически для объектов ссылаться на уровень репозитория для цели только для чтения, чтобы получить значения.

    Interesting Posts

    Кэширование в веб-браузере Android

    На каких платформах целое деление на ноль запускает исключение с плавающей запятой?

    GetEntryAssembly для веб-приложений

    Инкапсуляция против абстракции?

    Как использовать с DefaultStreamedContent в ui: repeat?

    Эффективность анализа (если, TryParse, Try-Catch)

    Самый эффективный способ найти верхние K Частые слова в большой последовательности слов

    Как рассказать рассказчику Windows 8 читать только то, что я конкретно скажу, чтобы он читал и оставался абсолютно безмолвным в противном случае?

    «Ls: not found» после запуска «read PATH»

    Как определить, какой тип Wi-Fi поддерживается вашим драйвером в Linux

    JSP-трюки, чтобы упростить создание шаблонов?

    Как найти, существует ли div с определенным идентификатором в jQuery?

    Попадание в интерпретатор во время произвольного местоположения кода scala

    Как установить языковые пакеты в Windows 8?

    Обход DNS-параметров маршрутизатора

    Давайте будем гением компьютера.