Сервис Akita — простое управление актуальным состоянием приложения

Akita

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

Раньше у нас было два варианта ее решения: 

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

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

На одном из недавних проектов мы перешли на сервис управления состоянием Akita — и это точно было удачное решение. Почему?

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

Система построена на RxJS, который массово использовался в проекте, поэтому для нас не возникло никаких сложностей. 

Косвенный, но немаловажный аргумент: изначально Datorama разработала сервис для собственного использования — а потому он сделан качественно. И затем, когда отлично себя показал, его выложили в бесплатный общий доступ.

Как работает Akita

В Akita три основных компонента.

  1. Хранилища Akita Store

Есть хранилища двух типов: 

  • базовое, которое можно использовать для хранения сессий юзера,
  • хранилище сущностей, которое обеспечивает удобное хранение массива объектов конкретного класса, например, категорий пользователей. 

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

2. Запросы Akita Query

Для запросов в хранилище используются расширения Query. Для начала использования Store не требуется создавать методы для выборки данных: просто создаем класс, расширяющий Query из Akita, и указываем в нем требуемое хранилище. После чего получаем доступ к встроенным методам для работы с данными в хранилище. 

Базовые классы содержат часто используемые методы: положить в хранилище, выбрать из хранилища. Например, в классе QueryEntity есть метод Select, который возвращает из хранилища то, что нам нужно, с помощью лямбда-выражения, и SelectAll, возвращающий все записи.

Набор методов можно расширить. Допустим, нужно выбрать юзеров, у которых есть какое-либо право доступа — внутри класса Query создаем метод getUsersWithPermission с указанием параметра разрешения. И возвращаем из Store юзеров, соответствующих параметру.

В результате расширение кода производится быстрее и проще, чем в NgRx.

3. Сервис и методы

Данные в хранилище мы не модифицируем напрямую. Akita реализует принцип “Single source of truth”: актуальные корректные данные лежат в одном месте, там же модифицируются, все компоненты получают их из одного источника. Значит, никакой компонент не может изменить у себя данные так, что другой об этом не узнает. 

При открытии страницы компонент в сервисе вызывает бэкэнд и достает данные, мы кладем их в Store. Для этого есть сервис, в котором используются специальные методы, которые модифицируют набор данных в хранилище. 

Например, метод Set полностью замещает все данные, которые хранятся в Store. Update может обновить даже единичную запись. Upsert обновит запись, если она есть в Store, и добавит, если ее нет. 

Удобна встроенная поддержка отслеживания состояния загрузки с использованием свойства loading. Ставим { loading: true }, на странице значение свойства проверяется и отображается прогресс-бар.

Метод withTransaction поддерживает взаимодействие компонентов, использование одних Store в других и одних Query в других. Например, если нескольким юзерам из таблицы присвоили несколько новых территорий — нужно обновить Store с юзерами и с территориями. Внутри транзакций мы обновляем оба Store, при этом никакие данные не потеряются.

Почему стоит выбрать Akita

Упрощается процесс разработки

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

Увеличивается скорость разработки

Написание нового кода и расширение имеющегося ускоряется в разы. Для запуска нужно создать три небольших файла: хранилище, сервис и запросы. Базовая функциональность создается очень быстро и может использоваться между компонентами: например, положить в хранилище lookup таблицу, чтобы один раз при загрузке страницы достать из базы. 

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

Стоит добавить, что чем выше скорость — тем меньше стоимость разработки.

Уменьшается количество ошибок

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

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

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

Таким образом, если посмотреть более глобально, то использование Akita дает выгоды всем сторонам, включая конечного заказчика.