Цель этого поста — дать вам лучшее представление о том, как вы можете создать свой слой данных и почему важно создать полный уровень абстракции.
Что случилось?
С самого начала приложения были ориентированы на данные. То есть база данных была сердцем приложений. Все, что было построено, было (и в некоторых отношениях все еще остается) просто прославленным интерфейсом администрирования базы данных (особенно приложений CRUD). Что еще хуже, некоторые по-прежнему считают, что хранимые процедуры необходимы и что в них должны храниться бизнес-правила (если не все).
На сегодняшний день большинство таблиц в базе данных смоделированы в соответствии с 3NF (третья нормальная форма). Мы получаем чистую базу данных с очень небольшим количеством повторных записей
Единственное, что действительно произошло, — это то, что база данных была скрыта за OR / M, и мы говорим, что наши приложения используют классы, смоделированные по бизнесу (поскольку OR / M генерирует классы и отображения для нас).
Создание приложения
Давайте начнем с изучения типичного создания приложения (напоминает мне цитату, которую я прочитал давным-давно: процесс создания приложения похож на изготовление колбас: его никогда не следует наблюдать ).
Уровни данных
Типичные проблемы
Вот некоторые типичные проблемы, которые могут возникнуть при использовании OR / M без надлежащих абстракций.
- Вы когда-нибудь боролись с запросом LINQ, который просто не хочет работать?
- Низкая производительность из-за ленивой загрузки
- Сгенерированный SQL оказывается медленным
- Срочные изменения в новой версии OR / M
- Сложные и / или сильно связанные модели благодаря всем отношениям в БД
Что такое абстракция?
Википедия определяет «абстракцию» следующим образом:
Абстракция — это процесс, посредством которого данные и программы определяются с представлением, похожим по форме на свое значение (семантику), скрывая при этом детали реализации. Абстракция пытается уменьшить и выделить детали, чтобы программист мог сосредоточиться на нескольких концепциях одновременно
Таким образом, в отличие от того, что многие считают, вы не создаете абстракции просто для того, чтобы иметь возможность переключать OR / M или механизм базы данных. Вы создаете это, чтобы уменьшить сложность.
Перед использованием абстракции вы должны запомнить все детали для обоих слоев при кодировании. Объем информации, которую вы должны запомнить, зависит от типа инструмента / библиотеки, которую вы используете в своем слое данных. Для OR / Ms вы должны помнить, как слой данных обрабатывает отложенную загрузку, как конкретно настроить загрузку элементов и как каждый объект был настроен. Для ADO.NET вы должны помнить структуру таблицы и все типы данных. Если вы используете OR / M, вы, вероятно, автоматически создали все объекты. И тем самым вы заставили себя использовать модель данных в качестве бизнес-модели. Если вы какое-то время использовали OR / M, вы, вероятно, заметили, что это не лучшая вещь для чего-либо, кроме простых приложений.
Поэтому перед использованием абстракции вы должны посмотреть на свой код следующим образом:
Используя абстракцию, вам не нужно заботиться о том, как работает ваш OR / M или механизм базы данных. Все, что вам нужно знать, — это то, что уровень данных имеет набор классов и методов, которые вы вызываете для извлечения и хранения информации.
Ваш OR / M не является абстракцией
Если вы используете OR / M в качестве слоя данных, у вас, вероятно, будет много кода, например:
public void Summarize(int userId) { // loading from db var carts = _dbSession.Query<ShoppingCart>().Where(x => x.UserId == userId); // some business logic var summary = 0d; foreach (var cart in carts) { summary += cart.Total; } return summary; }
Это выглядит достаточно просто.
Но как мы можем это проверить?
Мы должны убедиться, что он вызывает БД с правильным набором параметров, верно? Издеваться над поставщиками LINQ не весело, так что об этом не может быть и речи. Мы можем создать БД с поддельной памятью, используя SQLite или аналогичный. Но это не гарантирует, что все работает с производственной базой данных (SQL Server или что-то еще).
Как насчет сопоставлений? Чтобы гарантировать, что все работает, мы должны сделать интеграционные тесты. То есть использовать реальные запросы к реальной БД.
Поэтому может показаться, что метод отвечает только за выполнение бизнес-логики. Но на самом деле он также отвечает за правильное чтение данных. OR / M — это просто удобный уровень, который загружает информацию из БД для нас. Это не отвлекает доступ к данным. Любое изменение в OR / M или базе данных повлияет на каждый фрагмент кода, который использует OR / M и базу данных.
Наши требования всегда будут меняться. Эти изменения всегда будут влиять на ваш код. С помощью абстракции легче справиться с этим (поскольку вы делаете это в одном месте за раз).
Используя правильную абстракцию
Давайте вместо этого изменим код на:
public void Summarize(int userId) { // loading from db var carts = _shoppingCartRepos.FindForUser(userId); // some business logic var summary = 0d; foreach (var cart in carts) { summary += cart.Total; } return summary; }
Сейчас мы используем абстракцию. Фактический доступ к данным скрыт. Нам не нужно знать подробности реализации OR / M, такие как сопоставления, энергичная / отложенная загрузка или специальные операторы LINQ для OR / M. Мы просто знаем, что мы должны вызвать FindForUser
метод, чтобы получить данные, которые нам нужны.
Теперь мы можем легко проверить метод. Единственные тесты, которые нам нужны для репозиториев, — это интеграционные тесты, чтобы убедиться, что все операторы работают правильно.
Резюме
Полные абстракции пишут дольше. Но спроси себя. На что ты будешь проводить больше всего времени? Поддерживать приложение или писать с нуля? Предположим, что у приложения ожидаемый срок службы четыре года, и на его написание уходит один год. Тогда у вас есть три года, в течение которых вы должны поддерживать это. Как вы думаете, эти три года будут легче или сложнее с абстракциями? Возможно, стоит потратить немного времени в начале …
Правильный путь
Надеюсь, теперь вы поняли, почему нам нужно создать какую-то абстракцию из базы данных, а OR / M — это не так. Но это только часть решения. Задайте себе вопрос: почему вы создаете приложение? Чтобы получить хорошо смоделированную базу данных, которой должно следовать ваше приложение? Или это решить проблему, которая есть у клиента?
Брэд говорит нам:
0,1872147469154859
База данных
Сумасшедшая вещь заключается в том, что мы позволили базе данных (как в СУБД) диктовать нашим приложениям долгий путь. И мы моделировали наши базы данных таким же образом (используя обычные формы) с 1971 года. Это 42 года, вы, ленивые программисты (я использовал калькулятор =)). Это безумие!
Пришло время положить конец безумию. Смею сказать нет. База данных — это просто хранилище.
Если это не помогает Послушайте королеву Елизавету (никакого неуважения не имеется в виду).
0,9660675303844571
База данных все плохо тогда? Нет. Но мы должны мысленно переключиться с того, чтобы позволить ему контролировать наше приложение, чтобы быть просто утилитой, в которой мы храним нашу информацию. Как мы можем сделать это?
Сначала смоделируйте домен
Одна вещь была упомянута ранее, это сначала моделирование домена / бизнеса, а затем базы данных. Мы создаем набор классов, как Person
и ChatMessage
т. Д. В первой итерации. Затем мы проверяем их и демонстрируем приложение клиенту.
После этого мы добавляем представления этих классов в базу данных. То есть мы моделируем итерацию БД тоже.
денормализовать
Нормализация не все хорошо. Хранение дешево сегодня. Подумайте дважды, прежде чем нормализоваться, если нормализация увеличит сложность вашего приложения.
Например, если вы сохраняете только адрес доставки в БД (т.е. вы не ищете и не индексируете его), то в действительности нет необходимости в отдельной таблице / отношении. Храните адрес в ShippingAddress
столбце Orders
таблицы. (У пользователя даже может быть ShippingAdress
столбец в таблице User, из которого вы копируете его при размещении Заказа).
SRP относится и к БД
Ваше приложение может иметь разные целевые аудитории, которые делают разные вещи в приложении. Например, бухгалтерия берет на себя все счета, а доставка — упаковку и отправку посылок. И, наконец, у вас есть пользователи, которые размещают заказы.
В такой ситуации было бы заманчиво создать Users
таблицу, содержащую все поля для сотрудников бухгалтерии, пользователей и сотрудников отгрузки. Более тонкий подход заключается в том, чтобы позволить пользовательской таблице представлять пользователей приложения, а затем создать таблицу AccountingUsers
с FK для Users
таблицы.
Эти подходы увеличивают сложность и косвенно делают всех людей в системе зависимыми от изменений в таблице Users.
Гораздо лучше создать три разных типа таблиц людей без связи между ними. Вы могли бы даже пойти так далеко, создав различные базы данных. Один для доставки, один для учета и один для основного приложения. Мы используем код, чтобы различные части взаимодействовали друг с другом, верно? Нет необходимости в единой базе данных.
Замени это
На сегодняшний день существует несколько бессхемных (NoSQL) баз данных. Многие из них достаточно созрели для использования в производстве. Если бы я мог выбрать, я бы в большинстве случаев выбрал NoSQL db. Это значительно снижает трение между хранилищем и кодом.
Mix!
Ничто не говорит о том, что единственное решение для базы данных (будь то СУБД или NoSQL) является подходящим инструментом для работы. NoSQL мог бы хорошо подходить для доставки, в то время как бухгалтерский учет должен использовать RDBMS, чтобы они могли выполнять свою аналитику.
Резюме
Поскольку источники данных удалены, мы получаем гораздо больше свободы в наших приложениях. Нам не нужно пытаться сделать наши приложения подходящими для базы данных, но вместо этого мы можем соответствовать базе данных после нашего приложения.
Создание приложений
Итак, если мы не должны начинать с базы данных, с чего начать? Как насчет чего-то вроде этого:
По созданию приложений
Надеюсь, тебе есть над чем подумать. Вам не нужно идти и изменять то, как вы создаете свои приложения, но, надеюсь, вы дважды подумаете, прежде чем выбрать конкретный путь.
Альтернативы доступа к данным
При работе с базой данных на уровне данных у нас фактически есть несколько подходов на выбор. Самым основным подходом является ADO.NET, и OR / M являются безусловно самым сложным решением. Так почему же OR / Ms набрали столько сил? Это потому, что они генерируют много вещей для вас. Ты ленивый. Мы все такие. Дело в том, что то, что мы получаем в начале, может быть потеряно во время дороги. Вот почему вы должны знать об альтернативах.
Этот пост стал немного больше, чем я хотел. Поэтому я перемещаю альтернативы отдельным постам. Они однако:
- Ваниль ADO.NET
- Картографы данных
- Или в / м
- NoSQL
Шаблоны уровня данных
Существуют также некоторые шаблоны, которые можно использовать для уровня данных (или для уровня данных). Те, которые я рекомендую:
- Шаблон репозитория
- Объект запроса
- CQS (разделение команд / запросов)
- CQRS (принцип разделения команд / запросов)
Это также будет обсуждаться в отдельном посте.