Microservices are a hot topic in industry today. However, most of the time, microservice "talk" is focused on back-ends. Obviously, a monolithic UI application is as problematic as a monolithic back-end service, but very few UI topics are discussed in the microservices literature.
As you may already know, we are actively embracing a microservices architecture for our Zuora Central Platform; we are on a journey transforming a monolithic web application, which has served us well to date, to a set of microservices. We are doing this not only for back-end services, but also for our front-end as well.
This blog post provides a brief introduction to some of the practices we are employing as we reinvent our front-end.
Problem Statement & Solution Requirements
The front-end stack of our legacy web application consists of pretty old JSP-based technologies: Struts, Sitemesh and custom JSP tags. There is no clear boundary between the client and the back-end. Pushing out even a small UI change requires waiting for a full product release cycle to clear.
Previously, we tried to introduce new UI technologies into the web application. This didn't work well, and ended up introducing new UI libraries that bloated our code base.
By reinventing the UI, we are avoid repeating some of the same mistakes of the past, while trying to achieve the following goals:
Replace the older pages incrementally - this is very important since it's not practical to re-implement all of the UI pages with a big bang release
Rapid UI application iteration - we should be able to release a new UI feature or bug fix very quickly
Flexibility in technology choices - we would like to encourage engineers to try different technologies, and the new architecture should allow the coexistence of different UI stacks
Seamless user experience - goes without saying!
Solving our Problem: Incremental Reinvention of the UI
Figure 1: Microservice based Front-End Architecture
The first step we took is to introduce a Routing Service. This is simply a reverse proxy sitting in front of our current legacy web application that routes UI requests according to various request conditions, such as HTTP method, path, request headers, and so on.
We then picked a single Zuora functional area (Usage Rating), and reinvented the UI for that service. We deployed the Usage UI application and then added routing rules to dispatch the older UI requests to /apps/Usage.do to the new Usage UI at /usage.
The main benefit of this iterative approach was that we were able to keep the legacy web application unchanged, just in case we needed to perform a fast failback if the new UI application introduced any problems.
Server Side Page Fragment Composition
In our microservices architecture, while each service is responsible only for its own business/functionality, we have higher level services composing/aggregating the lower level services to implement larger functional offerings. We applied the same design pattern to the front-end tier.
If you are at all familiar with Zuora UI, you will notice that there are several common UI components. For example, the Navigation Bar (Navbar) on the top of our UI, the Footer on the bottom, and the menu panel located on the left hand side of the screen.
Figure 2 - Zuora UI
Given this UI layout, it made perfect sense to have a UI application be responsible for rendering the navigation bar and menu items, so that when functional teams work on the their own UI reinvention, they do not need to build it themselves.
The Navbar, the Footer and the other UI applications that are responsible for rendering parts of a whole UI page are called a Fragment Server. Our Layout Service is responsible for composing the UI fragments.
For instance, when a user clicks the Usage menu item, the Routing Service dispatches the request to the Layout Service, which serves a usage UI template HTML and calls out to the 3 fragment servers in parallel - namely, the Navbar, Footer and Usage UI - fetching the Fragment contents (in HTML), assembling the HTML fragments and finally rendering the single HTML page in the browser.
As such, this design pattern is called server side page fragment composition.
Building the UI with Public APIs
One explicit decision we made early on in our design was to ensure the UI application is just another regular API user. As such, any new UI apps can only be built with public APIs. In other words, we ensured:
API First - anything you can do with the UI, you can automate with API calls
It's possible to build different UI - customers can build their own "Zuora UI" to simplify their UI operations, or to integrate Zuora UI with their own systems
The Zuora UI engineering team has been working on this project for several months, and several UI infrastructure pieces have already been deployed to production, including the Routing and Layout services.
We’re also happy to say that several functional teams have already re-implemented their UI modules and apps with modern UI stacks. This includes the Orders, Usage, and RevPro teams, and we look forward to more teams leveraging our new front-end micro services in order to update their UIs for our customers!
... View more