πŸ“œ ⬆️ ⬇️

How we develop a new frontend Tinkoff.ru

Tinkoff.ru


In April of this year, we restarted tinkoff.ru. The bank has become a financial supermarket. Now not only the bank’s client, but any visitor will pay for the mobile, check taxes and issue a mortgage - all on one platform. In this article, I will share the experience and technological solutions to which we came during the year of development.


Our stack for the front end showed itself well. Now we solve problems and adapt to new requirements within the framework of the concept of architecture.


Over the year, we have developed 350 independent interface blocks of 3000 React components. Optimized the loading of pages, because 7 million users visit tinkoff.ru per month and the number of visitors is constantly growing. The frontend develops 30 people and we are constantly looking for talented developers.


We have combined the functions of the previous Internet banks, the portal and the wallet. Therefore, I will first describe the source stacks, and then tell you how we came to the current stack.


Review of previous stacks


Portal and Internet Bank 2011


We developed the first version with internal resources based on a commercial solution from a Dutch company. Backend spun based on heavy Java / Spring applications. At the front, due to lack of flexibility and detailed documentation, a stack of jQuery, Backbone, Handlebars was formed.


Maven collected front with back. There were too few plugins for the front, since Maven was not suitable for building client packages. This led us to a dead end. The blessing found how to separate client assembly from the server using Grunt.


Using templates on the server and unrelated templates on the client with its own logic and architecture was considered the norm. It was necessary to support two UI-layers: server UI and client UI. When we have a large RIA, a lot of logic is duplicated, which is written in different programming languages. For example: routing through pages, data acquisition logic, or patterns with the same markup.


At the end of 2013, an isomorphic approach to creating web applications began to develop. And the solution with duplication of templates on the server and on the client was considered conceptually incorrect.


Wallet 2014


This project is interesting for two reasons:


  1. Tinkoff Mobile Wallet is the first application that everyone used, not just bank customers.
  2. We tried an isomorphic approach based on the MVC architecture. The starting point is an article from Airbnb .

The stack was similar to Internet banking: Backbone and Handlebars, a server on Node.js. Part of the view was rendered on the server. The application solved its tasks and even turned out one UI-layer. But it became clear that on a large application such architecture would bring complexity. There were problems with the enrichment of data models in the browser. I had to write separate controllers for the server and client side.


The next project was developed according to a different paradigm.


Online Banking 2015


The Internet Bank was separated from the external site and was a Single Page Application. For online banking, the framework used is Angular.js. As a result, we received a modern online banking interface.


In 2015, the business changed its development strategy and presented new requirements for the web application. We had to:



A new Internet bank had a UI-layer only on the client. Search robots have not yet learned how to index such applications well. And it is not clear when this will happen. It was not possible to abandon the server layer. We have seen several paths for the development of existing stacks:


  1. Combine old portal and new internet bank. The portal would act as a server UI layer, and Angular.js as a client. This option would not solve our fundamental problems.
  2. Replace Java application Node.js application. This could simplify support, but there are two UI layers left.
  3. Remove Java, leave only Angular.js. And render SPA using deployed servers with Phantom.js headless browser. This scheme is difficult to debug, so this option is not suitable for a large number of pages.

We analyzed the development paths of existing stacks and realized that they did not solve our problems. As a result, we chose a radically different approach.


Platform 2016


What is now available on Tinkoff.ru, we call the platform.


We chose the Flux architecture, which was proposed by Facebook engineers in 2014, as the basis for this system. Flux differs from MV * in that there are no multidirectional data streams, which makes it easier to fit the isomorphic paradigm and all this helps to debug the application faster. The Flux architecture has taken the CQRS database model as a basis.


We implemented the idea of ​​an isomorphic application with a single UI layer. We chose React.js library with virtual DOM support as a template engine. Thanks to which templates are easily rendered on the server.


The established stack solves the problems of SEO and SMM. Re-uses code between the browser and the server, with the exception of environment-specific code, such as working with cookies. Allows you to solve the whole range of tasks by one group of developers, which leads to acceleration of work. It does not depend on one framework / vendor. The application is united by a set of rules, lined up from small and independent modules. The module can be replaced if there is a more suitable implementation.


Universal application


Fluxible


The Fluxible framework from Yahoo engineers was chosen as the Flux implementation. This implementation is focused on isomorphic rendering. The chosen solution fully meets our requirements.


We try not to associate the application with large dependencies, so we use only two libraries from the set:


  1. Routr - routing through pages. The library supports express-like paths. It is isomorphic and fast: we are now moving across 2000 pages.
  2. Dispatchr - Flux controller.

Layer distribution


Application architecture


image


Services. Access to browser or HTTP API, interaction with external systems. This contains part of the business logic. Services have several implementations: isomorphic shared, server for node.js and client for browser. Can cache the result.


Actions. Flux action creators, contain part of the UI and business logic. Have access to services.


Stor. Data models that contain UI logic.


Components. Render data from HTML to HTML.


Progressive download


Thanks to the server renderer, we get the effect of progressive loading and reducing time to glass. The average user sees a working page after 600 ms after a site request. After a couple of seconds, the speaker is initialized and personal data is loaded.


image


We can render all the data on the server, we can render part of it. And we can completely disable the server part and use the application as a SPA. To reduce server load, we render only general data for all users, and work with personal data in the browser.


image


Code example


What to perform on the server or what to allow the user to start with certain roles, we define at the level of the function of creating actions:


import { ACCOUNT_LIST } from '../actions';
import { CLIENT } from '../roles';

accountList.isServer = true; //        
accountList.requiredRoles = [CLIENT]; //     

function accountList(context) {
    return context.service('account/list') //             .    .
        .then(payload => context.dispatch(ACCOUNT_LIST, payload)); //      
}

export default accountList;

. , .



. , . :


image


.



import { LogoPure } from './LogoPure.jsx';

const UILogo = connect(
    ['config', 'brands'],
    ({
        brands,
        config: { brandsBasePath }
    }) => ({
        brands,
        brandsBasePath
    })
)(LogoPure);

export default UILogo;

connect ( redux), . – , . – , .


Higher-order Components


, β€” . , React.createClass.


. , . Mixins Are Dead. Long Live Composition.



: . .



– render. pure-render. render shouldComponentUpdate, (props state).


, . , , . pure-render . render , react-perf. :


image


, render-logger. , render:


image


- , onClick. bind render , render . - pure-render .


bind render, Eslint eslint-plugin-react jsx-no-bind.


Batched updates


React runtime. . , . .



, , . .


Tinkoff.ru , .


image image


JS. . , .


, . , .



React . , .


  1. ES6 Class, React.createClass ( - autobinding) NODE_ENV=production, . 4 .
  2. React. . +2% ( )
  3. Babel. :
    1. transform-react-constant-elements
    2. transform-react-inline-elements createElement . +10%
  4. stateless . 45%.
  5. React-dom-stream renderToString , (TTFB) 3-5. (TTLB) 55%.
    React 15. , React: https://github.com/aickin/react-dom-stream/issues/18 , , .

. HTML, .



:


  1. Nginx .
  2. Express-cache-on-deamand .
  3. Lru-cache .

lru-cache . TTL , . – . , . , .


, :


image



CI Teamcity. . Webpack. .


tar . Babel , – Babel .


– , .
Babel , .
. , Babel .


, – Webpack. , .
, . .


application runner PM2. PM2 .


  1. pm2 startOrGracefulReload processes.json, .
    processes.json:
    {
    "name": "portal",
    "script": "server/index.js",
    "instances": -1
    }
  2. , . Node.js . – Nginx. Nginx . PM2 . NODE_APP_INSTANCE: PORT: process.env.PORT + process.env.NODE_APP_INSTANCE

image



- , . React.js -. DOM - . Flux . Node.js .


, , , . , , . React.js, Flux Dream Team.


best-talents@tinkoff.ru.


:


image


image


!


PS: -, , FinTech. .


')

Source: https://habr.com/ru/post/303580/


All Articles