Резюме. В этой части мы опишем новую архитектуру для веб-приложений, создав новый слой, который прослушивает наше приложение на наличие изменений и переводит эти изменения в обновления базы данных для нас.

В произвольном порядке, некоторые из преимуществ этого:

  • гибкость в выборе используемой базы данных
  • повышенная тестируемость и разделение в кодовой базе
  • параметры времени выполнения для настройки производительности

Давайте посмотрим, какое место эта архитектура занимает в некоторых «политических» сферах современной веб-разработки.

Императивный и реактивный

При реактивном программировании код записи базы данных прислушивается к приложению на предмет событий, которые должны привести к записи, а не управляется приложением напрямую. Этот небольшой переход от обычного способа ведения дел является нововведением, которое приводит нас к реагированию на уровне базы данных, а не к указаниям, что делать в императивном стиле.

Итак, что произойдет, если вы инвертируете зависимость от базы данных и вам нужно выполнить обновление или запись? Простой. Ваше приложение больше не сообщает базе данных, какое обновление нужно сделать. Вместо этого вы вносите изменения в объекты, а компонент вашего приложения подписывается на изменения ваших объектов и создает поток императивных обновлений, тем самым симметрично заставляя БД реагировать на изменения в состоянии вашего приложения и принося преимущества реактивной парадигмы в пользу операций записи.

Если вам когда-либо было сложно превратить что-то синхронное во что-то асинхронное — например, добавить вызов API к существующему методу, требуя, чтобы он теперь возвращал обещание, а не значение — вы занимались императивным программированием, и у вас не было бы такого трения, если бы вы с самого начала были реактивными.

В императивном программировании нет ничего плохого, но когда дело доходит до пересечения уровней и входа в асинхронность, реактивное программирование может поддерживать гибкость, с которой не может справиться императивное программирование. И это не должно стоить вам производительности, потому что...

Синхронизация и асинхронность

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

Если вы используете Promise, Observable или обратный вызов, стиль — неважно, вызывается ли этот обратный вызов синхронно или асинхронно, — и это хорошо! Позже, если появится задержка в несколько миллисекунд, вы уже защищены, потому что ваш код никогда не предполагал синхронный ответ.

Действия и различия

В отличие от различий, написанных на языке вашей базы данных (например, `{$set: {status: ‘inactive’}}`), действия представляют собой более высокий уровень абстракции. Действия более аналогичны имени метода и аргументу этого метода (например, `INACTIVATE_USER 25`). Сейчас они модны из-за Redux, но на самом деле уходят корнями в прошлое и являются частью архитектурного шаблона, известного как CQRS — Command Query Responsibility Segregation.

Идея действия заключается в том, что вы заполняете форму, чтобы попросить что-то сделать — детали реализации остаются за пределами формы. Любая часть вашего приложения может создавать действия, но самое главное, вся работа вашего пользовательского интерфейса заключается в создании действий и их отправке. Как только действия будут реализованы в вашей базе данных, любые измененные данные будут реактивно возвращаться в приложение через публикации, и никаких явных сложных обновлений не требуется. Кроме того, упоминал ли я, как здорово для отладки иметь журнал действий, предпринятых пользователем в вашем приложении? Мы используем журнал действий, чтобы реализовать функцию путешествия во времени для наших пользователей. Мы думаем, что Действия — это колени пчел.

Действия и редукторы

Редьюсер — это просто жаргон для определенного типа реализации изменений. Его ключевое поведение заключается в том, что это чистая синхронная функция, которая использует только JavaScript и не использует внешние зависимости для создания представления о мире, в котором было реализовано изменение. Таким образом, он вернет объект, который является новым объектом для сохранения или новым объектом для сравнения и применения к базе данных в качестве обновления. Мы можем пометить объект как удаленный, но мы не верим, что на самом деле что-либо удаляем навсегда, по той причине, что Бухгалтеры не используют ластики).

Когда редюсер управляется хранилищем, как в случае с vanilla Redux, функции могут быть подписаны на хранилище для новых и измененных объектов. Затем подписчики передают эти изменения в базу данных (см. диаграмму выше). В результате ваши редьюсеры и действия полностью лишены битов, специфичных для базы данных **ВО ВСЕ**! Представьте, если бы каждая новая реализация изменений, которую вы вносите, была бы только на языке вашего приложения, а не платформы базы данных. Насколько быстрее будет разрабатывать новые функции и насколько более независимым от платформы будет ваш код!

Обязательные побочные эффекты и чистые функции

Типичный серверный метод:

  • Создает оператор обновления на языке базы данных
  • Сообщает базе данных, чтобы сделать это обновление

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

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

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

В заключение

Инвертирование зависимости от базы данных — это концепция, которую я не видел широко применяемой в веб-приложениях; императивный, синхронный стиль царил в течение моей 20-летней карьеры. Но такие компании, как Netflix, используют его для масштабирования, и разумное использование инверсии зависимостей снова и снова приводило к уменьшению связанности. Суть в том, что новые распределенные архитектуры и возросшие ожидания пользователей заставляют пересмотреть старые способы развертывания кода в Интернете, поэтому необходимы исследования новых — не присоединитесь ли вы к нам и попробуете?

Я хотел бы услышать ваши мысли по этому поводу, пробовали ли вы подобную реализацию или нет, и что вы хотели бы услышать в будущих частях.

Спасибо за чтение!

Дин Рэдклифф (Дин Рэдклифф)

Дополнительная литература
* Выворачивание базы данных наизнанку
* Бухгалтеры не используют ластики
* Принцип инверсии зависимостей
* Команда Разделение ответственности за запросы
* Netflix использует реактивное программирование