В процессе разработки одностраничного приложения в реальном времени я постепенно внедрил веб-сокеты, чтобы предоставить своим пользователям новейшие данные. На этом этапе мне было грустно замечать, что я слишком сильно разрушаю структуру своего приложения, и мне не удалось найти решение этого явления.
Прежде чем углубляться в детали, просто немного контекста:
- Веб-приложение представляет собой SPA в реальном времени;
- Серверная часть находится в Ruby on Rails. События в реальном времени помещаются Ruby в ключ Redis, затем сервер микроузлов возвращает его и отправляет его в Socket.Io;
- Внешний интерфейс находится в AngularJS и подключается непосредственно к серверу socket.io в Node.
На стороне сервера до реального времени у меня было четкое разделение ресурсов на основе контроллера / модели с обработкой, привязанной к каждому. Этот классический дизайн MVC был полностью уничтожен или, по крайней мере, обойден, когда я начал пихать вещи через веб-сокеты своим пользователям. Теперь у меня есть один канал, в котором все мое приложение передает более или менее структурированные данные . И я нахожу это стрессовым.
На переднем крае основной проблемой является дублирование бизнес-логики. Когда пользователь загружает страницу, мне приходится загружать свои модели через классические вызовы AJAX. Но я также должен справиться с потоком данных в реальном времени, и я обнаружил, что дублирую большую часть своей бизнес-логики на стороне клиента, чтобы поддерживать согласованность моих моделей на стороне клиента.
После некоторых исследований я не могу найти хороших постов, статей, книг или чего-либо еще, что дало бы советы о том, как можно и нужно разрабатывать архитектуру современного веб-приложения с учетом нескольких конкретных тем:
- Как структурировать данные, которые отправляются с сервера пользователю?
- Должен ли я отправлять только события типа «этот ресурс обновлен, и вам следует перезагрузить его с помощью вызова AJAX» или отправить обновленные данные и заменить предыдущие данные, загруженные с помощью первоначальных вызовов AJAX?
- Как определить связный и масштабируемый каркас для отправляемых данных? это сообщение об обновлении модели или сообщение об ошибке "blahblahblah"
- Как не отправлять данные обо всем откуда угодно в бэкэнд?
- Как уменьшить дублирование бизнес-логики как на стороне сервера, так и на стороне клиента?
источник
Ответы:
Используйте шаблон обмена сообщениями . Ну, вы уже используете протокол обмена сообщениями, но я имею в виду структурирование изменений как сообщений ... в частности, событий. Когда серверная часть изменяется, это приводит к деловым событиям. В вашем сценарии ваши клиенты заинтересованы в этих событиях. События должны содержать все данные, относящиеся к этому изменению (не обязательно все данные просмотра). Страница клиента должна затем обновить части представления, которые она поддерживает, с данными события.
Например, если вы обновляли биржевую биржу, а AAPL изменились, вам не хотелось бы сбрасывать все цены на акции или даже все данные об AAPL (имя, описание и т. Д.). Вы только подтолкнули бы AAPL, дельту и новую цену. На клиенте вы затем обновите только ту цену акций в представлении.
Я бы сказал, нет. Если вы отправляете событие, продолжайте и отправьте с ним соответствующие данные (а не данные всего объекта). Дайте ему имя для такого рода событий. (Наименование и данные, относящиеся к этому событию, выходят за рамки механической работы системы. Это больше связано с моделированием бизнес-логики.) Ваши средства обновления представления должны знать, как преобразовать каждое конкретное событие в точное изменение вида (т.е. только обновление того, что изменилось).
Я бы сказал, что это большой открытый вопрос, который должен быть разбит на несколько других вопросов и размещен отдельно.
Тем не менее, в целом ваша внутренняя система должна создавать и отправлять события для важных событий в вашем бизнесе. Они могут поступать из внешних каналов или из активности самого сервера.
Используйте шаблон публикации / подписки . Когда ваш SPA загружает новую страницу, которая заинтересована в получении обновлений в реальном времени, страница должна подписываться только на те события, которые она может использовать, и вызывать логику обновления представления по мере поступления этих событий. Возможно, вам понадобится логика pub / sub на сервер для снижения нагрузки на сеть. Существуют библиотеки для публикации / публикации Websocket, но я не уверен, что это такое в экосистеме Rails.
Похоже, вам нужно обновить данные представления на клиенте и сервере. Я предполагаю, что вам нужны данные вида на стороне сервера, чтобы у вас был моментальный снимок, чтобы запустить клиент в реальном времени. Поскольку задействованы два языка / платформы (Ruby и Javascript), логика обновления представления должна быть записана на обоих языках. Помимо транспаранта (у которого есть свои проблемы), я не вижу способа обойти это.
Технический момент: манипулирование данными (просмотр обновлений) не является бизнес-логикой. Если вы имеете в виду проверку варианта использования, то это кажется неизбежным, поскольку проверки клиента необходимы для хорошего пользовательского опыта, но в конечном итоге не могут быть доверены серверу.
Вот как я вижу такую вещь, хорошо структурированную.
Просмотры клиента:
Клиентские команды:
На самом деле серверная сторона может быть разбита на несколько компонентов с ограниченной ответственностью. Тот, который просто обрабатывает входящие запросы и создает события. Другой может управлять подписками клиентов, прослушивать события (скажем, в процессе) и пересылать соответствующие события подписчикам. У вас может быть третий, который прослушивает события и обновляет представления на стороне сервера - возможно, это происходит даже до того, как подписчики получают события.
Я описал форму CQRS + Messaging и типичную стратегию для решения проблем, с которыми вы сталкиваетесь.
Я не включил Event Sourcing в это описание, так как не уверен, что это то, что вы хотите взять на себя, или вам это нужно обязательно. Но это связанный шаблон.
источник
После нескольких месяцев работы, в основном, с бэкэндом, я смог воспользоваться некоторыми советами для решения проблем, с которыми столкнулась платформа.
Основная цель при переосмыслении бэкэнда заключалась в том, чтобы как можно сильнее придерживаться CRUD. Все действия, сообщения и запросы, разбросанные по многим маршрутам, были сгруппированы в ресурсы, которые создаются, обновляются, читаются или удаляются . Сейчас это кажется очевидным, но это был очень сложный способ применить осторожно.
После того, как все было организовано в ресурсы, я смог прикрепить сообщения в реальном времени к моделям.
В Rest API все методы create, update, delete генерируют ответ только заголовок, HTTP-код информирует об успехе или неудаче и о фактических данных, передаваемых через веб-сокеты.
На внешнем интерфейсе каждый ресурс обрабатывается определенным компонентом, который загружает их через HTTP при инициализации, затем подписывается на обновления и поддерживает их состояние в течение долгого времени. Затем представления связываются с этими компонентами для отображения ресурсов и выполнения действий над этими ресурсами через одни и те же компоненты.
Мне показалось, что чтение CQRS + Messaging и Event Sourcing очень интересно, но мне показалось, что оно несколько усложнило мою проблему и, возможно, более приспособлено к интенсивным приложениям, где фиксация данных в централизованной базе данных - дорогая роскошь. Но я обязательно учту такой подход.
В этом случае приложение будет иметь несколько одновременных клиентов, и я взял на себя обязательство полагаться на базу данных. Самые изменяющиеся модели хранятся в Redis, который, я надеюсь, обрабатывает несколько сотен обновлений в секунду.
источник