Реактивное веб-приложение с Kotlin и Spring

В этой статье я рассмотрю пример использования Kotlin, Spring и MongoDB при разработке полностью реактивного веб-приложения.

Reactive Web with Kotlin and Spring

Настройка проекта

Разрабатываемый проект будет реализовывать функциональность списка задач. Для проекта потребуются поддержка языка Kotlin и две дополнительные зависимости: Reactive MongoDB и Reactive Web

Созданный pom.xml будет выглядеть примерно так:

Предметная область будет представлена классом Todo:

Данный класс объявлен data-классом, что автоматически создаст для него методы equals(), hashCode() и toString(). В свойстве id будет идентификатор задачи типа java.util.UUID, в completed — булевый признак выполненности задачи, а в task — строковое описание задачи.

REST API

На стороне бекенда при обработке запросов мы будем использовать WebFlux.fn, легковесную функциональную модель программирования, позволяющую функционально настраивать маршрутизирование запросов между обработчиками. Для использования реактивных API MongoDB в классе-обработчике потребуется объект типа ReactiveMongoOperations:

В WebFlux.fn каждый метод, обрабатывающий запросы, принимает в качестве аргумента объект типа ServerRequest, а возвращает — объект типа Mono<ServerResponse>. Всю информацию о параметрах запроса можно получить из объекта типа ServerRequest, будь то параметр запроса, переменная пути или аутентифицированный пользователь.

Пример обработки запроса на получение списка задач:

На стороне фронтенда получить список задач в реактивном стиле можно при помощи fetch():

События

Наличие REST API позволит получать данные на стороне веб-фронтенда динамически с использованием JavaScript, но этого будет недостаточно для полноценной динамичности страницы. Как узнать, что другой пользователь внёс изменения в список задач? Для этого потребуется реализовать передачу событий от сервера к клиенту. Два самых очевидных способа реализации данной функциональности: при помощи WebSocket или SSE (Server Sent Events, события, отправляемые сервером). Вариант использования SSE в данном случае наиболее очевиден.

Первым делом нужно на стороне бекенда написать эндпоинт, который будет регистрировать подписки на события:

В качестве тела ответа на запрос на подписку на события мы возвращаем Flux, при помощи которого события будут отправляться клиенту. Для хранения подписок нам потребуется какой-нибудь контейнер, в данном случае списка будет достаточно. Подписка на событие представляет в данном случае собой объект FluxSink, при помощи которого можно отправлять объекты типа ServerSentEvent. Фактически, вместо объектов типа ServerSentEvent можно отправлять объекты любого другого типа, главное условие — HTTP-заголовок ответа Content-Type должен быть text/event-stream или application/stream+json.

После того как подписка на события зарегистрирована, при её помощи можно отправлять события клиенту:

Для работы с событиями на стороне веб-фронтенда используется класс EventSource, метод onmessage() которого обрабатывает события:

Настройка маршрутизации запросов

Последний штрих — настройка маршрутизации запросов на стороне бекенда. Для маршрутизации запросов в контексте приложения должен быть компонент типа RouterFunction. В рамкаж поддержки Kotlin в Spring 5 была добавлена поддержка Kotlin DSL маршрутизации, упрощающая конфигурирование маршрутизации HTTP-запросов.

Пример настройки маршрутизации:

В результате у нас получилось простое, но полностью реактивное веб-приложение. Ниже вы можете видеть пример его работы.

Полезные ссылки