Server-Driven UI (SDUI) — это подход для динамичного и гибкого пользовательского интерфейса, когда сервер посредством API сообщает приложению, какие компоненты и с каким контентом отображать. Он довольно популярен, и мы его тоже используем на многих экранах — помогает быстро выпускать фичи в продакшн. 

В статье покажу, на каких экранах мы его применяем, и расскажу, как развивались у нас подходы гибкого UI, какие плюсы и минусы мы вывели из его использования. Сначала рассмотрим формы на динамических полях, контракты и ​тонкие моменты создания новых полей. Потом поговорим про динамические флоу, как за ноль калорий на стороне фронта добавлять новых провайдеров для проведения оплаты и закончим зависимыми полями. Всё это с примерами экранов мобильного приложения. 

Об авторе:

Анна Саботович

В Android-разработке с 2010 года, техлид в Альфа-Банке

Динамические поля

Динамические поля у нас появились, потому что мы хотели иметь возможность настраивать состав экрана с бэкенда: когда форма меняется в зависимости от профиля пользователя и открытых на него фича-тогглов (feature toggle).

На картинке ниже вы видите примеры экранов на динамических полях. Сейчас разных полей уже больше 50, и с их помощью получается добиться большого многообразия UI. 

Они работают так:

  • от сервера приходит массив моделей с разными типами и набором параметров;

  • мы парсим этот массив и маппим на компоненты дизайн-системы;

  • в итоге получается экран, сверстанный на компонентах, состав которого легко и быстро меняется с бэка. 

Список динамо-полей можно сочетать со статической вёрсткой и вставлять его в любое место экрана.

Контракт динамических полей

Покажу пример ответа с динамическими полями.

В ответе от бэка на любой из ендпоинтов может прийти массив моделей динамических полей. Мы их различаем по полю type и маппим в разные модели, в зависимости от типа. 

Кроме типа у динамо-поля должны быть:

  • id — чтобы отличать поля друг от друга;

  • label — чтобы отрисовать заголовок поля.

Разные типы динамо полей маппятся в разные компоненты дизайн-системы, от простых, типа строкового заголовка, до сложных, например, поле выбора счёта.

Поля делятся на две категории: 

  • output поля, которые просто выводят данные, например HEADER или STRING

  • input поля — интерактивные: пользователь может поменять, например, INPUT, CHECKBOX, ACCOUNT_SELECT.

У интерактивного поля может быть булев параметр required. Если он true, то форма не будет отправлена на бэк, пока это поле не будет заполнено. Также у интерактивных полей может присутствовать модель, которая описывает правила валидации, например минимальная или максимальная длина строки, и любые регулярки.

Примечание. Также важна обратная совместимость. Мы решили, что если фронт по какой-либо причине не может распарсить модель от сервера, то мы не отображаем поле.

Новые поля

Продакты любят динамо-поля, поэтому всё время появляются запросы на новые типы. И тут возникает две проблемы:

Первая проблема: часто при получении дизайна нового поля думаем — заводить под это поле новый тип или лучше расширить какое-то из существующих?

Здесь мы стараемся соблюсти баланс между универсальностью поля и сложностью его контракта.

  • Если новое поле отличается минимально, то просто добавляем в модель новые параметры.

  • Если оно кардинально новое, или мы видим, как сделать его универсальным сразу, то заводим новый тип и придумываем контракт для него. 

На картинке показали примеры полей с разными типами, хоть они во многом и похожи. Последнее поле — самое новое. Оно в итоге объединит первые два.

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

Как раз динамо-поля помогли нам решить задачу быстрой и гибкой настройки контента экрана. 

Когда у бизнеса появился запрос настраивать не только состав одного экрана, но и вообще порядок следования экранов друг за другом и зависимость контента следующего экрана от результатов пользовательского ввода на предыдущем, у нас появился многошаг.

Многошаг

Это апишка, которая на вход принимает данные с текущей формы, а на выход отдает набор динамо-полей для следующего экрана. 

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

Этот многошаг состоит из трёх шагов:

Шаг №1. Пользователю приходят динамо-поля для ввода имени, даты рождения, электронной почты и дисклеймер. Также в респонсе есть модель для настройки кнопки внизу экрана. 

  • При нажатии на неё сначала происходит валидация формы на фронте по правилам, которые зашиты в каждое динамо-поле.

  • Затем формируется массив с id полей и введенными в них данными. 

  • Этот массив отправляется в API многошага, а он отдаёт новый массив динамо-полей. Например, на втором шаге могут прийти поля для ввода паспорта, если человек старше 14 лет, или для ввода свидетельства о рождении, если младше 14.

Шаг №2. От сервера приходят данные для ввода паспорта. После ввода данных, пользователь нажимает кнопку «Продолжить» и всё повторяется: валидация, сбор значений полей в массив, отправка на бэк, получение данных для нового экрана.

Многошаг будет продолжаться пока в ответе не придёт признак, что текущий шаг последний.

Шаг №3. Последний шаг может быть просто терминальным экраном, например финальным, с информацией, что всё заполнено. А может быть экраном, ведущим на флоу подтверждения оплаты.

Вырожденный вид многошага — одношаг: вторым шагом он всегда переходит на финальный экран.

Многошаг — это уже более строгая апишка. По сути, это одна активити с ресайклером, так что рисовать на ней мы можем только динамо-поля, никакой дополнительной статической верстки быть не может.

Все платежи в пользу сторонних провайдеров в Альфа-Мобайл у нас реализованы на многошаге. При выборе провайдера, например, ЖКУ Москвы или оплаты штрафов, вызывается диплинк, который ведёт на базовый экран многошага и дергает API с Query-параметрами из диплинка.

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

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

Для решения этой задачи мы сделали поля зависимыми друг от друга.

Зависимые поля

Перед вами пример, который стал пилотом для реализации зависимых динамо-полей. Здесь стояла задача гибко настраивать подписку, например на штрафы. Если пользователь выбрал свитчер «Оплачивать счета автоматически», то мы показываем ему поле с выбором счета списания и возможностью установить лимит платежа. 

Если пользователь выбрал установить лимит, то показываем ему поле ввода лимита.

Чтобы это реализовать, мы сделали надстройку над массивом динамо полей, в которой записаны правила зависимости полей друг от друга и их поведение. Эту надстройку назвали хэндлеры. Они приходят при загрузке экрана вместе со списком полей и обновление экрана происходит локально, без дополнительных запросов на бэк.

Схема работы зависимых полей

Выглядит так.

Сейчас расскажу подробнее, как это работает. 

  • От бэка приходит массив полей и хендлеров, экран отрисовывается. 

  • Пользователь взаимодействует с каким-либо полем, например меняет свитчер. Выбрасывается событие с id измененного поля и типом произошедшего события. 

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

  • Дальше проверяются условия срабатывания хэндлера, например, свитчер включен или выключен.

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

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

Рассмотрим подробно каждый элемент хэндлера.

Триггер

Определяют, для каких событий должен срабатывать данный хэндлер.

Триггеры содержат параметры:

  • id поля, по которому определяется, от какого поля должно прийти событие на обновление;

  • тип события, по которому определяется, по какому типу события должен срабатывать хэндлер, например, SELECT, CHANGE или CLEAR.

Когда какой тип триггера срабатывает:

  • SELECT — когда выбрано значение из выпадающего списка;

  • INIT — сразу же после получения массива динамо полей;

  • CLEAR — при очищении поля;

  • CHANGE — при изменении значения в поле.

Условия

Условия — это логические выражения, которые проверяют и сравнивают данные с формы. Если они истинны, то хэндлер выполнится.

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

Некоторые из реализованных условий:

  • EQUALS — проверка на равенство;

  • CONTAINS — равно одному из значений из списка;

  • NOT — отрицание;

  • AND / OR / LESS — и/или/меньше.

Операции

Операции описывают список действий, которые должен сделать хэндлер.

Если операций несколько, то они исполнятся в том порядке, в котором указаны в JSON. Все существующие операции синхронные, но мы подумываем об асинхронных походах в сеть.

Некоторые из реализованных операций:

  • HIDE_FIELDS / SHOW_FIELDS — скрыть или показать поля из списка;

  • SET_VALUE — установить значение в поле;

  • SET_REQUIRED — сделать поле обязательным (или нет).

Итоги

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

К плюсам я бы отнесла:

  • Быструю реализацию новых фич на уже существующих компонентах.

  • Быстрый багфикс и поставка изменений клиенту.

Минусы тоже присутствуют:

  • Большой оверхед при разработке на старте. Приходится долго согласовывать контракты между всеми платформами и стараться всё время думать об универсальности и расширяемости.

  • Много логики и на бэке и на фронте, это даёт высокий порог вхождения для новых разработчиков.

  • Дизайнеры часто упираются в ограниченность разработанных компонентов и хэндлеров, что влечёт постоянный выбор: «ехать» на реализованных компонентах и упростить дизайн, расширять компонент или вообще делать новый.

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

Этот пост сделан на основе недавнего доклада Анны Саботович на конференции Mobius. Если захотелось посмотреть этот доклад целиком и вообще приобщиться к конференции — 22 июня у Mobius пройдёт офлайн-день в Санкт-Петербурге. Если приобрести билет на него, то заодно получите видеозапись доклада Анны и многих других. А если хотите больше узнать о работе в Альфа-Банке — обратите внимание на Telegram-канал Alfa Digital Jobs. Там мы интересно и весело рассказываем про нашу работу, делимся новостями и полезными советами, иногда даже шутим.

Рекомендуем почитать другие статьи:

Комментарии (15)


  1. DevilDimon
    01.06.2022 10:37

    Backend-driven UI для мобильного разработчика - абсолютное зло, которое превращает нативщика, учитывающего тонкие детали и отличия платформы в создателя очередного парсера.

    Некоторые BDUI даже с сервера получают layout и реализуют свой декларативный велосипед для его отображения, собирая те же грабли, что html, RelativeLayout, autoresizing masks и другие достижения UI-рендеринга прошедших десятилетий.

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


    1. boronov
      01.06.2022 13:49
      +1

      Не знаю насколько это зло, но для бизнеса это довольно крутая штука позволяющая быстро что-то внедрять / обновлять. Насчет разработчиков согласен, лично я бы не стал долго таким заниматься, очень скучно )


      1. Fantasi_Anna
        01.06.2022 15:31

        Как здорово, что в мире разработки найдутся задачи на любой вкус и интерес ????


    1. Fantasi_Anna
      01.06.2022 15:26
      +1

      на вкус и цвет все фломастеры разные, мне лично нравится учавствовать в разработке такого фреймворка, там и нативных штук много и сложных инженерных решений


      1. rdevjke
        01.06.2022 20:04

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


  1. Ab0cha
    01.06.2022 15:54
    +2

    Отличная статья, было интересно !)


    1. Fantasi_Anna
      02.06.2022 10:45

      Спасибо! Очень рада, что нашли что-то полезное для себя ????


  1. Simipa
    01.06.2022 17:26
    +2

    О, мы такое в Сберсервисе делали в 20 году, даже и не знал что это какой-то устоявшийся паттерн)


    1. Fantasi_Anna
      02.06.2022 10:45

      Вы сделали SDUI еще до того, как это стало мейнстримом ????????


  1. AzatSabirov
    01.06.2022 20:04
    +1

    Узнал много нового! Круть!! )))


    1. Fantasi_Anna
      02.06.2022 10:46

      Класс! Рада, что оказалось полезно!


  1. edyanakov
    02.06.2022 13:25

    Очень интересно!


  1. IrinaCold
    03.06.2022 09:20

    Спасибо за статью, было интересно почитать о таком подходе


  1. Washburne
    03.06.2022 11:37

    Вот вроде постик неплохой, но блин, Альфа, серьезно, зачем забивать комменты дешёвыми ботами и 24*7 восторгающимися сотрудниками по поводу и без? Я не так чтобы давно читаю хабр, постоянно последние месяцев 7, но такого себе даже упоротые хостеры не позволяли, кажется. Хотя их корпблоги это отдельное. У вас опять директора с пиаром обновились, что ли, и раз не моргентшерны, то накрутки?

    Вот этим мусором вы превращаете пост во что-то среднее между отзывами на алиэкспрессе и каким-то виси.

    Jug.ru, вы-то как это одобрили, у вас обычно максимально техничные и вылизанные (в хорошем смысле) посты :(

    Поставил свой первый минус к посту с момента регистрации на Хабре.


    1. AlfaTeam Автор
      03.06.2022 11:57

      Здравствуйте! Приносим извинения за такой казус. Мы не просили специально забивать комментарии, пара человек хотели поддержать Анну и ее статью. Наша вина, что мы забыли предупредить, что так делать не стоит и можно поддержать другими способами. Мы бы и предупредили, если бы специально нагоняли комментарии. Мы же понимаем, что такой способ добавлять комментарии не самый эффективный, кринжовый можно сказать, и он не работает, и делали бы иначе, если бы специально действовали по такой стратегии. В данном случае, как уже говорили, специально не нагоняли комментарии, поэтому и получилась такая ситуация.