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.
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.
This project is interesting for two reasons:
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.
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:
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.
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.
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:
Application architecture
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.
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.
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.
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;
. , .
. , . :
.
import { LogoPure } from './LogoPure.jsx';
const UILogo = connect(
['config', 'brands'],
({
brands,
config: { brandsBasePath }
}) => ({
brands,
brandsBasePath
})
)(LogoPure);
export default UILogo;
connect ( redux), . β , . β , .
, β . , React.createClass.
. , . Mixins Are Dead. Long Live Composition.
: . .
β render. pure-render. render shouldComponentUpdate, (props state).
, . , , . pure-render . render , react-perf. :
, render-logger. , render:
- , onClick. bind render , render . - pure-render .
bind render, Eslint eslint-plugin-react jsx-no-bind.
React runtime. . , . .
, , . .
Tinkoff.ru , .
JS. . , .
, . , .
React . , .
. HTML, .
:
lru-cache . TTL , . β . , . , .
CI Teamcity. . Webpack. .
tar . Babel , β Babel .
β , .
Babel , .
. , Babel .
, β Webpack. , .
, . .
application runner PM2. PM2 .
{
"name": "portal",
"script": "server/index.js",
"instances": -1
}
- , . React.js -. DOM - . Flux . Node.js .
, , , . , , . React.js, Flux Dream Team.
:
!
PS: -, , FinTech. .
Source: https://habr.com/ru/post/303580/
All Articles