До сховища чи не до сховища


10

Коли я вперше дізнався про дизайн, керований доменом, мене також ознайомили з сховищем та одиницею роботи, які колись здавались найкращими для крутих дітей, які кидали запити SQL, як печери, проти баз даних. Чим глибше я заглиблювався в цю тему, тим більше я дізнався, що вони, здається, вже не потрібні через такі ORM, як EF та NHibernate, які реалізують як одиницю роботи, так і сховища в одному API, що називається сесії або контекстом.

Тепер я не впевнений, що робити. На сховище чи не в сховище. Я дійсно розумію аргумент, що такі витікаючі абстракції лише надто ускладнюють речі, додаючи абсолютно нічого, що може спростити доступ до даних, однак, не вважається правильним поєднувати всі можливі аспекти моєї заявки, наприклад, з Entity Framework . Зазвичай я дотримуюся кількох простих рекомендацій:

  1. Доменний шар - це серце системи, що містить об'єкти, служби, сховища ...
  2. Інфраструктурний рівень забезпечує реалізацію доменних інтерфейсів, пов'язаних з інфраструктурою, наприклад, файл, база даних, протоколи.
  3. У шарі додатків розміщений корінь композиції, який з'єднує речі і все оркеструє.

Мої рішення зазвичай виглядають так:

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? Як один абстрактний чистий інфраструктурний каркас?


1
Якщо говорити про "сховище", як це описано в DDD, то EF та nHibernate не є сховищами. Звичайно, вони частково абстрагують механізм доступу до даних, але до сховища є значно більше .
Ерік Кінг

Відповіді:


4

У інженерії йдеться про компроміси. Так само і розробка програмного забезпечення. Зараз я вважаю, що лише іншим варіантом, який був би простішим, є робота безпосередньо з ORM. Але, як ви сказали, це може зафіксувати вас у конкретних системах стійкості.

Тож вам доведеться запитати себе: "Чи варто додаткова складність від'єднання вашого коду від стійкості". Кожного разу, коли я чую, як люди кажуть, що хочуть від'єднати свій код від наполегливості, я запитую "Скільки разів за свою кар'єру ви змінили рамки стійкості?"

Проблеми, які я бачу із сховищами, полягають у тому, що вони ускладнюють загальні зміни. Напр. додаючи новий спосіб запиту сукупності. І роблять нечасті зміни (нібито) простішими. Напр. зміна реалізації сховищ.

Тоді є також аргумент тестування одиниць, на який я кажу: "Якщо система стійкості не дозволяє вам знущатися з бази даних, ні в пам'яті, ні на локальному рівні, використовувати рамку взагалі не варто".


1
Сенс репозиторію полягає в тому, щоб відключити додаток від наполегливості, і тут немає ніяких складностей. І я міняю деталі збереження хоча б один раз, тому що доступ до db - це останнє, що я кодую, до цього моменту, який я використовую в репозиції пам'яті. Крім того, є дуже тонка пастка, коли ви безпосередньо використовуєте деталь наполегливості. Об'єкти вашого бізнесу, як правило, розроблені таким чином, щоб бути сумісними з ним, а не агностичними. І це тому, що багату, правильно капсульовану сутність не можна безпосередньо відновити з будь-якого сховища (крім пам’яті) без (деяких потворних) обхідних шляхів
MikeSW

Про те, як додати новий спосіб запиту сукупного кореня чи будь-якої сутності, ось чому з'явився CQRS. Особисто я зберігаю сховище для цілей домену та використовую обробники запитів для фактичного запиту. І ці обробники дуже щільно з'єднані з db і ofc дуже ефективні в тому, що вони роблять.
MikeSW

3

Я прорепозиторій, хоча я відійшов від загальних шаблонів сховищ. Натомість я узгоджую свої сховища з функцією бізнесу, яку вони обслуговують. Репозиторії не спрямовані на те, щоб абстрагувати ORM, оскільки це не те, що я сподіваюся змінити, і в той же час я уникаю робити сховище занадто детальним. (Тобто CRUD) Натомість мої сховища виконують два-три ключові цілі:

  • Пошук даних
  • Створення даних
  • Жорстке видалення

Для пошуку даних сховище завжди повертається IQueryable<TEntity>. Для створення даних він повертає TEntity. Репозиторій обробляє мій базовий рівень фільтрації, такий як "активний" стан авторизації для систем, які використовують схеми м'якого видалення, та "поточний" стан для систем, що використовують історичні дані. Створення даних несе відповідальність лише за те, щоб потрібні посилання були вирішені та пов'язані, а також організація створена та готова до роботи.

Оновлення даних - це відповідальність бізнес-логіки, яка працює з відповідними суб'єктами (організаціями). Сюди можна віднести такі речі, як правила перевірки. Я не намагаюся інкапсулювати це методом сховища.

Видалення в більшості моїх систем є м'яким видаленням, щоб потрапити під оновлення даних. (IsActive = false) У випадку жорсткого видалення це буде одноклапник у сховищі.

Чому сховища? Тест-здатність. Звичайно, з DbContext можна знущатися, але простіше знущатися над класом, який повертаєтьсяIQueryable<TEntity>. Вони також чудово грають з шаблоном UoW; я особисто використовую шаблон DbContextScope Мехдіме, щоб розширити одиницю роботи на потрібному рівні (тобто контролери в MVC) і дозволити моїм контролерам і допоміжним класам обслуговування використовувати сховища без необхідності передавати посилання на UoW / dbContext навколо. Використання IQueryable означає, що вам не потрібно багато методів обгортки у сховищі, і ваш код може оптимізувати спосіб використання даних. Наприклад, сховище не потребує викриття таких методів, як "Існує" або "Порахувати", або намагатися обернути об'єкти іншими POCO у випадках, коли потрібно підмножини даних. Їм навіть не потрібно обробляти нетерплячі варіанти завантаження пов'язаних даних, які можуть вам або не знадобляться. Передаючи IQueryable, код виклику може:

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

Дуже гнучка, і з тестового PoV моєму глузливому сховищу просто потрібно повернути Списки населених об'єктів.

Що стосується загальних сховищ, то я здебільшого віддалився від них, тому що, коли ви опиняєтесь сховищем у таблиці, ваші контролери / служби в кінцевому підсумку мають посилання на кілька сховищ, щоб зробити одну ділову дію. У більшості випадків лише одне або два з цих сховищ насправді виконують операції запису (за умови правильного використання властивостей навігації), а решта підтримують ті, які читають. Я вважаю за краще мати щось на зразок OrdersRepository, який здатний читати і створювати замовлення, а також читати будь-які відповідні пошуки тощо (легкі об’єкти, продукти тощо) для довідки при створенні замовлення, ніж натискання на 5 або 6 різних сховищ. Це може порушувати пуристів DNRY, але мій аргумент для цього полягає в тому, що мета сховища - служити створенню замовлень, що включає відповідні посилання.Repository<Product>щоб отримати продукти там, де для основи замовлення мені потрібна лише організація з кількома полями. Мій OrderRepository може мати .GetProducts()метод повернення, IQueryable<ProductSummary>який мені здається кращим, ніж той, Repository<Product>що в кінцевому підсумку має кілька методів "Get", щоб спробувати обслуговувати різні області потреб додатків та / або якийсь складний вираз фільтрації для входу.

Я вибираю простіший код, який легко слідкувати, тестувати та налаштовувати. Це можна зловживати поганими способами, але я б скоріше мав щось, де зловживання легко помітити і виправити, ніж намагатися "заблокувати" код таким чином, щоб не можна було ними зловживати, не вдаватись до нього, а потім мати щось, що є кошмар, щоб насправді зробити те, що платить клієнт, щоб в кінцевому підсумку це зробити. :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.