Получение данных и логика сложных экранных форм
Всем привет! Меня зовут Алина. Я работаю аналитиком более 3-х лет, сейчас – в ГК Юзтех с продуктом Тил Эйчар. Это программа лояльности и мотивации персонала, которая позволяет снизить стоимость привлечения новых сотрудников и удержать текущих.
Эта статья будет полезна, в первую очередь, для бизнес и системных аналитиков, а также для дизайнеров, проджект менеджеров и даже тестировщиков. Аналитики смогут поразмышлять над описанными подходами в плоскости своих непосредственных задач, дизайнеры – над тем, в какой момент определяться с внешним видом компонентов для загрузки и ошибок, проджект менеджеры – над оценкой временных затрат на похожие задачи, а тестировщики – над структурой тест-планов для тестирования таких ЭФ.
Введение
Сегодня поговорим об экранных формах (далее – ЭФ), для отрисовки которых нужно получить данные из нескольких источников. В связи с этим возникает ряд вопросов, на которые нужно ответить прежде чем приступать к разработке. Например:
- Получать ли данные в рамках одного или нескольких эндпоинтов?
- Где реализовывать бизнес-логику (на фронтэнде или на бэкэнде)?
- Есть ли SLA (от англ. Service Level Agreement или Соглашение об уровне сервиса) у систем-источников данных и удовлетворяет ли он нас как потребителя?
Количество вопросов зависит от особенностей нашей системы и систем — источников данных. Без ответов на них нельзя даже сверстать чистовой дизайн, поскольку дизайнер не сможет выбрать правильные компоненты, иллюстрирующие загрузку ЭФ и ошибки.
На что обратить внимание перед принятием решения
Во-первых, выясняем у заказчиков бизнес-требования и сценарий того, как пользователь будет работать с ЭФ. Варианты можно разделить на такие:
- Пользователю нужно отобразить ЭФ полностью. Он не может перейти к следующему шагу, пока не увидит все данные на ЭФ;
- Пользователю можно отобразить не всю ЭФ. Для перехода к следующему шагу важно, чтобы загрузились некоторые её части, а остальное является лишь рекомендацией.
Важно проговорить и зафиксировать с заказчиками максимальное время загрузки ЭФ, а также что мы делаем, если в итоге нам вернулась ошибка.
Во-вторых, узнаём о SLA систем — источников данных. Оговорюсь, что мы разбираем ситуацию, когда у систем — источников данных уже есть API для интеграции с ними и получения нужных нам данных.
Итак, может сложиться ситуация, что у системы – источника данных попросту нет SLA или в существующем SLA не оговорены интересные нам аспекты. Нам нужно знать гарантированное время ответа при обращении к эндпоинтам, установленные настройки таймаутов, сроки устранения критических инцидентов, правила доработки API по запросу потребителей – всё это стоит иметь в виду при принятии окончательного решения.
Если нам не могут предоставить гарантии, а только некоторое среднее время ответа, полученное по результатам нагрузочного тестирования, и максимальное время недоступности системы, выведенное на основании последних инцидентов, то при превышении времени ответа над средней величиной или времени недоступности системы над ожидаемым временем спросить нам будет не с кого. Возникает риск превышения допустимого максимального времени загрузки нашей ЭФ.
В-третьих, изучаем принятые паттерны проектирования внутри нашей системы и/или организации. Кого-то мог удивить вопрос, связанный с тем, где реализовывать бизнес-логику, потому что невольно просится ответ: «На бэкэнде, конечно же, о чём разговор». В основном я встречала подходы, когда категорически запрещалось наличие бизнес-логики на фронтэнде. Причина была в её сложности и частом переиспользовании одних и тех же данных в разных частях приложения, которые не должны были отличаться. Вся бизнес-логика была вынесена на бэкэнд, и переиспользовались эндпоинты.
Однако в моей практике был случай, когда ЭФ №1 содержала в себе ЭФ №2 и на основании данных, которые приходили в ЭФ №2 на ЭФ №1 отображался статус сущности. Бизнес-логика в обеих ЭФ была идентичной и в обозримой перспективе мы не планировали её менять, а система – источник данных была очень зрелой и гарантировала нам все интересующие аспекты.
Мы приняли решение встроить одну ЭФ в другую, а не переиспользовать эндпоинты, потому что если на ЭФ №2 появлялся новый атрибут, то он должен был синхронно появиться и на ЭФ №1, а также они должны были быть единообразными по дизайну. То есть нам пришлось бы следить за действиями соседней команды и дорабатывать свою ЭФ в соответствии с их изменениями (ЭФ №2 активно наполнялась данными в тот период), когда нам нужно было всего несколько атрибутов, чтобы отобразить статус сущности.
Для дальнейшей дискуссии в качестве примера представим ЭФ, состоящую из 5 частей. Данные для 1-й, 2-й и 3-й её частей получаем из источника «А», для 4-й – из источника «Б», а 5-я часть – это некий светофор, который на основании данных остальных 4-х частей показывает зелёный, жёлтый или красный цвет.
Приступаем к проектированию
Рассмотрим вариант, когда данные для всей формы мы получаем в рамках одного эндпоинта.
На диаграмме изображен только основной сценарий (без нюансов, связанных с пагинацией и т.д.), если данные мы запросили и успешно их получили. Что делать в случае возникновения ошибки, зависит от того, нужно ли отобразить ЭФ пользователю полностью или допускается частичное отображение данных.
Если обязательно нужно отобразить ЭФ полностью, то при возникновении ошибки на любом этапе сценария получения данных можно «положить» весь запрос в ошибку, так как если нам не ответила хотя бы одна из систем – источников данных, то мы уже не можем рассчитать «светофор» для 5-й части ЭФ.
Если можно отобразить не всю ЭФ, и нам поступили данные, которые обязательно нужно вернуть пользователю, то мы можем вернуть успешный ответ, а вместо не пришедших данных «положить» в ответ исключение (-я).
Например, нам неважно значение «светофора» и данные из системы-источника данных «B». Критической является информация из системы-источника данных «А». Мы можем заложить в эндпоинт массив объектов exceptions, который будем заполнять, если при обращении к «B» возникла ошибка. Здесь важно также знать требования к логированию, чтобы массив объектов exceptions содержал все необходимые атрибуты для записи в журнал логов.
Это не конец сценария. Пользователь видит ЭФ, где в моём примере отображаются 1-3 её части, и решает, что ему недостаточно информации для перехода на следующий шаг. Мы с вами подумали о таком кейсе заранее и добавили на ЭФ кнопку «Повторить», но что произойдет при нажатии на неё? Мы будем заново запрашивать всю информацию полностью? А если у нас такой случай, когда данные совершенно независимы друг от друга и нам не критично, чтобы часть ЭФ была получена в Х ч YY минут, а часть – в Х ч YY минут + несколько минут?
Можно придумать некий query-параметр в виде ENUM и перечислять в нём список систем-источников, из которых мы хотим получать данные, но я бы в такой ситуации предложила разбить получение данных на несколько эндпоинтов. При наличии отдельного эндпоинта для получения данных из системы-источника «B» при нажатии на кнопку «Повторить» мы отправим простой запрос непосредственно к «B».
Подождите, а как же в случае с несколькими эндпоинтами соблюсти требование к обязательному отображению ЭФ полностью? Очень просто: отображать данные только тогда, когда будет получен успешный ответ от всех эндпоинтов.
Что касается 5-й части ЭФ, то я бы выделила отдельный эндпоинт для получения значения «светофора», поскольку логика расчёта этого атрибута может усложняться или на ЭФ могут добавиться новые атрибуты, значения которых зависят от данных разных систем – источников. На этапе развития ЭФ, который описан у меня в качестве примера, допускается вынесение логики на фронтэнд, но её будет сложно поддерживать при дальнейшем масштабировании.
Заключение
Для того чтобы правильно спроектировать ЭФ, нужно основательно подготовиться: обсудить бизнес-требования с заказчиком, изучить системы, из которых мы собираемся получать данные, а также паттерны проектирования и лучшие практики. Начать стоит ещё до момента подготовки чистового дизайна, чтобы правильно изобразить все состояния ЭФ. То, как получать данные (в рамках одного или нескольких эндпоинтов) и на чьей стороне заложить логику, зависит от многих факторов, каждый из которых стоит учитывать при принятии окончательного решения.