Here, at AUTO1 Group, we are growing. Not only in terms of company size, number of employees, revenues, etc, but also in terms of database size, amount of servers, lines of code in applications, amount of accessible pages. For supporting this growing codebase we are adopting new approaches and new technologies. Different innovations were already introduced, but today I want to tell You about switch of a paradigm from MVC to SPA, and how implementation of SPA approach evolved.
We have our internal application, called Admin, which is heavily used by hundreds of colleagues in many countries. Historically, it was written as a monolith php application, based on Silex (and later Symfony) frameworks (some overview is present in this blog article). Admin application had architectural layers, which fell into a concept of Model-View-Controller (MVC). The View part, which is in essence the HTML code of all pages, was prepared on web-server using TWIG templates. Usually, one developer was working on both back-end and front-end implementations of web-page. That was called
Full Stack development.
Downside of such approach became obvious when first HTML tables with complex internal structure were implemented. Lets take as an example a page that displays hundreds of cars, provides pagination, possibility to edit data, shows aggregated results, etc. Such page consists of static components (navigational menu, list of available locales, information about current user, html-template, that renders appearance of the page, etc) and dynamic components (information about cars). This kind of web-page is
hard to build for a single person. Knowing that development cycle of such complex pages could be split between several software engineers, we divided responsibilities between back- and front-end developers. We decided to generate the
View part of MVC architecture in client's browser instead of application's server.
And this was exactly the case, that fitted well into SPA paradigm.
SPA stands for Single-Page Applications - an architectural paradigm, that divides elements of the page into static and dynamic. Static elements of the page (such as page layout, navigational menu, user information) should be loaded into client's browser only once per user session. The page layout rarely changes. On the contrary, dynamic components of the page can be updated continuously.
Regardless of the technologies involved for rendering a page in client's browser, main difference between SPA and non-SPA approaches is whether a browser
requests each time full page content (including static content, such as layout, menus, etc), or it
requests only the important (dynamic) content. This difference can be seen in a diagram below.
First picture represents traditional request where full page contents are passed from web-server to a browser on each request. Second picture illustrates SPA approach: full page content is fetched only once, afterwords only dynamic content is downloaded and full page reload does not happen.
Starting from a monolith application, where HTML was rendered on a server-side, we gradually moved to our current state, where a separate application serves SPA for us. But let's go step by step.
For organic integration with new technology we updated existing workflow of generating HTML of the page. Specifically, we introduced several new endpoints, that provided essential information for rendering HTML page layout. Those endpoints were:
Front-end developers wrote React code, which could fetch data from aforementioned endpoints and render HTML5-compliant page layout.
Now we needed a way to pass this React code from web-server to client's browser during initial page load!
This was achieved by introducing of what we call SPA-controller: set of php application's code (written with Symfony framework), which returned very basic standard html template that contained latest version of the React code. Being executed in the browser, this code rendered HTML contents of specific SPA page.
Under the hood of SPA controller we implemented such functionality:
(if template was not cached, or TTL of the cache is over) do a request to CDN, that contains actual version of front-end assets
The response contains lightweight HTML similar to this:
As You can see, SPA index page contains none of the data that should be shown on a page. This data will come as a result of subsequent requests. Stages of building SPA page are illustrated in the diagram (notice 3 phases):
Page lifecycle consists of 3 phases:
This approach, regardless of its increased amount of requests, actually resulted in a faster page load times, mostly because caching of SPA index was introduced and because web-server did not render HTML code anymore (this responsibility was moved to the React application code, that was executed on the client-side in web-browser).
Overtime we noticed drawbacks with this approach: actual SPA version was hardcoded in a config file in php code and full php application required re-deploy each time, when a new version of React code was introduced. Deployment of a big monolith application to production environment was slow, also release notes were meaningless and annoying.
Having understood that keeping a reference to a latest SPA version in the code of main application was causing real problems, we started thinking about another possibilities.
The most promising was idea to create a standalone application, whose single responsibility would be to serve latest version of SPA index page to authorized users. Expected benefits were:
As a result, we implemented a Golang application, that did exactly the same, as it's predecessor-monolith, but outside of php environment. The current page lifecycle started to look like this:
Now, initial request to fetch SPA index was re-routed to a standalone application, that served the same HTML, as we have seen above. All other phases of SPA page lifecycle did not change.
Implementing of a separate application, that serves SPA index pages, brought us several significant improvements in a page load lifecycle:
This is pretty good, huh?
Of course, there are challenges in current approach also. We are investigating the best way to deal with user locales in SPA index, we learn how to do proper caching and authenticating user, but this is a topic of another story!