javascript

За закрытой дверью фронтенда ЕФС

  • суббота, 8 апреля 2017 г. в 03:14:56
https://habrahabr.ru/company/efs/blog/325916/
  • Разработка мобильных приложений
  • Программирование
  • ReactJS
  • JavaScript
  • Блог компании Программа «Единая фронтальная система»


В этой статье мы расскажем о библиотеке компонентов Единой фронтальной системы (ЕФС)  и как в целом устроен фронтенд платформы.



Одной из основных задач программы ЕФС является трансформация всех фронтальных систем к единому технологическому стеку. Фронтальная система в нашем контексте это интерфейс, через который любой пользователь взаимодействует с банком. Это может быть интернет-банк — многим известны приложения Сбербанк Онлайн и Сбербанк Онлайн для бизнеса, — банкоматы, терминалы, интерфейсы операторов в отделениях и call-центрах и другие системы, которыми пользуются многие тысячи клиентов и сотрудников банка в России.

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

Очевидно, что это неудобно для всех: для клиентов, сотрудников и банка в целом.
Поэтому главная миссия фронтенда ЕФС – заменить существующую сборную солянку и привести все к единой кодовой базе, к единому технологическому стеку с удобным и понятным пользовательским сценарием.

Какие задачи стоят перед разработкой?


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

  • Отказоустойчивость. Здесь фигурирует такая цифра, как 99,99%, это примерно 52 минуты в год, когда мы можем приостановить работу системы.

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

  • Быстрота работы и внедрения, так называемый  time-to-market.

До эры ЕФС время вывода нового технического продукта на рынок могло составлять до 1 года.  С ЕФС мы ставим себе задачу сократить  time-to-market  до 1 месяца.
И это только начало!

Как устроен фронтенд в ЕФС?


У нас есть команда разработчиков платформы ЕФС, а также прикладные разработчики, задача которых – реализовать бизнес-логику.

Команда платформы разрабатывает библиотеку UI-компонентов для внутреннего использования. Примеров подобной разработки довольно много — у таких компаний, как Google, Yandex, Avito, Mail.ru и др. также есть библиотеки компонентов. Команды же прикладных проектов используют эту библиотеку для реализации своих проектов, предоставляя фидбек в случае проблем.
В команде платформы сейчас 8 человек. Мы работаем двухнедельными спринтами, в конце каждого из них   выпускаем новую версию библиотеки, в которой содержатся фиксы  и, возможно, новые компоненты. У нас, разумеется, есть code review, свой code style – мы  взяли лучшие практики программирования  и адаптировали их под себя.

В качестве инструментария мы используем набор инструментов от компании Atlassian: JIRA для постановки задач, BitBucket для git-репозиториев и Confluence для документации.

Из чего состоит библиотека?


  • Различные UI-элементы: всевозможные селекты, кнопки, большой набор полей с различными типами данных, чекбоксы, радиокнопки и  другие элементы формы, с которыми взаимодействует пользователь.



  • Способы вывода информации, например, таблицы или панели с кнопками и заголовками





  • Сетка.  Мы посмотрели, как ее реализовал Bootstrap, а затем создали подобное для своих целей. По нашей сетке компоненты строятся на формах.

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

  • Компонент загрузки файлов

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



Поддержка браузеров


Целевыми браузерами являются IE8+. Сейчас IE8, если кто-то помнит, это как в свое время был IE6:  ужасное API и ужасная отладка. Конечно, время,  проведенное за дебагом в IE8, бесценно. Были случаи, когда разработчики проводили несколько дней в попытках найти, в каком месте возникала ошибка, потому что в IE8 очень скудный инструментарий для дебага и он показывает порой, что ошибка возникла совсем в другом месте.

Поддержка IE не случайна, нам приходится работать с железом из браузера: RFID-таблетки, различные принтеры, сканеры и т.д. В вебе нет единого стандарта по работе с железом: в далеком прошлом технологией для работы с ним был выбран ActiveX. Количество ПО, написанного с использованием ActiveX, колоссально,  и это не дает нам в одночасье отказаться от поддержки IE и перейти в сторону современных браузеров. В планах — перевод устаревшего ActiveX на Java-апплеты и отказ от IE8.



Стек технологий


Мы своего рода стартап внутри крупной организации и наш стек технологий фронтенда не сильно отличается от большинства мировых стартапов: react, redux и PostCSS. Все эти технологии зарекомендовали себя с лучшей стороны, к тому же, они позволяют нам поддерживать IE8. Однако, мы не можем резко менять стек технологий, т.к. вокруг него завязана определенная архитектура приложений, например, именно React позволил нам разбить одно огромное приложение на сотни маленьких и подгружать их по требованию, используя SystemJS.

React


Это первая технология, которую мы выбрали по следующим причинам:

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

  2. Легок в изучении — мы не требуем от нового коллеги, чтобы он знал React, мы просто даем ему пару дней разобраться в проекте.

  3. Поддержка IE8.  С этого, на самом деле, можно было начинать, т.к., если посмотреть, какие фрэймворки и библиотеки поддерживают IE8, окажется, что таких крайне мало.  Самые популярные — Angular, Ember, Vue.js — его не поддерживают, поэтому с React нам повезло.

Также в процессе разработки мы начали экспериментировать с React Native, что позволило нам выпустить кросс-платформенную библиотеку UI-компонентов. Прочитать об этом вы сможете в нашей первой статье.

Как мы подружили  React и IE8 ?


Во-первых, мы не обновляем версию React, потому что с какого-то момента они тоже отказались от поддержки IE8. Во-вторых, мы используем es3ify — это loader для webpack, который берет наш ES5 код и перегоняет в ES3. Он просто заменяет некоторые вещи, которые в IE8 не работают.

TypeScript


Второй технологией, которую мы выбрали, был TypeScript, вот почему:

  1. Строгая типизация
    По нашим стандартам тип any запрещен, однако, бывают исключения из правил, т.к. далеко не всё можно покрыть generic’ами.

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

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

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

Производительность


Во-первых, мы сделали компоненты «глупыми», то есть избавились от state, вынося его на прикладной уровень. Теперь прикладные разработчики решают, как менять state, а в наши компоненты только пробрасываются нужные props.

Возьмем пример:

export default class Input extends React.Component<Props, State> {
   static defaultProps: Props = {
       value: ''
   }

   state: State = {
       value: this.props.value
   }

   handleChange = event => {
       const value = event.nativeEvent.target.value;
       this.setState(prevState => ({value}))
       this.props.onChange(value);
   };

   render() {
       return <input onChange={this.handleChange} 
                     value={this.state.value} />;
   }
}

Есть библиотечный компонент Input, у него в state хранится value, в render он возвращает input и в onChange он меняет state. Не много ли кода для такого компонента? Однозначно много, давайте отрефакторим этот пример:

export default class Input extends React.Component<Props, {}> {
   static defaultProps: Props = {
       value: ''
   }

   handleChange = event => 
       this.props.onChange(event.nativeEvent.target.value);

   render() {
       return <input onChange={this.handleChange} 
                     value={this.props.value} />;
   }
}

Код компонента стал короче, а на уровне выше есть компонент Form, который сам решает, как управлять состоянием компонента: через redux или через простейший setState. Input стал проще, и, соответственно, производительнее.

Второе, мы придерживаемся архитектуры чистых компонентов (PureComponent), т.е. все внешние свойства, внутренний state и контекст проходят проверку соответствия предыдущему состояния. Если состояние не изменилось, то нет смысла вызывать render лишний раз. Эту проверку мы осуществляем в методе shouldComponentUpdate, который добавили во все наши компоненты.

И третье, мы избавились от утечек памяти в коллбеках.

export default class Button extends React.Component<Props, {}> {
   render() {
       return (
           <button onClick={event => this.props.onClick(event)}>
               {this.props.children}
           </button>
       );
   }
}

В данном примере у компонента есть коллбек onClick и в него передается стрелочная функция.
Если ее так задать, то здесь возникает утечка. В IE8 ее особенно видно, потому что при каждом повторном вызове render эта функция создается, она накапливается и возникают тормоза в компоненте. Немного изменим наш пример:

export default class Button extends React.Component<Props, {}> {
   handleClick = event => this.props.onClick(event);

   render() {
       return (
           <button onClick={this.handleClick}>
               {this.props.children}
           </button>
       );
   }
}

Сам код стал лаконичнее и к тому же, мы избавились от утечки, поскольку callback-функция больше не создается при каждом вызове render.

В планах на будущее — прекращение поддержки IЕ8, что позволит использовать более прогрессивные фронтенд-технологии. Кроме того, мы уже приступили к работе над масштабным проектом интеграции с мобильной платформой ЕФС, приступили к разработке гибридной библиотеки, позволяющая один и тот же код использовать и для web, и для мобильных устройств, используя React Native.

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

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