Observable builds front-end anti-corrosion strategy

1 Dilemmas and problems

In order to explain the difficulties faced by the front end more clearly, we take the common dashboard page in To B business as an example. This page contains three parts of information display: available memory, used memory, and used memory ratio.

When the interface return structure is adjusted, the calling method of the MemoryFree component to the interface needs to be adjusted. Likewise, MemoryUsage and MemoryUsagePercent must be modified to work.
There may be hundreds of interfaces faced by a real To B business, and the integration logic of components and interfaces is far more complicated than the above examples.

After several years or even longer iterations, the interface will gradually produce multiple versions. Out of consideration for interface stability and user habits, the front end often relies on multiple versions of the interface to build the interface at the same time. When some interfaces need to be adjusted offline or changed, the front end needs to re-understand the business logic and make a lot of code logic adjustments to ensure the stable operation of the interface.

Common interface changes that affect the front end include but are not limited to:

* Return field adjustments
*Calling method changed
*Multiple versions coexist

When the front-end is facing a platform-based business, such problems will become more difficult. Platform products will encapsulate one or more underlying engines. For example, machine learning platforms may be built based on machine learning engines such as TensorFlow and Pytorch, and real-time computing platforms may be built based on computing engines such as Flink and Spark.

Although the platform will encapsulate most of the interfaces of the engine at the upper layer, it is inevitable that some underlying interfaces will be directly transparently transmitted to the front end. Challenges posed by interface changes.

The dilemma faced by the front-end is determined by the unique front-end and back-end relationships. Different from other fields, in the To B business, the front-end usually accepts the supply from the back-end supplier as a downstream customer, and in some cases becomes a follower of the back-end.

In a customer/supplier relationship, the front-end is downstream, while the back-end team is upstream, and the interface content and launch time are usually determined by the back-end team.

In the follower relationship, the upstream back-end team will not make any adjustments according to the needs of the front-end team, and the front-end can only conform to the model of the upstream back-end. This situation usually occurs when the front-end cannot exert influence on the upstream back-end team, for example, when the front-end needs to design an interface based on an open source project, or when the model of the back-end team is very mature and difficult to modify.

The author of "The Way of Clean Architecture" has described such a difficult problem of embedded architecture design, which is very similar to the dilemma we described above.

Software is supposed to be a long-lived thing, and firmware is obsolete as the hardware evolves, but the reality is that while the software itself does not wear out over time, the hardware and its firmware do. Obsolete over time, requiring corresponding changes to the software.

Whether it is a customer/supplier relationship or a follower relationship, just as software cannot determine the development and iteration of hardware, it is also difficult or impossible for the front-end to determine the design of the engine and interface, although the front-end itself will not change over time. Unavailable, but the technology engine and related interfaces will become outdated over time, and the front-end code will gradually rot following the iterative replacement of the technology engine, and eventually cannot escape the fate of being forced to rewrite.

Two anti-corrosion layer design

Long before the birth of Windows, engineers introduced the concept of HAL (Hardware Abstraction Layer) in order to solve the above-mentioned maintainability problems of hardware, firmware and software. HAL provides services for software and shields the implementation details of hardware, so that software does not have to Frequent revisions due to hardware or firmware changes.

The design idea of HAL is also called Anticorruption Layer in Domain Driven Design (DDD). Among the various context mapping relationships defined by DDD, the anti-corrosion layer is the most defensive one. It is often used when downstream teams need to prevent external technical preferences or domain model intrusion, and can help to isolate upstream models from downstream models.

We can introduce the concept of anti-corrosion layer in the front-end to reduce or avoid the impact of the current back-end context mapping interface changes on the front-end code.

There are many ways to implement the anti-corrosion layer in the industry. Both GraphQL and BFF, which have become popular in recent years, can be used as alternatives, but the technology selection is also limited by business scenarios. Completely different from To C business, in To B business, the front-end and back-end relationships are usually customer/supplier or follower/follower relationship. Under this relationship, it has become unrealistic to hope that the backend cooperates with the frontend to transform the interface with GraphQL, and the construction of BFF generally requires additional deployment resources and operation and maintenance costs.

In the above cases, building an anti-corrosion layer on the browser side is a more feasible solution, but building an anti-corrosion layer in the browser also faces challenges.

Whether it is React, Angular or Vue, there are countless data layer solutions, from Mobx, Redux, Vuex, etc., these data layer solutions will actually invade the view layer. Is there an anti-corrosion layer solution that can be combined with the view layer? What about complete decoupling? The Observable solution represented by RxJS may be the best choice at this time.

RxJS is the JavaScript implementation of the ReactiveX project, which was originally an extension of LINQ developed by a team led by architect Erik Meijer at Microsoft. The goal of this project is to provide a consistent programming interface to help developers process asynchronous data streams more conveniently. At present, RxJS is often used as a responsive programming development tool in development, but in the scenario of building an anti-corrosion layer, the Observable solution represented by RxJS can also play a huge role.

We chose RxJS mainly based on the following considerations:

*The ability to unify different data sources: RxJS can convert websocket, http requests, even user operations, page clicks, etc. into a unified Observable object.
* The ability to unify different types of data: RxJS unifies asynchronous data and synchronous data into Observable objects.
* Rich data processing capabilities: RxJS provides a wealth of Operator operators, which can pre-process Observables before subscription.

Does not invade the front-end architecture: RxJS Observable and Promise can be converted to each other, which means that all concepts of RxJS can be completely encapsulated in the data layer, and only Promise can be exposed to the view layer.
After introducing RxJS to convert all types of interfaces into Observable objects, the front-end view components will only rely on Observable and decouple from the details of interface implementation. At the same time, Observable can be converted to Promise, and what is obtained in the view layer Promises, which can be used with any data layer solution and framework.

In addition to converting to Promise, developers can also mix with RxJS solutions in the rendering layer, such as rxjs-hooks, to get a better development experience.

Three layers of anti-corrosion

Referring to the anti-corrosion layer design above, we implement the anti-corrosion layer code with RxJS Observable as the core in the dashboard project at the beginning.

The implementation code of MemoryUsagePercent is as follows. At this time, the component no longer depends on the specific interface, but directly depends on the implementation of the anti-corrosion layer.

In the Observable anti-corrosion layer, there will be two designs of high-level Observable and low-level Observable. In the above example, Free Observable and Usage Observable are low-level packages, while Percent Observable uses Free and Usage Observable for high-level High-level encapsulation, when the low-level encapsulation is changed, due to the characteristics of Observable itself, the high-level encapsulation often does not require any changes, which is also an additional benefit brought to us by the anti-corrosion layer.

2 Call method changes

When the calling method changes, the anti-corrosion layer can also play a role. /api/v3/memory directly returns the data of free and usage, and the interface format is as follows.

The anti-corrosion layer code only needs to be updated as follows to ensure that the component layer code does not need to be modified.

3 Coexistence and use of multiple versions

When the front-end code needs to be deployed in multiple environments, the v3 interface is available in some environments, and only the v2 interface is deployed in some environments. At this time, we can still shield the environment differences in the anti-corrosion layer.

Through the race operator, when any version of the v2 or v3 interface is available, the anti-corrosion layer can work normally, and there is no need to pay attention to the impact of the interface on the component layer.

Four additional applications

The anti-corrosion layer is not just an additional layer of encapsulation and isolation of the interface, it can also play the following roles.

1 Concept Mapping

The semantics of the interface and the semantics of the data required by the front end sometimes do not completely correspond. When calling the interface directly at the component layer, all developers need to have a sufficient understanding of the semantic mapping between the interface and the interface. With the anti-corrosion layer, the calling method provided by the anti-corrosion layer contains the real semantics of the data, reducing the cost of secondary understanding for developers.

2 format adaptation

In many cases, the data structure and format returned by the interface do not match the data format required by the front-end. By adding data conversion logic in the anti-corrosion layer, the intrusion of interface data on business code can be reduced. In the above case, we encapsulate the data returned by getMemoryUsagePercent, so that the component layer can directly use the percentage data without conversion again.

3 interface cache

For the situation where multiple businesses depend on the same interface, we can add caching logic through the anti-corrosion layer, thereby effectively reducing the pressure of calling the interface.

Similar to format adaptation, encapsulating the cache logic in the anti-corrosion layer can avoid secondary caching of data at the component layer, and can centrally manage the cached data, reducing the complexity of the code. A simple cache example is as follows.

4 Stability Pockets

When the stability of the interface is poor, the usual practice is to handle the response error at the component layer. This back-and-forth logic is usually more complicated, and the maintenance cost of the component layer will be high. We can check the stability through the anti-corrosion layer. When the interface fails, we can return the bottom-up business data. Since the bottom-up data is uniformly maintained in the anti-corrosion layer, subsequent testing and modification will be more convenient. In the multi-version coexistence anti-corrosion layer above, add the following code, even if the v2 and v3 interfaces cannot return data, the front end can still remain available.

5 Joint debugging and testing

There may be a state of parallel development of the interface and the front-end. At this time, the development of the front-end does not have a real back-end interface available. Compared with the traditional way of building a mock API, it is a more convenient solution to mock the data directly in the anti-corrosion layer.

Mocking data in the anti-corrosion layer can also be used to test the page, for example, the impact of mocking a large amount of data on page performance.

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00

phone Contact Us