Когда я впервые узнал о Domain Driven Design, меня также познакомили с репозиторием и шаблонами рабочих единиц, которые когда-то казались первоклассными для крутых ребят, которые бросали SQL-запросы, например, пещерные люди, в базы данных. Чем глубже я углубился в эту тему, тем больше я узнал, что они больше не нужны, поскольку ORM, такие как EF и NHibernate , реализуют как единицу работы, так и репозитории в одном API, называемом сеансом или контекстом.
Теперь я не уверен, что делать. В хранилище или нет в хранилище. Я действительно понимаю аргумент, что такие дырявые абстракции только чрезмерно усложняют вещи, добавляя абсолютно ничего, что может упростить доступ к данным, однако неправильно связывать все возможные аспекты моего приложения, например, с Entity Framework . Обычно я следую нескольким простым правилам:
- Доменный уровень - это сердце системы, содержащее сущности, сервисы, репозитории ...
- Уровень инфраструктуры предоставляет реализации доменных интерфейсов инфраструктуры, например, файлов, баз данных, протоколов.
- На прикладном уровне размещается корень композиции, который связывает вещи и организует все.
Мои решения обычно выглядят так:
Domain.Module1
Domain.Module2
IModule2Repo
IModule2Service
Module2
Infrastructure.Persistence
Repositories
EntityFrameworkRepositoryBase
MyApp
Boostrapper
-> inject EntityFrameworkRepositoryBase into IRepository etc.
Я сохраняю свой уровень домена чистым, используя объект, IRepository<'T>
который также является проблемой домена, не зависящей от чего-либо еще, что говорит мне, как получить доступ к данным. Когда я сейчас сделаю конкретную реализацию того, IModule2Service
что требует доступа к данным, мне придется внедрить DbContext
и тем самым связать его непосредственно с уровнем инфраструктуры. (При переходе к проекту Visual Studio это может оказаться очень сложным из-за циклических зависимостей! )
Кроме того, что может быть альтернативой депозитариям и актам работ ? CQRS? Как можно абстрагироваться от чистой инфраструктурной структуры?
Ответы:
Инженерия это все о компромиссах. И так же разработка программного обеспечения. На данный момент, я полагаю, что только другой вариант, который был бы проще, это работать напрямую с ORM. Но, как вы сказали, это может заблокировать вас в определенных рамках персистентности.
Поэтому вы должны спросить себя: «Стоит ли дополнительная сложность отделять ваш код от постоянства». Каждый раз, когда я слышу, как люди говорят, что они хотят отделить свой код от настойчивости, я спрашиваю: «Сколько раз в вашей карьере вы меняли структуру постоянства?»
Проблемы, которые я вижу с репозиториями, заключаются в том, что они усложняют общие изменения. Например. добавление нового способа запроса агрегата. И они делают необычные изменения (предположительно) легче. Например. изменение реализации репозиториев.
Кроме того, есть аргумент модульного тестирования, на который я говорю: «Если постоянная среда не позволяет вам смоделировать базу данных, ни в памяти, ни локально, тогда вообще не стоит использовать эту среду».
источник
Я сторонник репозитория, хотя я отошел от общих шаблонов репозитория. Вместо этого я привожу свои репозитории в соответствие с бизнес-функциями, которые они выполняют. Репозитории не нацелены на абстрагирование ORM, так как это не то, что я ожидаю изменить, и в то же время я избегаю делать репозиторий слишком гранулированным. (Т.е. CRUD) Вместо этого мои репозитории служат двум-трем ключевым целям:
Для извлечения данных хранилище всегда возвращается
IQueryable<TEntity>
. Для создания данных он возвращает TEntity. Репозиторий обрабатывает мою фильтрацию базового уровня, такую как «активное» состояние авторизации для систем, которые используют шаблоны мягкого удаления, и «текущее» состояние для систем, которые используют исторические данные. Создание данных отвечает только за то, чтобы необходимые ссылки были разрешены и связаны, а объект был настроен и готов к работе.Обновление данных является обязанностью бизнес-логики, работающей с соответствующими объектами. Это может включать в себя такие вещи, как правила проверки. Я не пытаюсь инкапсулировать это в метод репозитория.
Удаление в большинстве моих систем происходит при мягком удалении, поэтому это может привести к обновлению данных. (IsActive = false). В случае аппаратного удаления это будет одна строка в репозитории.
Почему репозитории? Тест-способность. Конечно, DbContext можно смоделировать, но проще смоделировать класс, который возвращает
IQueryable<TEntity>
, Они также хорошо работают с шаблоном UoW, лично я использую шаблон Mehdime DbContextScope, чтобы охватить единицу работы на уровне, который я хочу (т.е. контроллеры в MVC), и позволить моим контроллерам и вспомогательным классам обслуживания использовать репозитории без необходимости передавать ссылки на UoW / dbContext вокруг. Использование IQueryable означает, что вам не нужно много методов-оболочек в репозитории, и ваш код может оптимизировать использование данных. Например, хранилищу не нужно предоставлять такие методы, как «Exists» или «Count», или пытаться обернуть сущности другими POCO в тех случаях, когда вам нужны подмножества данных. Им даже не нужно обрабатывать загружаемые опции для связанных данных, которые могут вам понадобиться или не понадобиться. Передав IQueryable, вызывающий код может:Очень гибкий, и из тестового PoV мой смоделированный репозиторий просто должен возвращать списки заполненных объектов-сущностей.
Что касается общих репозиториев, я по большей части отошел от них, потому что, когда вы получаете репозиторий для каждой таблицы, ваши контроллеры / сервисы заканчивают ссылками на несколько репозиториев для выполнения одного бизнес-действия. В большинстве случаев только один или два из этих репозиториев фактически выполняют операции записи (при условии, что вы правильно используете свойства навигации), в то время как остальные поддерживают те, что с Reads. Я предпочел бы иметь что-то вроде OrdersRepository, которое способно считывать и создавать заказы, а также читать любые релевантные запросы и т. Д. (Облегченные объекты клиентов, продукты и т. Д.) Для справки при создании заказа, а не использовать 5 или 6 различных репозиториев. Это может нарушить пуристов DNRY, но я утверждаю, что цель репозитория - служить созданию заказов, которые включают соответствующие ссылки.
Repository<Product>
чтобы получить продукты, для которых на основании заказа мне нужен только объект с несколькими полями. В моем OrderRepository может быть.GetProducts()
метод, возвращающий,IQueryable<ProductSummary>
который, на мой взгляд, более приятный, чем метод, который вRepository<Product>
итоге имеет несколько методов «Get», чтобы попытаться обслуживать различные области потребностей приложения, и / или некоторое сложное выражение фильтрации передачи.Я выбираю более простой код, который легко отслеживать, тестировать и настраивать. Его можно использовать ненадлежащим образом, но я предпочел бы иметь что-то, где злоупотребления легко обнаружить и исправить, чем пытаться «заблокировать» код таким образом, чтобы его нельзя было злоупотребить, потерпеть неудачу и получить что-то, что кошмар, чтобы заставить фактически делать то, что платит клиент, чтобы сделать это в конце. :)
источник