Director ant Financial · Data Experience Technology team
Ant Financial’s data platform front-end team is mainly responsible for a number of data-related PC Web single-page applications, whose business complexity is similar to Excel and other desktop applications. The number of front-end business codes ranges from tens of thousands of lines to hundreds of thousands of lines. With the continuous improvement of the product, it will soon exceed one million. Managing front-end applications with 100,000 or even a million lines of code is one of the core challenges for our team.
In the next series of articles, I will try to introduce our team’s approach to challenges from the following perspectives:
- The front-end architecture
- Quality assurance
- Performance optimization
- Team front-end development process
- Staff literacy
The front-end architecture
The architectural scheme of the team is a set of architectural scheme suitable for the business scenarios of the team’s data products, which has been continuously explored after a year of continuous iteration of several products. There are still unresolved pain points and controversial parts in the architectural scheme that need to be continuously optimized, so it is not guaranteed that this architecture is suitable for your products.
Product features
Let me first introduce the product features of our team:
- ToB products, high business complexity, high business understanding threshold;
- Huge amount of front-end code (80,000 lines of business code were iterated for 8 months from zero for the data analysis product, which only realized 20% of the long-term planning requirements of the product)
Architecture plan
The purpose of architecture is to manage complexity, divide and conquer complex problems, and manage them effectively. Our approach is as follows:
1. Cut function modules of the page-level granularity by routing
The “page-level” granularity here refers to the components of a routing map
2. Divide modules on the same page
Principles of division:
- Vertical: Divided by service functions (based on view modules)
- Horizontal: divided by three different functions of Model-View-controller
3. Merge similar items
Continue to subdivide the granularity, and then extract reusable modules or components into common areas
3.1 Data Model
Data models fall into two categories based on responsibilities:
- Domain Model Domain Model
- App State Modal Application State model
3.1.1 Domain model
Domain models are business data that can be persisted to a database or localStorage and are common data that can be reused across modules, such as:
- Users User information
- Datasets Dataset information
- Report information
As common data, it is recommended that Domain models be stored in an architecture-independent Layer called Domain Model Layer (commonly known in the front-end industry as the ORM Layer).
There are several advantages to sinking into the Domain Model Layer:
- Cross-module data synchronization issues no longer exist, such as: Previously, the Users object was stored separately in the two service modules A and B. After the Users object is changed in module A, the Users change needs to be synchronized to module B. If it is not synchronized, the User information displayed on the interface of module A and MODULE B is inconsistent, and the problem no longer exists after the unified management at the domain model layer.
- In addition to domain model reuse, domain model-related CRUD Reducer can also be reused. For example, the previous Create Read Update Delete method corresponding to Users objects may be maintained in each of the two business modules A and B, and code duplication is reduced after being lowered to the domain model layer for unified management.
- Nature undertakes part of the responsibility of cross-module communication, and the cross-module communication code related to data synchronization is no longer necessary.
3.1.2 Application state model
Application state models are view-specific state data, such as:
- CurrentSelectedRow: someId = someId
- IsModalShow: false
- Whether some view element is in drag isDragging: true
This data is strongly related to a specific view module or business function and is recommended to be stored in the Model of the business module.
3.2 View layer components
Components are divided into two categories based on responsibilities:
- Container Component Container components
- Presentational Component
3.2.1 Container Components
Container components are components that are directly connected to the Store and provide data and behavior for presentation components or other container components, while trying to avoid doing anything related to interface rendering.
3.2.2 Display components
Presentation components are independent of other parts of the application and do not care about data loading and changing. They only perform view presentation and the most basic interaction. They receive data through props and output results through callback functions to ensure that the received data is the minimum set of data dependent on the component.
A complex system with hundreds or thousands of presentation components can precipitate many reusable common business components if the granularity of presentation components follows the principles of high cohesion, low coupling and single responsibility.
3.3 Public Services
- All HTTP requests are managed together;
- Log services, local storage services, error monitoring, Mock services, etc. are stored in the common service layer.
After combining similar items according to the above three points, the business architecture diagram changes to
4. Cross-module communication
The gradual refinement of module granularity will bring more demands for cross-module communication. In order to avoid mutual coupling between modules and ensure long-term clean and maintainable architecture, we stipulate:
- It is not allowed to call the Dispatch method of other modules directly from within a module (write operations, change the state of other modules)
- It is not allowed to read state methods of other modules directly inside one module (read operations)
We recommend that the logical code for cross-module communication be placed in the parent module or maintained separately in a layer called Mediator.
Finally, we got the complete business logic architecture diagram of our team:
Data Flow management
Just talked about the solution of architecture management from the spatial dimension, now talk about the application data flow from the time dimension – Redux one-way data flow.
The Redux architecture is designed with one-way data flow at its core, and all data in the application should follow the same lifecycle to ensure predictability of application state.
1. Action
- Click drag input…
- The subsequent behavior of the data returned by the server
2. Reducer
Each Action corresponds to a data processing function, known as Reducer. In particular, Reducer must be a pure function, which brings a great benefit, data processing layer code becomes very easy to write unit tests.
The characteristic of a pure function is that the return value of the same input parameter is identical, for example 🌰 :
Pure functions:
function add(a, b) {
return a + b;
}
Copy the code
Impure functions:
function now() {
let now = new Date(a);return now;
}
Copy the code
A function is impure if it contains math.random, new Date(), asynchronous request, etc., and affects the return of the final result.
3. Store
Store Stores the state of data generated by all Action actions since the page is entered. Each change in data caused by an Action must generate a new state object and ensure that the old state object is not modified. This ensures predictable, traceable application state and makes it easy to design Redo/Undo functionality.
Our team uses immutability-Helper, a lightweight IMmutable scheme, for better performance and storage utilization than deep Clone.
The IMmutability-Helper API is not friendly enough, so we wrote a library immutability-helper-x to enhance its usability.
Immutability – Helper API style:
import update from 'immutability-helper';
const newData = update(myData, {
x: {
y: {
z: { $set: 7}}}});Copy the code
Immutability-helper-x API style:
import update from 'immutability-helper-x';
const newData = update.$set(myData, 'x.y.z'.7);
Copy the code
4. Unify the rendering view
React/Redux is a typical data-driven Development framework. During Development, we can concentrate more on the operation and flow of Data (domain model + state model), and no longer have to be troubled by various complicated DOM operation codes. The React/Redux framework helps us automatically unify the render view when the Store changes.
The react-redux function is used to listen for Store changes and refresh the view:
- The <Provider> component passes
context
Property provides store objects to descendant <connect> components; -
is a high-level component that connects store and View layer components (redux defines the component that
connects directly as a Container Component).
opens several callback hooks for developers (mapStateToProps, mapDispatchToProps…) The pose for customizing props for injecting the Container Component;
- React-redux listens for changes in the Redux store and notifies each connect component to refresh itself and its descendants. To reduce unnecessary refreshes and improve performance, connect implements shouldComponentUpdate. Do not refresh the connect wrapped Container Component if props is unchanged.
conclusion
Strictly follow architecture specification and one-way data-flow specification, can assure you our front application on a relatively coarse granularity of maintainability and extensibility, the finer the granularity of the code, we organize children to learn and share “design patterns” and “refactoring: improving the design of existing code, continuous grinding and optimize their own code, The team will continue to produce a series of articles on this topic in the future.
This paper will talk about the general front-end architecture first. The business architecture of specific modules, the principles followed by the architecture, and the architecture review process of the team architecture group will be elaborated in the following series of articles. If you are interested, please follow the column or send your resume to ‘tao.qit####alibaba-inc.com’.replace(‘####’, ‘@’)
Original address: github.com/ProtoTeam/b…