Reactive multistores с брокером сообщений.
Иногда бывает так, что front-end приложение становится слишком большим и громоздким. Связей между частями системы становится так много, что она превращается в ball of mud. Достаточно долго нам помогают совладать с этим подходы с разделение на домены, правильно выделение абстракций и слоёв, умелое использование инкапсуляции. Но современный бизнес диктует очень изменчивые правила, особенно для front-end, то что вчера казалось совершенно не связанными фичами — уже завтра может находится на одной странице рядом и взаимодействовать друг с другом, т. е. поведение одной части системы может внезапно стать зависимой от другой части системы. Перестроить доменные области обычно слишком трудоёмкая задача, сопоставимая с переписыванием нескольких слоёв (достаточная большая часть системы), этот вариант рассматривать пока не будем.
На помощь на придёт реактивная архитектура.
До сих пор все о чем мы говорили было интерактивной архитектурой, т. е. обычно это выглядит так. Мы заранее пишем коллбэк, который должен представлять реакцию на какое-то событие, например нажатие на кнопку. Когда событие происходит мы вызываем этот коллбэк, который где-то что-то изменяет, т. е. мы интерактивно влияем на систему. Это хороший базовый подход для работы с несложными системами. Проблема тут в том, что когда мы программируем такую кнопку, мы в интерактивном подходе должны заранее где-то уже создать этот коллбэк и передать его кнопке. Если должны измениться две части системы, то в этом коллбэке это будет указано. И если 5 частей системы должны измениться, то это тоже должно быть указано. В сложных системах таких «реагирующих» частей может быть много (порядка десятков). Т.е. появляется лишняя связь - кнопка как будто знает заранее (если предположить, что она может прочитать свой коллбэк), что и где будет изменяться. Как убрать эту лишнюю связь? Ответ - реактивная архитектура, или Event Driven Archotecture (EDA). Рассмотрим базовый паттерн Pub/Sub и то, как он поможет нам немного упростить систему. Схематично представим это на схеме (Рис.1):

Рис.1 Pub/sub паттерн
Стрелки на схеме показывают как компоненты с помощью сервисных функций publisher\subscriber могут взаимодействовать друг с другом. Идея в следующем — если у нас много реагирующих компонент, то зачем каждый раз прописывать в каждом месте, что должно происходить? Давайте сразу уведомим всех желающих в системе! При этом сам инициатор (кнопка из примера) не будет знать кто на её нажатие реагирует и наши связи, за которыми нужно следить, пропадут. Браузерное окружение из коробки позволяет работать с реактивным подходом. Вся работа DOM построена на событиях! И мы можем создавать кастомные события и обрабатывать их! В схеме приведён пример как компонент с помощью специальной функции отправляет сообщение, а все кто подписанн на это сообщение — получают его и могут обработать. Publisher не знает о том, кто реагирует на него, значит и связей дополнительных нет.
Реализовать это можно по-разному. Классические варианты — BroadcastChannelAPi, или использование пользовательских кастомных сообщений, но можно это организовать и через замыкание, и эмулировать такую реактивную архитектуру через предоставление функции-подписки напрямую подписчикам и через какие-нибудь webWorkers, всё зависит от целей и налагаемых ограничений.
Хочу упомянуть одну из самых важных особенностей EDA. События на которые мы подписываемся должны нести именно бизнес смысл. Т.е. создавать событья по типу «buttonInBuyFormPressedCustomEvent» - это антипаттерн, который сводит на нет идею EDA. Интересно ли другим компонентам системы, что была нажата кнопка? Обычно — нет. Для них главное - произошла ли покупка. На это они смогут правильно и семантически верно с точки зрения бизнеса реагировать. При этом если в системе появится дополнительный интерфейс покупки (не кнопка а например что-то более интерактивное или экзотическое, например ползунок, который нужно отодвинуть до предела вправо чтоб произвести покупку), то им не нужно будет подписываться и на его поведение отдельно. Главное не то, что кнопка была нажата, кнопки могло и вовсе не быть, это не должно влиять на поведение системы, а то, что произошло бизнес событие.
Для хорошей работы типы бизнес сообщений с интерфейсами должны храниться отдельно (не спрятаны в системе) для того, чтоб их можно было правильно переиспользовать.
Самое классное, что мы можем комбинировать интерактивный и реактивный подходы и использовать в разных частях системы оба, что обычно и реализуется на практике, они отлично дополняют друг друга!
Отметим основные плюсы данной архитектуры:
- Может являться хорошим инструментом уменьшения зацеплённости в коде, что делает систему прозрачнее, а разработку быстрее. Особенно хороша когда реагирующих компонент много — например в разработке игр.
- Подход к разработке некоторых сложных инструментов может сильно упроститься (например кастомная аналитика для сайта, интеграционные механизмы для компонент и др.).
Без минусов не обошлось:
- Совладать с такой архитектурой без подготовки достаточно сложно, особенно если приложение развито и там много таких реакций. Ведь по коллбэку мы всегда можем относительно быстро отследить что происходит, а с сообщениями код обработки может быть разбросан по разным частям приложени я и вызывать дополнительные реакции. Требуется высокая квалификация инженеров и если команд несколько, то наверняка для координации потребуются архитекторы.
- Отлавливать ошибки становится сложнее, особенно если некоторые события вызывают циклические или отложенные реакции. Т.е. нужно дополнительно инвестировать в стабилизацию системы (дополнительные тестировщики, дополнительные инструменты мониторинга)
Эти плюсы и минусы являются компромиссом для обуздания сложной системы. Чем хороши архитектуры и паттерны — тем, что делаю работу системы прозрачнее и более предсказуемой. А плохи тем, что добавляют дополнительные абстракции, что требует более высокой квалификации для понимания. Главный посыл — не нужно мудрить с архитектурой, следует использовать подходящую, а именно самую простую какую только можно с которой работа будет максимально удобной и быстрой. И стараться не пропустить момент, когда у вас уже появился ball of mud, а то его слишком сложно распутывать.