EDA для PWA

Дата публикации: 2021-12-30

Когда разбираешься с особенностями работы событий сервера (Server Sent Events) рано или поздно сталкиваешься с Event Driven Architecture. Вообще-то, EDA — это “родная” для web-приложений архитектура. Настолько “родная”, что мы зачастую даже не замечаем её, добавляя JS-функционал в HTML-код:

Взаимодействие браузера (и нашего приложения) с пользователем основано на событиях. В общем, событийно-ориентированная архитектура для web’а — это то, что интуитивно понятно любому web-разработчику, даже если он никогда про неё не слышал (что вряд ли).

С другой стороны, любому web-разработчику так же интуитивно понятно взаимодействие браузера с сервером — браузер отправляет запрос на сервер и получает в ответ результат выполнения запрошенного действия:

Image 2

HTTP basics

В этом суть HTTP-протокола, альфа и омега взаимодействия клиента с сервером: отправил запрос — получил результат.

Что же предложил Ян Хиксон в далёком 2004-м? Он предложил растянуть ответ сервера во времени (создать поток) и обрабатывать на клиенте результат не единым целым, а по частям (по событиям), по мере получения информации. Развитие этой идеи привело к новому Web API — Server Sent Events.

Теперь информация с сервера на клиент может литься отдельным потоком, отображая процессы, происходящие на сервере:

Image 3

Server Sent Events

Если рассматривать связку “Browser — Server” с позиций EDA, то сервер представляет собой “Генератор событий” (Producer), браузер — “Подписчик на события” (Subscriber), а HTTP-соединение (и поддерживающий его с обеих сторон код) — механизм обработки событий (EventQueue).

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

Image 4

Events confirmation with HTTP

Подтверждения о получении событий можно отправлять обычным POST’ом на отдельный сервис — один сервис для всех событий (нужно только указать ID события). Т.е., получается, что на один канал событий сервера нам нужен один web-сервис с потверждением доставки сообщений о событиях.

Но проблема в том, что EDA требует от приложения устойчивости к потере событий и простым обратным подтверждением проблему потери события не решить. Подтверждение ведь тоже может потеряться. Приложение должно быть спроектировано таким образом, что потеря отдельного сообщения не должна приводить к сбою в работе. Допустим, если ожидаемой реакции на действие не произошло в допустимый интервал времени, то приложение просто ещё раз отправляет сообщение о событии — а там уже от Подписчиков зависит, кто успел в предыдущий раз среагировать на это событие, а до кого оно не дошло.

Но что интересного в предыдущей схеме? То, что все подтверждения идут на один сервис, по одному каналу. А если использовать этот канал, как развернутый SSE, и таким же асинхронным образом отправлять сообщения с браузера на сервер?

То есть, вместо того, чтобы делать множество web-сервисов, выполняющих различные действия на сервере и возвращающих результаты этих действий в синхронных запросах, сделать два канала: один прямой канал (сервис, который получает сообщения от браузера), а второй — обратный (события сервера, отправляемые браузеру):

Image 5

На мой взгляд, такое построение очень хорошо подходит для мобильных PWA, которые должны учитывать возможность работы в offline-режиме, а поэтому содержат набор рабочих данных на клиентской стороне (localStorage, indexedDb) и обмениваются данными с сервером по мере возможности.

Эта архитектура делает ненужым web-сервисы в приложении и сводят весь Web API к двум сервисам:

По сути, данная архитектура делает бессмысленным использование в приложениях, её реализующих, таких технологий, как REST, GraphQL, gRPC и им подобных (ориентированные на синхронное получение данных по схеме “запрос — ответ”).

На мой взгляд, EDA очень подходит для мобильных PWA приложений, но ориентация архитектуры на события требует полной перестройки характера сетевого взаимодействия клиента и сервера. Я не утверждаю, что EDA вытеснит REST, GraphQL, gRPC, я просто отмечаю, что для мобильных PWA с их постоянными переходами “offline — online” потоки событий, IMHO, подходят лучше, чем классический “запрос — ответ”. Для обычных web-приложений, работающих в среде с устойчивым интернетом, классические технологии являются более проверенными и прогнозируемыми.

Дополнительные фрагменты исходного кода

<button onclick="myFunction()">Click me</button>