Convergent UI: Distributed Composition with Microservices and Spring Cloud

Convergent UI: Distributed Composition with Microservices and Spring Cloud

Microservices Architectures are taking the development community by storm lately.  As distributed systems continue to grow, so do the tools that surround them.  Projects like Spring Cloud have provided common services that help support distributed systems using proven technologies from companies like Netflix.  Spring Cloud provides services for distributed configuration management, service discovery, circuit breakers, proxy services, and much more.  While these infrastructure services help developers to spin up cloud deployments quickly, we are still left with other gaps in the architecture that need to be solved.

When building a Microservices Architecture, we are told to separate concerns as much as possible, and to us, that also means separating the UIs from each other. At the same time, there is a desire to have a unified UI that doesn’t look/act like a frameset from the 90’s. Distributed Composition is a term that describes the need for a single UI to include pieces of UIs from many services and we feel this is a gap within the Microservices Architecture that needs to be filled.

In looking for a solution to this problem, we ran across a great article by Clifton Cunningham on Medium and his work on Compoxure. We decided to work on porting their architecture to our Spring Cloud based Microservices Architecture. The Compoxure Architecture used many similar constructs available within Spring Cloud, so it made sense to us to port the ideas proposed by Clifton to the Spring framework.  To summarize Clifton’s approach, he described a Proxy that would handle composing a single UI from multiple backend services which they implemented as Compoxure using many Node.js features along the way. If you are interested in a more indepth description of the problem, and different options for solving this problem, please read Clifton’s article or as presented by Dejan Glozic.

At ACES, Inc., we strive to build robust architectures and have implemented many or our products using the Microservices Architecture patterns. We also want to give back to the community and share the technology that helps us with our services as well.  So today, we would like to share the result of the work we have done in trying to solve the Distributed Composition problem within a Spring Cloud architecture and would like to introduce you to Convergent UI.

The Spring Cloud Architecture, which relies on many of the Netflix Open Source projects, provides a proxy called Zuul. The Zuul Edge Server provides a mechanism to inject Filters into the processing stream which is where we inject the ConvergentUIFilter. The Spring Framework and Spring Cloud also provides Circuit Breakers and Caching mechanisms that help us implement many of the other robust features described in the Compoxure architecture.

The ConvergentUIFilter scans HTML coming across the Proxy for content enriched with some special tags. These tags are processed in realtime and can reach out to other services running within your architecture that have been registered in a Discovery Service. Convergent UI will retrieve content from those other services and will replace the content within your composed HTML page, just like a Server Side Include.

As an example, say you have two services, service1 and service2.  service1 may provide your main user interface for your customers, and service2 may provide a specialized portion of your user interface that you would like to include within service1‘s UI.  Each of these services when running within your Spring Cloud architecture will have registered with the Discovery Service as ‘service1’ and ‘service2’ making them discoverable by those names to other services within your architecture and now by Convergent UI.

Within the main page for your service1, you may have a <div> that you would want to replace with service2 content.  In order to utilize Convergent UI, you would include a HTML snippet that may look like this:

    <div data-loc="https://service2/test2" data-fail-quietly="false" data-fragment-name="test1" data-cache-name="service2:test2">
      replace me!
    </div>

Your service2 could then provide HTML content at it’s test2 endpoint that may look like this:

    <!DOCTYPE html>
    <html>
        <head>
            <title>Test 2</title>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        </head>
        <body>
            <div>This is test page 2</div>
            <div data-fragment-name='test1'>
                This is content from Service 2!
            </div>
        </body>
    </html>

When someone requests your service1 page, it will pass though the Convergent UI filter and the “replace me!” content would be replaced with the content provided by service2 and the resultant <div> would look like so when served to the client:

    <div data-loc="https://service2/test2" data-fail-quietly="false" data-fragment-name="test1" data-cache-name="service2:test2">   
        <div data-fragment-name='test1'>
            This is content from Service 2!
        </div>
    </div>

Not only can Convergent UI include HTML from other services, but it can include CSS and Javascript if needed as well.  As you can see, Convergent UI can provide a robust framework for distributed architectures. Convergent UI can enable different Microservices teams to utilize different technologies within their UIs without requiring it across the architecture. For instance, we currently have our main service built using plain HTML/CSS/Javascript using only jQuery and Twitter Flight, and we include content from another microservice that uses ReactJS for it’s UI Framework.  These are combined into a single UI that is seamless to the user.

If you are interested in learning more about how Convergent UI works and how you can use it in your project, we have released it on our GitHub page. If you like it, please Star it on GitHub, or leave us a note on Twitter/Facebook! We hope this helps others working with Microservices as much as it has helped us.

Share this post