Один мой друг-архитектор жилого дома однажды сказал мне, что у каждого здания ровно две стороны: видимая и невидимая. Это то, о чем вы, вероятно, думали, если у вас когда-либо был жуткий опыт посещения нескольких кондоминиумов в одном здании: план этажа, структура и фундамент идентичны, но плитка, краска и отделка расходятся. с годами. Поэтому я спросил своего друга: «А как насчет противоположного? Если вы построили визуально идентичные дома, например, с совершенно другим фундаментом? Какой дом будет лучше? »Он засмеялся и сказал:« Тот, который рухнет последним! »

Недавно я подумал о шутке моего друга с точки зрения программного обеспечения, и, ну, он был прав.

Миссия

Еще в сентябре 2020 года моей команде было поручено добавить в наше медицинское программное обеспечение функцию: многоэтапную форму (также известную как мастер) для проверки личности пациента. Как правило, в начале консультации врачу необходимо отсканировать карточку социального обеспечения своего пациента, связать ее с нужным файлом пациента, вручную заполнить важную информацию, если она отсутствует (имена, дата рождения, пол), и - почему бы и нет. - объединить дубликаты файла пациента, если наше программное обеспечение их обнаружит.

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

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

«Круто», - сказал я своему менеджеру по продукту, - «амбициозно, но круто. Давайте построим что-нибудь красивое! »

«О, - сказал он, - кстати, нам нужна первая версия для бета-тестирования, которую мы запускаем через три недели».

'КАКИЕ?'

(диалоги преувеличены для драматического эффекта)

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

Версия 1: простая последовательность модальных окон

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

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

Вердикт?

Что ж, сработало. Мы отправили вовремя. Наш «дом» выглядел (и вел себя) так, как мы этого хотели. Этого было достаточно для бета-версии, но, заглянув на шаг вперед, мы уже могли понять, почему изделия из дерева не выдерживают:

  • Код было очень трудно читать и поддерживать. Кто-то, глядя на код любого из модальных окон, едва ли мог сказать, что это часть мастера. Даже если бы вы знали о существовании волшебника, вам нужно было бы пройти квест по нескольким файлам, чтобы выяснить его шаг, не говоря уже о понимании тонкой логики пропуска того или иного шага.
  • Наш мастер не был гибким. Хотите вставить новую ступеньку между двумя другими? По сути, вам нужно все отключить и снова подключить. Хотите иметь две версии потока с немного разным поведением? Будьте готовы раздувать полезные нагрузки еще больше с помощью троичных файлов. Хотите иметь возможность переключать файл пациента, с которого вы начали, на полпути, если вы понимаете, что это не тот файл? Нет, обратные вызовы вычислялись с самого начала мастера. На самом деле, волшебник должен был в конечном итоге делать все эти вещи, так что, да ладно.
  • Было несколько нежелательных действий из-за того, что наш мастер не имел состояния, наиболее раздражающим из которых было отсутствие возврата к предыдущему шагу (опять же, возможно, но кропотливо кодировать, поэтому он не сделайте это в версии 1).

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

Версия 2: Как конечный автомат 🎶

Мы ставим две основные цели для нашей второй итерации мастера:

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

В конце концов, цель состояла в том, чтобы мы могли управлять мастером из любого места, например:

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

Мы запускаем наш компонент мастера с массива шагов, каждый из которых представлен объектом, содержащим два обратных вызова, effect и shouldSkip. Вот пример:

И мы вводим переменную состояния currentStepIndex. Видите, к чему это идет? Когда вас просят запустить «следующий шаг», мы продвигаемся вперед по списку шагов, пока не найдем тот, который нельзя пропускать, и активируем его.

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

Вот и все.

Обратите внимание, что наш компонент мастера не отображает никаких визуальных компонентов. Он использует наш openModal API для представления разных модальных окон на каждом этапе. Единственный JSX, который мы визуализируем, - это поставщик контекста React:

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

Как мы и хотели! И теперь редактировать шаги в мастере так же просто, как добавлять, удалять, менять местами или изменять элементы в массиве STEPS.

Выводы

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

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

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

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