Micro Applications and Decomposition of UI

Anil Sharma
trillo-platform
Published in
11 min readFeb 18, 2017

The primary goal of this post is to discuss techniques of decomposing a large UI into smaller pieces to handle complexity. These smaller pieces can be planned such that, each piece is self contained and represents a logical functionality of the application. We can call each such piece a “micro application”. It draws its equivalence from microservices approach used for backend services. Another goal of this post is to demonstrate that the template driven approach along with decomposition is effective and delivers high business value. Two concepts together can simplify and derisk the development of large and complex UIs or web-applications. They also make applications more palatable to customization and ecosystem growth.

You will realize that the technique is rather obvious but requires paying attention of few finer points to maximize its value.

The technique discussed here is effective for rich-UI. In order to set the stage, let us review what is a “rich-UI” and how it differs from its alternatives. Modern WEB applications rely on client side rendering by browsers. This is especially the case for applications which are data-centric, where a page is rendered by mixing HTML snippets with data. UI rendered in the browser is responsive and provides experience similar to a native application. Therefore such UI is called rich-UI. In a rich-UI a user should not notice latency during interactions. Few examples of interactions in rich-UI are — navigate back and forth between pages instantaneously, fill-up forms, apply filters, search, sort quickly, view data using different perspectives, get back to the left off state etc. These applications differ from content-centric applications such as Wikipedia or a newspaper website where a user spends more time reading content of a page. A content-centric application can also be designed to render on client side and in some cases provide better user experience. For example, a documentation website with a table-of-content can use client side rendering to provide delightful and book-like readability. A web applications rendered on the client side is also referred as Rich Internet Applications (RIA).

A rich-application may consists of several pages, dialogs, popups (collectively called views). In a complex application, the number of views can be in hundreds or even close to thousand. The volume complexity and how to deliver smooth user-experience are main challenges in developing rich-UI. A smooth user-experience means delivering workflows that are intuitive and true to the design as put forward by user-experience (UX) experts. Unfortunately, it is not an easy goal and requires several rapid iterations. Therefore the design and architecture of rich-UI should be flexible and template driven. It should also separate code (JavaScript) from visual aspects (HTML, CSS) so programmers and visual-designers can collaborate effectively.

The benefits of decomposition as micro-applications are similar to microservices and listed below.

  1. Modularity so that multiple teams can work independently.
  2. Turning a large problem into manageable pieces.
  3. Independent release and upgrade of pieces without affecting other pieces.
  4. Customization of UI for multi-branding — We will see that the decomposition of UI automatically provides flexibility to extend and rebrand applications. This is becoming increasingly important feature for building an ecosystem of partners.

Anatomy of Client Side Rendered Pages

Before we get into the details of the application decomposition, let us quickly review the anatomy of a rich page. The following diagram shows anatomy of a rich page, interactions between server (web-server, services) and browser for rendering the page by mixing data with HTML templates.

Anatomy of RiCH UI Page and Its Rendering in Browser

The components shown in the diagram above are similar to any other web-app. There are few additional functions required in web-server for application decomposition which we will discuss later. Let us walk through the diagram and decipher it:

  1. Server serves a shell page (index.html) with empty body. It includes JavaScript and CSS which are common across all micro-applications. These are framework libraries (generally open source). “index.html” is served in response to a URL that hits the server. In most cases, its content is same irrespective of URL (in-lining app specific HTML templates, CSS, JS are optimizations which we can leave out for the present discussion).
  2. The URL is indicative of the micro-application and its associated templates.
  3. After the shell page is loaded, the framework code kicks-in and it requests application specific HTML templates, CSS and JS.
  4. JS code makes API calls to request data. The API call can be triggered either a) by framework JavaScript code based on some specification in templates. This is data driven approach; or b) by application specific JavaScript code.
  5. API call is served by backend services. API is normally delivered to backend services through a API gateway service (not shown in the diagram) or it can be proxied through some intermediate server acting as security layer.
  6. When data arrives, framework or application code mixes data with HTML templates (or DOM elements created by it) to render the final page.

Decomposition of UI as Micro Applications

The decomposition of application is simple and a natural extension of the discussion in the previous section. Rather than placing all HTML, CSS, JS, images in one directory, simply divide them logically into multiple directories. The browser can request assets of a particular application by including its directory in the URL path. This is shown in the diagram below.

Since there is no server side rendering involved here, the directories are different from conventional webapps directory. They do not have any code that will be loaded by the server. These are directories with static web-content and can be added to the server without restarting the server (it opens up an avenue for simplifying deployment pipeline which I will discuss in a future blog).

Here, it is worth to highlight a special directory called “shared”. This will play a critical role in decomposition of UI. We have a detailed discussion of its role in the following section.

Decomposition of Rich UI as Micro-Application in Multiple Directories

Role of Shared Directory

Just to impress upon its importance, it is worth repeating that “shared” directory plays a critical role in the decomposition of UI. As name implies it contains code that is shared across micro-applications. Based on our experience, if you follow data driven approach, over a period the most JS code gravitates towards it. Over a period, you can solidify it and treat as framework code. It will make micro-application code lean and simple. Let us have a look at the kind of code that can be shared.

  1. HTML: Templates for header, footer, dialog shells, message dialogs, alerts, common components such as slider, spinner, date-time picker etc.
  2. CSS: Since micro-applications are part of a large application, they will have common theme. Therefore a lot of CSS can be shared.
  3. JS: If you follow a data driven approach, you can make JS code 95% or more shareable. For example, a) a JS component can be driven by HTML template; b) API interface code can be driven by specification of end-point, parameters supplied as JSON via template; c) Common behavior can be written as base controller classes; d) validation code; e) code related to security etc.

Whenever you have shared code, you also need a way to override it. For example you may have a situation, a common “footer” is good for several micro-applications but one micro-application requires a special footer-HTML. You can have a custom footer-HTML in the micro-application directory so it can override the one in “shared” directory. But there is a problem that it will require a change in some code to load it from a different directory. How do we solve this problem? The solution is — we never load from shared directory. We always load from micro-application directory. If an asset is not found in its directory then look for it in the “shared” directory. This is a kind of resource-fault or back-chaining for searching resource. It is shown by the diagram below. We can extend this chain to be any length. In the next section, we will see how this is leveraged for code sharing and customization.

Backchaining for Sharing Web Applications’ Assets

Customization of Micro Application

The concept of backchaining directories can also be applied to customization of application. Let us take one example, you have a need to change the branding of application App-1. This involves changing — a) logo image, b) primary color, secondary and tertiary colors (assuming you designed UI themes using 3 colors and called them so). You can create a new directory called “App-1-custom”. You can back chain it to “App-1” as shown in the diagram below. “App-1-custom” can simply have a new CSS file which will override colors and, a new image for logo.

The backchaining order for any App can be provided through a configuration file (JSON or properties file). Web server searches for assets in chained directories in the specified order and serves the first occurrence.

Customization of Application using Chained Directories

Backchaining and Version Management

In the example above, App-1 is back-chained to “shared”. This is default backchaining. Rather than using default chaining, an application specific backchaining can be configured. It can be used for version management as shown in the following diagram. In this diagram, App-2 has moved to “shared (v 2.0)” whereas App-1 continues to use the initial version of “shared”.

Chaining Directories for Version Management

Web Server — Required New Functionality

In order to support UI decomposition as Micro-Applications, a web server needs few new functionalities:

  1. Support for linking multiple directories in a chain and traverse backward to find a given asset. Such a function is not available in existing web-servers but can be easily added. It helps with several important features discussed above — code sharing, version management, customization of application for branding etc.
  2. Inlining assets of a micro-application inside “index.html” results in fast startup of the application. The initial load time is a very important for overall user experience. This basic functionality for inlining code is available in some form in the existing web-servers. It can be extended to inline required assets using configuration files also residing inside micro-application directory.
  3. Minification: Assuming that all open source code is minified, the server can do basic minification of JS and CSS in shared and apps directory. Or you can minify code in dev-op pipeline. The basic minification functionality in the server may be good enough and simplify dev-op pipeline. It will also be useful when an new micro-application is added on the fly. Also, based on a query parameter, the server can serve minified and unminified version of code. It will be handy for troubleshooting. The feature can be disabled in the production environment or password protected.
  4. Cache Busting: You would have realized that an additional benefit of the approach discussed here is that you can deploy new assets of a micro-application without bringing down server. For example, you can support “git” based deployment. In such cases, the server should change the URL appropriately to bust the cache (either name change or add a random query parameter such as time stamp of the file).
  5. Role base access of resources: Since the assets of micro-applications are not actual data, the security may be less of a concern compared to an API gateway or back-end services. Still there may be situations where you want to deny access to assets if the user is not authenticated / authorized.
  6. Serving content to a search engine crawler requires special handling in case of rich-UI. In this case, you need to render the page content on the server side as done by the browser. Visual style is not important in this case. If you are limiting this requirement to certain pages or micro-application which are content centric then it can be supported by the web-server out of box. For the cases where web-server will require to make API calls, it can be achieved in one of the two ways

i. Delegate URL handling to a special application service. Web server simply need to support such a delegation.

ii. Run JavaScript engine in background to generate content. This can be done in real-time or in advance depending on the performance needs.

Code for Behavior and Templates for Visuals

Due to stylistic reasons or performance, people are building applications without external CSS or HTMLs. ReactJS uses JSX in place of HTML template and in most cases it is a part of JavaScript code. This may be suitable for certain use-cases and small applications. But it makes decomposition of UI very hard if not impossible. One important aspect of effective decomposition of UI is that most of JavaScript code stays constant and shared. The micro-application specific code is mostly HTML templates, specification JSON within templates and CSS. There will be some micro-application specific JavaScript code and it should be viewed as pluggable business logic.

In my experience, a data-driven / template-driven approach is more suitable for the UI decomposition as micro-applications. Having said that, there are several competing approaches of problem solving in software. But some may be more suitable than others for organic growth.

CSS & JS Namespace Issue

While constructing a view consisting of multiple views, a view may use templates, CSS and JS from different micro-applications. As a good practice inter micro-application reference of code should be avoided by pulling it to “shared” directory. But you want to retain the flexibility of referring code across micro-applications for handling one-off cases. In order to avoid namespace clash (javascript function/ variable names; CSS class names), you can follow these simple rules or something similar:

  1. Export JS code using a namespace (top level object) that is same as the name of the application. For example, if your application name is “ContactManager”, all your exported JS functions and variables can be referred as ContactManager.createContact() etc. This can be achieved by declaring, ContactManger = {}.
  2. You can create CSS namespace using combinators. For example, all your CSS class names which are specific to ContactManager can be referred as .contact-manager .edit-form {…}. The top level element can be set to have “contact-manager” CSS class.

Composition Of Application at Runtime — Comes Free

We discussed decomposition of a large UI into multiple micro-applications for development. But our end goal is to deliver a single application to the end user. It is probably obvious that if you follow certain rules then composition of micro-application is automatic and would be done by the browser. Let us review these rules:

  1. While writing JS and CSS for micro-applications, create synthetic JS and CSS namespaces as discussed above.
  2. When URL navigates from one app to another (routes without page reload), unload JS, CSS of previous application, load JS and CSS of new application using JavaScript.
  3. Server should support organization of code as micro-applications in separate directories. Server should be able to serve the assets of a micro-application based on its name in URL path. This is supported by all web-servers (some configuration may be required).
  4. Server should be able to back-chain resource and lookup by traversing the chain to find the match. This is not needed. But this is what makes sharing of assets possible and delivers the maximum business value. Put differently, this is what this churn is all about.
  5. Browser should be able to talk to services to get data directly or through API gateway.

Conclusion

A large rich-UI can be decomposed into smaller micro-applications by logically organizing assets in different directories and following certain rules while writing code. Code can be shared using directory chaining. In the reverse direction, the composition of micro-applications as a large application is automatic and done by the browser.

--

--

Anil Sharma
trillo-platform

Founder and architect of cloud-based flexible UI platform trillo.io.