Building A Traffic Quality Control System, Part 2: Creating a Robust Frontend
This part of an article series is about the development of a traffic quality control system, and how the frontend of the application was built. The link to the first article about the system architecture can be found at the bottom of this page.
As cities are growing in terms of population but also pollution, it becomes even more necessary to provide them with efficient mobility solutions. The customer we built this solution for, is known for developing modern ecosystems and services for “smart” cities.
The business logic in the frontend is based on two entities — Intersections and Monitors.
As smart cities have billions of smart devices, which are placed at almost all of their intersections, they produce massive amounts of data per hour. Not an easy task for engineers or data scientists to use this data for useful and valuable cases in real-time.
The customer wanted to create a module that monitors traffic signals and gives a detailed analysis of certain parameters (such as waiting time for pedestrians or vehicles upon green requests). For traffic engineers, these parameters are referred to as “Monitors” (because they help them to monitor traffic situations). When a drop in the quality of these parameters is observed, it becomes easier to take targeted actions for improvement.
Introduction
The requirement was to be able to see the data collected from these smart devices on different levels of groupings:
- Average quality percentage for each monitor for all intersections
- Dissected (evaluated) data per intersection, per monitor, that lead to the average quality percentage.
- Raw data that lead to the analysed data in step 2.
React JS is the chosen framework for creating the frontend for this application. In this article we explain a few important topics and best practices and how the Concept Reply frontend team made it a successful project:
Efficient (re-) rendering of the application
Every re-render process of the entire or just parts of the application causes system resources and performance. Because huge amounts of data are fetched — even with a single user action. The user actions could cause the application to re-render in the following way:
- Language change or theme change causes the entire application to re-render
- Fetching data or selecting a specific intersection, monitor or filter could cause one or several components to re-render
Separate React Contexts were created for all of the above-mentioned scenarios. React Context is a method to send information between components within a component tree. It allows you to skip all the intermediate components and send information arbitrarily deep into the component tree.
The reactive state of the application is reflected in the hierarchy of the MobX store. E.g. Any data that affects all monitors is kept in the Monitor store but data related to independent monitors is kept a level below.
External Libraries/Tools:
MobX reactively uses observers to handle any changes to states/data. Data and relations are monitored and changed only when needed, then re-rendered.
Handling Data fetching
Users had to wait a long time, while the backend did some complex calculations to retrieve evaluated data for several intersections at the same time. There were two scenarios while querying for data in the frontend:
- Data on demand — when the user interacts with the page
- Initial Data — data you want to see immediately once you land on the page
Open API generator was used for generating the code for using the backend services to get data. Services were only instantiated the first time (Singleton Pattern) and the fetched data was kept in the stores (single source of truth).
- Higher order components were used to make the decision about when to show the loading state or the component with fetched data to the user, based on a passed enum prop.
- The useEffect hook along with an await statement waits for the data to be rendered.
- While MobX observer was used to react to the user interacting with the page by observing the modified values in the UI store.
External Libraries/Tools:
In order to manage our HTTP calls and generate typescript models, we used the OpenAPI generator
Robustness and Extensibility
The approach taken to build this application was evolving (due to the complex business logic). This meant that a feature was not finalised in one cycle, but is refined and built over in several iterations. That raises the risk of introducing new bugs to existing code while building a feature over multiple sprints. Also adding a monitor as a plug-in (the backend adds a monitor and frontend renders it without changes) had to be taken into account.
Modular programming was used to ensure the extensibility to add new monitors. Unit tests using React testing library and Jest were added to provide a safety net for adding more features and functionalities. The overall test coverage provided to the application was more than 80%.
External Libraries/Tools:
Jest was used for mocking and spying on dependencies outside the components- like libraries and services and writing test assertions.
React testing library was used for DOM testing — like checking to see if a particular text expected to be rendered has appeared.
Conclusion
Following the best practices of Concept Reply´s frontend experts and respecting the few guidelines mentioned above, the team was able to build a reactive frontend, despite the complexity of business logic and UX requirements.
Building A Traffic Quality Control System, Part 1: Handling Big Data