Наверное, вы уже не раз слышали о мобильной разработке на ReactNative в Единой Фронтальной Системе (ЕФС) Сбербанка. Мы уже писали для чего мы используем саму технологию ReactNative в своих разработках, а также рассказывали как не бояться это делать.



Сегодня мы взглянем с высоты птичьего полета на построение архитектуры мобильных приложений с использованием ReactNative, Objective-C, Typhoon, VIP, SOA, TypeScript, React и Redux.

Об архитектуре и её предпосылках


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

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

Обычно в подобных коллективах и компаниях решения принимаются быстро и строятся путем перебора различных гипотез и экспериментов.

Стоимость изменений достаточно мала, а скорость достаточно велика. При этом комплексная производительность остается небольшой, но это и не требуется в компаниях подобного масштаба.

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

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

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

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

и др.

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

Задачка: «Строим  город»


Давайте решим архитектурную задачку:

Требуется спроектировать и построить город. Город — это Сбербанк.

image

В городе есть дома, в которых живут люди и различные организации/учреждения, в которых люди приобретают товары или получают услуги. Люди — это клиенты Сбербанка.

Дома, в которых живут люди и заведения, которые они посещают — это интерфейсы от Сбербанка, которыми они пользуются.

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


Вода, газ, электричество, а также продукты и прочие товары каким-то образом поставляются людям в дома и магазины — и это делается благодаря мощной back-end инфраструктуре, которая сокрыта от клиента (водонапорные станции, ГЭС, АЭС, заводы, складские помещения и т.д.)

А теперь к требованиям архитектуры города:

  • Количество людей постоянно растет и людям требуются новые дома для проживания;
  • Ваши дома должны быть прочными и стоять многие десятилетия;
  • Нужно не забывать делать обновления и ремонт;
  • Жильцы даже самого старого дома хотят иметь возможность пользоваться новыми услугами;
  • С увеличением количества людей увеличивается и общая потребность в продуктах и услугах;
  • Люди не хотят ждать — они хотят получать продукты и услуги максимально быстро;
  • Клиенты непостоянны: они хотят новых продуктов и услуг.

Мы с вами архитекторы — специалисты, которые строят этот город и решают все встающие проблемы и задачи. Задача не из лёгких, но крайне интересная.

Её решение не может быть полностью охвачено одной статьёй, поэтому сперва давайте поговорим о том, как мы строим дома на практике!

Как мы строим дома или 3 уровня архитектуры


Итак. Нам нужно строить дома. Строить быстро, но при этом качество домов должно быть на высшем уровне. Дома должны быть современны и удобны (т.е. удовлетворять требованиям UX). Мы должны обеспечить возможность быстрого вывода новых услуг даже в старые дома.

Дома, как мы говорили — это интерфейсы ЕФС. Интерфейсы являются канально-специфичными, например, есть мобильные и web-интерфейсы, контактный центр, отделения, банкоматы и т.д.

Одна из ключевых задач программы ЕФС — создать возможность быстрого вывода услуги во все каналы (омниканальный вывод), обеспечить узнаваемость и единый визуальный стиль интерфейсов для взаимодействия с клиентом.

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

Давайте вернёмся к нашему сравнению с градостроением и зодчеством.

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

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

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

В результате получаем 3 основных уровня:

  • дом
  • конструкция
  • материал

В нашем мире архитектуры мобильного PL (Presentation Layer) есть также 3 уровня:


Компоненты — это строительные материалы


Наши мобильные интерфейсы строятся из атомарных (неделимых) сущностей, которые мы называем компонентами.

Несколько основных наших правил при проектировании компонентов:

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

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


Каждый компонент представляет из себя полнофункциональный, законченный, не зависящий от других модуль. Для реализации модулей на нативном Objective-C коде мы используем VIP-архитектуру (View, Interactor, Presenter).

За основу мы взяли VIPER-архитектуру, которая хорошо предназначена для создания отдельных модулей и их дальнейшего переиспользования. Мы удалили Router, т.к. переходы между модулями осуществляются на уровне прикладного кода, т.е. TypeScript.

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

Давайте разберём небольшой пример работы всей цепочки на основе компонента TextInput.

TextInput — простой компонент, предназначенный для ввода в него текста.

Компонент визуальный, поэтому для отображения его на экран необходимо в методе render() родительского компонента вставить JSX-тег .

На текущий момент компонент имеет 14 свойств:
Свойство
Тип
Описание
maxLength
number
Максимальное количество символов  
value
string
Значение, отображаемое в поле. Может быть использовано как для задания первоначального значения так и для последующего контроля над содержимым.
label
string
Указанный текст будет выведен в виде подписи над полем ввода
placeholder
string
Placeholder, выводимый в Input, если значение не задано.
disabled
bool
Данный атрибут отключает элемент и не позволяет пользователю его редактировать
keyboardType
UFSLibrary.KeyboardType
Тип клавиатуры из словаря KeyboardType.
onChange
function
Вызывается каждый раз, когда пользователь меняет значение в поле
underlined
bool
Задает отображение горизонтальной линии под компонентом. По умолчанию значение свойства установлено в «true».
onFocus
function
Вызывается каждый раз, когда поле становится активным.
onBlur
function
Вызывается каждый раз, когда поле выходит из активного состояния.
hasError
bool
Задает компоненту состояние ошибки (title и подчеркивание окрашиваются в красный цвет).
errorText
string
Сообщение, отображаемое в состоянии ошибки компонента.
hasWarning
bool
Задает компоненту состояние предупреждения при условии, что hasError = false (title и подчеркивание окрашиваются в желтый цвет).
warningText
string
Сообщение, отображаемое в состоянии предупреждения компонента.

Наше внимание нужно сконцентрировать на двух: text и onChange. Эти свойства важны для демонстрации одного из основных принципов, принятых в React и соответственно в Мобильной библиотеке: компонент не хранит состояний, т.е. является Stateless.

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

Другими словами, если мы будем вводить текст в компоненте TextInput свойство text не изменится. Для корректного поведения нам нужно сохранять возвращаемое значение newText из колбека onChange в Redux-store и переназначать его в свойство text компонента TextInput. Прочитайте предыдущую фразу ещё раз, а потом посмотрите на картинку снизу, которая это демонстрирует.


.TS — интерфейс компонента на TypeScript
RN — ReactNative
VM — View Manager (класс для работы ReactNative)
A — Typhoon Assembly, служащая для сборки нативного VIP-модуля
P — Presenter
I — Interactor
V — View

Viewless компоненты или сервисы


Компоненты для построения интерфейсных форм — не единственные компоненты. Для реализации полноценных мобильных приложений, а не просто свёрстанных экранов, нам необходимо иметь возможности по работе с сетью, базами данных, файлами, нотификациями и т.д.

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

Примером компонента данного типа может служить компонент Log.

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

При построении Viewless компонентов мы придерживаемся стандартных принципов мобильной SOA архитектуры: т.е. создаем ряд классов-сервисов и ряд core-классов. Классы-сервисы могут использовать core-классы, но не могут использовать классы-сервисы. Кстати, эти же классы-сервисы могут быть использованы интеракторами VIP-модулей визуальных компонентов.

Ниже представлена схема как выглядит цепочка вызовов, скрывающихся внутри компонента Log на нативном уровне.


.TS — интерфейс компонента на TypeScript
RN — ReactNative
B — Bridge (класс для работы ReactNative, наследует протокол RCTBridgeProtocol)
A — Typhoon Assembly, служащая для сборки нативного Viewless модуля
S — класс-сервис
C — core-класс

Мы рассказали о двух принципиально различных типах компонентов, которые у нас есть: визуальные и невизуальные (компоненты-сервисы). Пришло время рассказать о том, как организованы компоненты и где они хранятся.

Место, где живут компоненты — библиотека компонентов


Компоненты не живут в хаосе — есть централизованное хранилище, которое объединяет их, предоставляет к ним доступ и обеспечивает их комплексную работоспособность. И это хранилище — библиотека компонентов.

Библиотека компонентов — это нативный скомпилированный фреймворк, который статически включается в конечное приложение. Как было сказано ранее каждый компонент библиотеки, доступный для использования, имеет интерфейс на TypeScript.

Библиотека размещена в корпоративном репозитории Nexus и подключается в проект как npm-пакет. Чтобы облегчить жизнь JavaScript-разработчиков, которые не имели (а главное и не должны) опыта работы с нативным кодом Objective-C, средой Xcode и многим другим мы реализовали целый ряд скриптов, которые на post-фазе npm install автоматически линкуют фреймворк, конфигурируют .xcodeproj и собирают базовую обвязку для реализации прикладного кода.

Библиотека — это не только набор визуальных и не визуальных компонентов, но и набор вспомогательных файлов, содержащих общую часть (базовые классы, декораторы, классы-хелперы, макросы).

Библиотека имеет разделение на 3 уровня, которые мы называем PL (Presentation Layer), BL (Bridge Level), SL (Service Layer).

PL — это полноценные VIP-модули, т.е. компоненты для построения интерфейсов.

BL — это промежуточный слой, который служит адаптером для использования компонентов с прикладного уровня на TypeScript. К этому уровню относятся прикладные интерфейсы (.tsx), классы необходимые для исполнения ReactNative (ViewManager, ShadowView и т.д.)

SL — это SOA слой, состоящий из классов-сервисов и core-классов. Сервисы могут быть использованы с прикладного уровня, для этого существуют Viewless компоненты, которые через BL предоставляют доступ к нативным вызовам внутри классов-сервисов. Также сервисы могут использоваться VIP-модулями на нативном уровне через свои интеракторы.

На схеме показано взаимодействие всех трех уровней между собой:


Из незнакомых букв на схеме присутствует только W. W — это Wireframe, класс, соединяющий VIP-модули на нативном уровне. Тема достаточно интересная и большая, поэтому обязательно расскажем об этом в следующий раз.

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

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

Учимся строить приложения


Имея библиотеку готовых компонентов, можно с лёгкостью собирать мобильные приложения, реализующие типовые бизнес-задачи. Мобильные приложения теперь могут собирать разработчики с компетенциями в TypeScript, а библиотека «защищает» их от нюансов разработки под мобильные устройства.

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

Мы предоставили прикладным разработчикам и дизайнерам-проектировщикам набор ЛЕГО-кубиков, из которых можно выстраивать интерфейсные формы и приложения.

Но нам, конечно же, показалось этого мало и мы захотели переиспользовать ещё больше.

На текущий момент приложения, которые используют библиотеку — это приложения для внутренних сотрудников банка (операционистов, выездных специалистов, коллекторов, финансовых советников и т.д.). Каждый из сотрудников имеет мобильное рабочее место (МРМ), в котором он работает. Т.е. МРМ — это набор некоторых реализованных функциональностей, решающих ряд задач определенной роли сотрудника.

Пример показывает возможные задачи, которые может решать сотрудник в МРМ и процессы, с которыми он сталкивается.


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


Т.е. мы видим, что есть целые цепочки или разделы, которые должны присутствовать во всех МРМ независимо от роли сотрудника. В нашем понимании это и есть те самые общие конструкции, из которых быстро строится конечный дом (в нашем случае МРМ).

Как мы уже говорили ранее, конечное приложение разрабатывается на стеке TypeScript, React + Redux, следовательно «вырезанная» из приложения часть разрабатывается на том же стеке.

Получается, что общие разделы внутри МРМ — это часть кода, написанная на TypeScript, использующая нашу библиотеку мобильных компонентов.

Отлично! Осталось оформить эту часть кода в виде чего-то законченного, дать ему название и начертить схему. Имя появилось очень быстро, так как оно давно существует в мире JavaScript — JS Bundle.

Появляется новый слой — переиспользуемые JS Bundle, которые также хранятся в корпоративном репозитории Nexus. Прикладной разработчик уже конечного МРМ приложения в package.json указывает зависимости от JS Bundle и на этапе компиляции включает их в свой конечный код.

Данный подход позволяет переиспользовать JS Bundle, но вводит одно существенное неудобство для бизнеса: из-за статического включения JS Bundle растет количество МРМ, которые предназначены для работы тех или иных ролей сотрудников.

Получается следующая схема переиспользования:


При этом каждый JS Bundle (оранжевый цвет) обязательно использует в своей реализации Библиотеку компонентов (желтый цвет).

Что дальше?


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

В ближайшем будущем мы будем заниматься двумя монструозными задачами:

  1. Кроссплатформенная библиотека (mobile и web) компонентов;
  2. Динамическое мобильное рабочее место.

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

Идея простая — мобильное приложение одно и вся требуемая функциональность динамически загружается в зависимости от роли.

Задача выполнима именно из-за использования гибридной технологии ReactNative. Прямо сейчас в Release-сборке JavaScriptCore интерпретирует входные JS-файлы, расположенные в локальном NSBundle.

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

Задачи, которые должны быть реализованы в этой новой системе намного шире. Помимо управления зависимостями и собственно сборки МРМ на лету, СМ должен решать задачи SSO между всеми JS Bundle, использования единого API, «доставки» справочных запросов на МРМ и многое другое.

Ждём тебя!


Итак! Планы наши амбициозны и масштабны, а фронт работ велик. Команде Мобильной платформы ЕФС требуется помощь в виде классных мобильных специалистов различных направлений.

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

  • нативные iOS-разработчики с хорошим знанием Objective-C и желанием развиваться горизонтально и освоить новые технологии по front-end как React, ReactNative, Redux;

  • мобильный тестировщик с желанием писать автоматизированные UI и нагрузочные тесты, а также всяческими изощрёнными методами ломать библиотеку компонентов;

  • back-end разработчик с уверенным знанием Java для реализации платформенных сервисов для работы всех мобильных приложений ЕФС.

Присоединяйся к нашей команде!

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

Ртищев Евгений ESRtishev.SBT@sberbank.ru
Островская Анастасия ASOstrovskaya.SBT@sberbank.ru

Поделиться с друзьями
-->

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


  1. hose314
    30.03.2017 10:15
    +1

    При прочих равных, кто по вашему больше подходит для разработки под react-native? iOS разработчик со знанием React/Redux/etc. Или frontend разработчик с поверхностыми знаниями iOS/Android.


    1. katleta
      30.03.2017 10:55
      +2

      Привет!

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

      Если приложение простое и компонентов от FB хватает — то хороший front-end разработчик, знающий React и Redux напишет приложение достаточно быстро.

      Если же нужно вносить модификации и работать с ReactNative на нативном уровне, то по нашему опыту этим должен заниматься уверенный iOS-разработчик, который уже разобрался с жизненным циклом React.


  1. hose314
    30.03.2017 10:32

    И самый острый вопрос, что вы используете в качестве провайдера навигации? Что вы думаете на счет такого подхода?


    Спасибо.


    1. katleta
      30.03.2017 10:58

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

      • локальная, основанная на UINavigationController, чтобы сохранять нативный UX
      • back-end driven, основанная на работе state-машины, именуемой Workflow


      1. hose314
        30.03.2017 12:02

        Это будет интересный парт для чтения, спасибо.


  1. Bimawa
    30.03.2017 11:50

    А у вас дедлайны есть?


    1. katleta
      30.03.2017 12:06

      Привет!

      У нас есть релизный цикл: определенные правила и даты, когда мы выпускаем версии.
      Например, у нас есть 4 больших релиза в год — это квартальные релизы.
      У нас есть итерационные релизы, которые происходят по окончанию каждого спринта, т.е. 1 раз в 2 недели.
      Также у нас есть релизы с хот-фиксами, которые не привязаны к датам, т.к. происходят при необходимости выпустить патч с исправлением ошибок каких-то версий.

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


      1. Bimawa
        30.03.2017 12:09

        Да, привет! ^_^
        Ну есть какая-то дата, когда можно сие чудо пощупать? Или, будет ли opensource? и когда?


        1. katleta
          30.03.2017 12:18

          Сейчас мобильная платформа для внутреннего использования.
          Как только какой-то из модулей выйдет в opensource — мы обязательно об этом расскажем.
          Сейчас чудо можно пощупать только работая внутри команды ЕФС :)


          1. Bimawa
            30.03.2017 12:19

            >Сейчас чудо можно пощупать только работая внутри команды ЕФС :)

            Блин за этот месяц меня еще так не хантили ^_^


  1. ilinsky
    30.03.2017 14:59

    Грамотно.