Чистая архитектура: инвертирование зависимости EF DbContext

Я наткнулся на эту статью, в которой объясняются принципы чистой архитектуры. Я хочу применить эти принципы в ASP.NET Core + EF. Я застрял на этой части:

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

Звучит вполне разумно: уровень приложения не должен знать, что EF — это вещь, а должен предоставлять только интерфейс, который затем будет реализован контекстом БД на уровне инфраструктуры.

Однако, когда я пытаюсь это сделать, я сталкиваюсь с блокировщиком. Скажем, мой контекст БД выглядит так:

public class DbContext {
   public DbSet<Person> Persons {get; set;} = null!;
}

Согласно «Чистой архитектуре», DbContext теперь должен реализовать IDbContext, расположенный на уровне приложения:

public interface IDbContext {
   public DbSet<Person> Persons {get; set;}
}

Однако DbSet — это тип, определенный в EF, поэтому уровень приложения также должен зависеть от EF, что, согласно моему пониманию статьи и «Чистой архитектуры», противоречит принципам.

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

Есть идеи, как выйти из этого и добиться уровня приложения, независимого от уровня инфраструктуры и EF?

Вы можете определить интерфейс, который предоставляет IQueryable<Person>.

David Browne - Microsoft 18.06.2024 16:38

Я думал об этом, но IQueryable<Person> не имеет Add (в отличие от DbSet), и при проверке метод Add не исходит из каких-либо интерфейсов, а реализуется непосредственно в DbSet, поэтому я, к сожалению, до сих пор не вижу, как это сделать. сделать эту работу.

karlosss 18.06.2024 16:43

Если вам абсолютно необходимо абстрагироваться от Entity Framework, возможно, вам стоит изучить шаблон репозитория: Learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions‌​/…

TheHvidsten 18.06.2024 16:47

@TheHvidsten Мне это «абсолютно не нужно», я учусь, поэтому мне интересно, может ли блестяще выглядящая теория работать на практике, чему сама статья немного противоречит, ссылаясь на репозиторий, который демонстрирует то, о чем в статье не говорится: - Д. Спасибо за ссылку, почитаю немного

karlosss 18.06.2024 16:54

@TheHvidsten эта ссылка говорит совсем не об этом. То, что вы предложили, является уродливым антипаттерном. DbContext — это высокоуровневая единица работы с несколькими объектами, а не соединение с базой данных. Помещение поверх него низкоуровневого CRUD-класса полностью нарушает его функциональность как в качестве ORM, так и в качестве отключенного UoW.

Panagiotis Kanavos 18.06.2024 16:55

@PanagiotisKanavos Я согласен, что это антишаблон, но нежелание правильно использовать Entity Framework не дает много хороших вариантов, поэтому, если вам абсолютно необходимо, то есть много плохих вариантов (отсюда и «абсолютно необходимо»).

TheHvidsten 18.06.2024 18:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
6
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Существуют разные подходы (с разными потенциальными недостатками):

  1. Используйте интерфейсы IQueryable вместо DbSet - хотя это все равно будет дырявая абстракция (например, такие методы, как Include, специфичны для EF, поэтому вам нужно будет ссылаться на них, когда они понадобятся)

  2. Не раскрывайте контекст как часть уровня вашего домена/приложения вообще V1:

    Используйте репозитории (и/или единицы работы) поверх EF Core. Обратите внимание, что этот подход (или некоторые его варианты) можно рассматривать как антипаттерн (например, ).

  3. Не раскрывайте контекст как часть вашего уровня домена/приложения вообще V2:

    Используйте шаблон CQRS (или что-то подобное), когда на уровне домена/приложения запросы и команды будут объявлены как интерфейсы и будут реализованы на уровне инфраструктуры через EF.

Я полностью против размещения классов CRUD поверх DbContext, а вовсе не против правильных репозиториев доменов с несколькими объектами. Однако в случае с ORM, такими как EF, они в конечном итоге становятся тонкими оболочками и контейнерами для запросов. С другой стороны, однообъектные CRUD-классы, которые люди называют «универсальными репозиториями», обычно начинают с нарушения параллелизма, затем реализуют методы, вызывающие удаления, а затем люди приходят сюда и спрашивают, почему их код не работает.

Panagiotis Kanavos 18.06.2024 17:00

Спасибо за обзор! Если чистая архитектура вокруг EF на практике не достижима, как практически решается эта проблема? Просто игнорировать чистую архитектуру и связать уровень приложения с EF, он же поставщик персистентности?

karlosss 18.06.2024 17:02

@karlosss TBH в своих приложениях (если только они не «простые») я склонен использовать EF Core в качестве инструмента уровня «DAL», используемого внутри репозитория, или абстракций CQRS, которые довольно хорошо сочетаются с чистой архитектурой. Конечно, бывают случаи, когда абстракция становится дырявой (например, когда нужно реализовать какую-то сложную логику в транзакционном контексте), но обычно в моих проектах это случалось не так часто. Также обратите внимание, что всегда есть «скрытый» 4-й вариант — шаблоны — это общие рекомендации, а не законы, их можно нарушать.

Guru Stron 18.06.2024 17:13

@GuruStron, случайно, есть ли у вас публичный репозиторий одного из этих приложений, где можно было бы продемонстрировать использование, где я мог бы «получить вдохновение»? Я думаю, что просмотр настоящего работающего кода поможет мне многое понять. Прежде чем нарушить шаблон, я хочу быть уверен, что у меня есть веские причины для этого, потому что, по моему опыту, нарушение шаблонов обычно не заканчивается хорошо :)

karlosss 18.06.2024 17:25

«Чистая архитектура» — это название книги, а не образец. Он превратился в общий маркетинговый термин для обозначения связанных идей и книг. Обычное описание узоров — A solution to a problem in a context. То, что работает для конкретной проблемы в одной ситуации (контексте), не обязательно является лучшим решением в другой ситуации. Как правило, это будет очевидно, когда вам понадобятся отдельные модели для EF, уровня приложения и… вашей модели ресурсов REST и API DTO. Хорошая модель базы данных не всегда является хорошим API DTO. Слой Abstractions, используемый для обоих, будет ломаться по мере роста приложения.

Panagiotis Kanavos 18.06.2024 17:42

Например, шаблон размещенного проекта Blazor создает проект Shared, в который вы помещаете объекты, используемые как сервером, так и клиентом Blazor WASM (GUI). Это очень полезно до тех пор, пока... вам не понадобится добавлять аннотации для добавления проверки в графический интерфейс, что, в свою очередь, влияет на EF. Или осознать, что то, что Required в одной форме, нет Required в другой. Именно так были созданы ViewModels, чтобы удовлетворить потребности различных представлений вместо того, чтобы строить весь графический интерфейс поверх одной модели приложения. Если вы используете OData или GraphQL, аннотации также повлияют на API.

Panagiotis Kanavos 18.06.2024 17:50

@karlosss "случайно, у вас есть публичный репозиторий" - извините, но нет, у меня нет ничего общедоступного в этой области, есть некоторые общедоступные, сделанные другими, но у многих из них та же проблема - они показывают очень кратко счастливый путь, который немного запутывает угловые случаи (но, с другой стороны, дает общее представление). Также вы можете просмотреть шаблоны ABP, которые имеют аналогичный подход AFAIK.

Guru Stron 19.06.2024 10:03

посмотрю, спасибо!

karlosss 19.06.2024 10:13

Другие вопросы по теме