Считается ли плохим замыслом передавать интерфейс репозитория в качестве аргумента методу в доменном классе? - PullRequest
2 голосов
/ 14 декабря 2011

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

Мы используем ORM-код EF 4.1 с первым кодом, и до сих пор мы стремились оградить наших начинающих разработчиков от страшного исключения «LINQ to Entities не может преобразовать blablabla в выражение хранилища» при выполнении запросов к контексту во времяранние итерации.

У нас есть различные агрегированные корневые интерфейсы хранилища через EF.Однако некоторые блоки кода в impls кажутся ответственными за домен.Пока интерфейс репозитория объявлен в домене, а impl находится в инфраструктуре (внедрена зависимость), считается ли плохим проектом передача интерфейса репозитория в качестве аргумента методу класса сущности (или другого домена)?

Например, будет ли это плохо?

public class EntityAbc {
    public void SaveTo(IEntityAbcRepository repos) {...}
    public void DeleteFrom(IEntityAbcRepository repos) {...}
}

Что если конкретному объекту необходим доступ к другим агрегированным корневым репозиториям?Будет ли это нормально или нет, и почему?

public void Save() {
    var abcRepos = DependencyInjector.Current.GetService<IEntityAbcRepository>();
    var xyzRepos = DependencyInjector.Current.GetService<IEntityXyzRepository>();
    // work with repositories
}

Обновление 1

Я не упомянул о переносе кода на прикладной уровень, потому что я рассматриваю часть кодакоторый использует IEntityAbcRepository для обеспечения применения бизнес-правил.Impl хранилища должен быть как можно более ванильным, верно?Его основной обязанностью должна быть простая абстракция над ORM, позволяющая вам находить / добавлять / обновлять / удалять сущности.Неправильно?

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

Затем прикладной уровень может координировать поток, внедряя impl-репозиторий в его конструктор и передавая его в качестве аргумента доменной службе или фабрике.Это плохая практика?

Обновление 2

Добавление еще одного пояснения здесь.Что если домену нужен только доступ к IEntityAbcRepository для выполнения его методов Find ()?В приведенном выше примере методы SaveTo и DeleteFrom не будут вызывать какие-либо методы добавления / обновления / удаления в интерфейсе репозитория.

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

  1. IEntityAbcReadRepository <- определяет все сигнатуры метода find </li>
  2. IEntityAbcWriteRepository <- определяет все add /обновить / удалить метод sigs </li>

В этом случае было бы плохой практикой передавать IEntityAbcReadRepository в качестве параметра методу домена?

Ответы [ 3 ]

5 голосов
/ 14 декабря 2011

Ваш первый подход лучше по сравнению со вторым подходом, который использует шаблон «Service Locator». Зависимости более очевидны при первом подходе.

Вот несколько ссылок, объясняющих, почему «Service Locator» является плохим выбором

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

Итак, ваш первый подход - лучшее решение.

0 голосов
/ 15 декабря 2011

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

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

С учетом сказанного, где вы можете поместить такие методы, как Save () и Delete ()?

  • Если вы хотите добавить / удалить вашу сущность из своего репозитория, Repository.Add () и Repository.Remove () - хороший выбор.Хранилище в основном служит иллюзией коллекции ваших сущностей в памяти, поэтому имеет смысл вести себя так же, как коллекция или список с соответствующими методами.

  • ЕслиВы хотите сохранить изменения, внесенные в существующую сущность, есть другие способы сделать это.Вы можете иметь метод Repository.Save (), но некоторые считают плохой практикой.Часто изменения являются частью операции более высокого уровня, которая обрабатывается в контексте, подобном транзакции, например в единице работы, и в этом случае вы можете позволить операции сохранить все объекты в своей области после ее завершения.Например, если вы используете в своем веб-приложении подход Open Session in View , изменения автоматически сохраняются после завершения запроса.Или вы можете рассчитывать на специальный вызов вашего ORM метода Save () для вашей конкретной сущности, который, мы надеемся, не должен быть внедрен в сам код сущности (например, с NHibernate, он доступен во время выполнения на проксируемой сущности).

[Обновление]

С точки зрения ваших последующих вопросов (хотя я не уверен, что понимаю их все хорошо):

  • Не вижу смысла разделять ваш репозиторий на ReadRepository и WriteRepository.В DDD ответственность за хранилище состоит в том, чтобы предоставить коллекцию для запроса из , а также добавить или удалить из.Это все еще довольно сплоченно.

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

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

    И последнее, тот тип внедрения, который вы упомянули, - SaveTo (репозитории IEntityAbcRepository) - необычен, потому что это не инъекция конструктора и не сеттера, а скорее эфемерная инъекция, которая длится только время метода.Это подразумевает, что тот, кто вызывает ваш метод, должен знать, какой репозиторий следует передать в этот конкретный момент, что не очевидно.Это может быть полезно, но я бы сказал, что это не та форма инъекции, которую вы обычно используете.

0 голосов
/ 14 декабря 2011

Краткий ответ : Да!

Длинный ответ :

Подумайте о создании AbcService на уровне службы приложений. Этот уровень обслуживания находится между вашим доменом и вашей инфраструктурой. Вы можете добавить столько хранилищ в AbcService, сколько захотите. Затем пусть служба обрабатывает SaveTo и DeleteFrom.

SaveTo и DeleteFrom, если только вы не сохраняете и не удаляете из другого объекта, то есть не имеете доступа к данным, это методы, которые звучат так, как будто они не должны быть в доменном объекте, IMO.

...