📜 ⬆️ ⬇️

How Sberbank Online applications work: Workflow API and frameworks

Many people use the Sberbank Online application, but few know how it works. It is time to open the veil of secrecy - in this article we will talk about some of the approaches that we use in the development.


There will be no big dates, blockchain, ajail and other rocket science. But API will be described, on which our most popular applications work. The value of this article is not in breakthrough ideas, but in approaches and practices that work in a large application with one of the most demanding audiences.

We hope that our experience will help readers to make their product better, and most importantly scalable, because most of the bumps in the development of the API, we have already caught and fixed.

What will be discussed


We will tell you how payment scripts work in Sberbank Online mobile and web applications, namely about API between applications and server-side.
')


Why focus on the API? Everything is simple - it is actually the only bridge that connects client applications and the backend. If the project is small, then we can easily change the API and rewrite applications for it. But if the project is large-scale (such as ours), then even small API changes require the involvement of a large amount of resources both at the front and at the back end, and become very expensive. And the second point - the earlier we fixed the API, the earlier front and back commands could start development. They just need to come together at one point.

First, we will talk a little about our capabilities and limitations, so that it is clear why we chose this and not a different solution, and then we will present the API protocol itself at the top level.

Specificity and motivation


Applications are large. When we wrote this article, the Sberbank Online application on Android took about 800,000 lines of code, on iOS - 500,000 lines of code. And this is just our code, without the link libraries.

Backward compatibility and many users. MAU - 32 million active users of the mobile application. And if we do not make backward compatibility at the API level, so many users across the country will have to download applications again. This is very bad. By the way, this is one of the reasons why we have so much code.

Sberbank Online develops many small teams. You've probably heard about Agile at Sberbank. It's true, we work on Agile in teams of 9 people.

Banking application: in spite of the fact that the functionality of banking applications is growing very quickly, the main thing that happens in remote banking is a sequential process (processing of client applications). We call these processes workflow. These applications can be of different kinds and they are processed by a huge number of interrelated services in the perimeter of the bank.

Two types of commands. There are platform - they are responsible for the development of the application kernel. And there are feature commands - they create application functionality for end users, using the architecture and tools that the platform gives.

Omnicanality An extremely important story. In order not to develop back-end several times - separately for mobile applications and separately, for example, for the web version and ATMs, you need to make the API as similar as possible for all channels (at least the response structure should be the same).

Mobile app


Data changes dynamically. The most popular operations in the mobile application are payment and transfer. The requisites of the service providers, the set of fields that the user needs to fill out, is dynamic information that can change frequently.

However, users may not update the application after installing it on the device. Just because they can. More often there are good reasons for this, for example, to update the application, you need to update the OS version, and to do this, buy a new phone. Therefore, we need a solution that will allow us to change data without a release of the application.

Mobile Internet: our applications should work everywhere, even where the Internet is unstable and slow. Therefore, we always fight for the size and number of messages between mobile applications and server-side.

The best customer experience: we have chosen for ourselves the basic technology of developing mobile applications - the development in native languages. This is the only way to get the best customer experience.

If you summarize all these requirements, applications should be developed in native languages, have reusable components inside, but all business logic should be managed from the server side.

How to do not become


After we have defined the boundary conditions, we will describe what existing solutions we analyzed.

JSON programming

It is easier to describe logic with code imperatively than to invent (and learn!) A new declarative language, which will always be limited more than the native language of the platform. In addition, it is necessary to provide a sandbox, error handling, some stage of piloting - pseudo-code should gradually spread to user devices and roll back in case of any failures. All this complicates development without tangible benefits.

CSS 3000

We do not use the description of component styles, since they can differ from the form factor, platform, and even the mode of operation (portrait / landscape orientation, responsive in the web). Declaration of styles in the final implementation will always be better, closer to reality and more correct to work with boundary cases. In addition, it happens that components with similar logic basically work differently on different devices: for example, entering a phone number with the phonebook on a mobile device and without it on the web.

Fixing the data model in the application interface

This method is also called "nailing." The idea is that the application interface is built on the unique identifiers of objects that are transmitted from the server. In this scheme, any changes on the server side lead to rework of the client part. Cannot reuse code. Difficult to maintain.
The only reason why you should choose this method on your project is 99% confidence that the API will not change. Well, or if the project is quite small and designing an API is more expensive than quickly reworking the user interface for API changes.

Styles

Add to each object a sign of style. UI applications build on the basis of this feature. There are a limited number of styles, so it is possible to build an interface dynamically. But with the increase in functionality, UI has to increase the number of styles.
In this variant, it becomes possible to control the display of individual elements, but the complexity of the implementation of connectedness between different fields increases. And most importantly - with increasing UI variability, you will have a constant need to extend the API protocol.

JSON API

The JSON API describes in detail the recommendations for structuring data and describing the relationships between them, but there is nothing that could describe the presentation. Our task also includes the visual expansion - adding new input fields, so this option does not suit us.

Web Components / React Components API

The concept of web components , which, among other things, greatly influenced the API of React components , is already much better for us: on the one hand, we have control over the display, on the other hand, it is possible to bind data to UI elements.
Unfortunately, everything is too tied to HTML + CSS + JS. You do not use it directly, but remember, it will come in handy later.

How did you decide to do


UI containers

Objects are packaged in containers, the presentation logic of the application is built on these containers. The main advantage is that we can group several simple objects into one container. This gives the freedom to program UX / UI on the client, for example, we can control the hiding / display of one field when filling in the data in another. At the same time, the basic types of objects are a limited number, and all business transport is implemented on them.

We chose this approach. First we describe the API protocol, and then how the frameworks within mobile and web applications are arranged.

API


To make it clearer, consider the API on the example of a simple process, for example, transfer between your accounts. How to get to the entry point, do not consider - this is not a process and for this we have our own API (we will also tell about it somehow). So the process starts at the entry point:


Data transport


To begin, agree on the basic principles - how to transfer data. We take the simplest approach as a basis - key-value pairs. The key will be a string of letters of the Latin alphabet, the value is also a string, but arbitrary.

Forms to fill in are complex, with nested elements and subsections, which means that nesting should be allowed. Keys can be named in the camelCase format, but they can be poorly readable (for example, in logs) or even “spoil” in case-insensitive systems. You must enter a separator.

The most obvious separator is a dot - in many languages ​​it is used to access the properties of an object. With careless use, keys with such a separator will create dictionaries (or objects) in which collisions are possible. For example, “foo.bar” ​​= “foobar” and “foo.bar.baz” = “foobarbaz” in javascript may entail overwriting the property “bar” of the object “foo” from line to object. In the end, we agreed on a colon: on the one hand, the obvious visual separation and semantic reflection of nesting, on the other hand, is safe enough for all the languages ​​used.

What to do with repeated fields? We introduce an additional rule: between a pair of delimiters can be either Latin letters or numbers. Constructs of the form: children: 5: name: first .

Having lived for some time with such a structure, we find a restriction: the multiple choice turns out to be nontrivial to implement and requires additional tweaks on the backend in order to keep the load high.

Solution: the value is either a string or a list of strings. So the solution looks typical, but at the same time, the overhead is insignificant.

Steps


A step is a state of the process. The first step with us is the selection of the account for debiting and the account for crediting and entering the amount.


The UI in this picture is not visible, because the step is about the server logic, and not about the presentation logic. There are two approaches to working with steps: you can transfer only the difference from the server (the cumulative total in the client application) or each step as a whole (the cumulative total on the server).

The analysis of the requirements showed that during the process the screen can be formed differently at different steps (branching processes), so instead of adding control commands to convert already transferred entities, each step is easier to pass completely the way the user should see it.

Of the additional advantages: when you return to editing, you do not need to play the entire script or pass an additional parameter “give everything”. When starting a step, the client application immediately receives all the necessary information for building screens.

"output": "screens": "events": "references": 

Screens


A screen is a division of a process into stages in a client application. As a rule, screens are used to make the form easier to read. In our case, everything is simple: one step - one screen.


For screens, we introduced two rules:

  1. the transition between screens can only be linear, without branches;
  2. the transition between screens does not require interactions with the backend.

This means that screens, in fact, become simple groups and can be transferred from the backend immediately upon entering the step.

 "screens": "title": "UI Block": "properties": 

UI components (blocks)


A UI component is an independent component that implements client logic and fills a document with data. In essence, this is an association between the management team in the protocol and a piece of code and markup in the application. The first screen has three components:

  1. Charge Account
  2. The same component for the enrollment account
  3. Transfer amount


Sometimes something can go wrong: for example, a new process was transferred to an old version of an application, or an old version of a block was deleted in a client application, but remained in one of the server application processes. In this case, the application performs a soft degradation: the block is replaced with a system (simple group of fields), which does not have any additional logic, but simply shows the fields in the composition. More will be below.
In this case, the form will be less beautiful, but at least the user will be able to fill in the data and send them to the server. The server then validates the input and returns errors that can be corrected.

 "UI Block": "type": "properties": "field": 

Fields


Fields are atomic components that act as a transport for individual data elements and process user input in case of block degradation. The field types are limited and all are supported at the framework level: text, checkbox, select, multiselect.

This means that any version of the application can draw an interface based only on field types.

Fields in the UI components from our example:

1. The field with reference to the reference book in the account of write-off and account of enrollment Why link to a static directory? Because the account we choose from the list of cards (accounts), without unnecessary access to the server.


2. Two separate fields for the amount and currency in the amount input component


Thus, the format for the fields has the following structure:

 "field": "id": "type": "title": "value": "style": "validator": 

Developments


Since applications do not know anything about the process, it is logical that events (buttons that the user sees) are also part of the response from the server.

We divided events into two types.

1) Basic - they are on almost every screen in familiar places for the user. As an example, these are the events “back” and “continue”. The first one goes back one step, and the second collects the completed data from the client form and sends it to the server along with the “Go to the next step” command.

2) And special ones - for non-standard actions that we cannot predict in advance, and there is no point in putting them into the engine part, as they are rarely used.
In our case, only the main events on the screen - “continue” and “back”. They are implemented at the platform level.


All events have a number of attributes, such as the type of the event itself, the title and the indication of visibility. And no server side UI like button size, position and color. This logic is implemented on the front.

 "events": "name": "type": "title": "description": 

Directories


With reference books everything is standard. If it is small, then we send it completely in response from the server and call it static. This is done in order to minimize the number of requests to the server side and the response time to the user action in the interface. To display it in the form on the screen, add a field with the type - selectList, one of the properties of which is a link to a static reference.

If the directory is large, then it is implemented as a separate rest-service. In the interface, it looks like a text field, as it is filled, a list of possible options is returned from the directory.

 "references": "referenceId": 

Validation errors on the client and server


Since the main interface element is a data entry field, it is logical to validate it on the client. Along with the fields, validation rules and messages are displayed, which are displayed if validation fails.

 "validator": "value": "message": "type": 

The structure of the response looks like this:



 "output": "screens": "title": "UI Block": "type": "properties": "field": "id": "type": "title": "value": "style": "validator": "value": "message": "type": "properties": "events": "name": "type": "title": "description": "references": "referenceId": 

Frameworks


Now a little about how frameworks inside applications work with this protocol. Conditionally, frameworks can be divided into two main parts: the workflow engine + UI container handler. This separation is caused not only by the application architecture, but also by the organizational structure. The engine is developed and supported by platform commands, and UI containers are actually extension points and they are programmed by feature commands. Thus, more commands do not need to make changes to the kernel.

Workflow engine


The engine inside the application (web and mobile) knows that the process of working with the document has begun and that according to the protocol a number of attributes will come to it: steps, screens, UI containers and field types. This data draws the basic interface - the lower and upper menus, the main buttons, the UI on simple field types, if used.

At the same time, the engine does not know how many steps of the process will be in the script, how the steps will be divided across the screens and what fields there will be.

If the script changes, for example, you need to display a new field, it will be enough to add it to the server response, and the client application will display it. For this release release of the front-end application is not required.

How do UI containers work?


An analysis of the needs of designers and business customers showed that all needs cannot be satisfied by simply expanding the attribute composition of the fields.

Therefore, expansion points were needed. With these extension points, UI components are the native implementation of the code in the applications themselves, which is identified by the engine by name. In essence, this is a grouping of a field / several fields into a logical block that can display a custom UI. In this case, the protocol data model is used only for data transport to the backend, the entire UX and UI is implemented on the application side.

Two modes of the framework

When the engine parses the data model, it compares the list of names of the UI containers with the registry that is stored inside the application. If the application does not find the name of the component, then the interface is built on simple field types. The process will be fully operational, but on standard UI elements.



On the left - how a container can be displayed for entering the amount on a list of simple field types. On the right, if there is a UI container in the application build. Despite the fact that in the list of simple fields there is no slider and there is a separate field instead of an icon with a choice of currency, we can transfer all data from PL and the process will be working.

And here we get one of the main advantages of the engine - to deliver changes to the user without updating the application. In the assembly there is a mapping of component names to classes, in which the UI of these components is programmed and the user interface is built on it.

What rules we try to follow when working with UI-components:


Coming soon ...


We tried very hard to write concisely, but this is the first technical article about the Sberbank Online platform and it should have covered a lot.

Write in the comments that it is not clear what is interesting - we will try to write less, but more often and on target. We have many interesting challenges, and therefore a lot of material.

The authors:

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


All Articles