Skip to main content

The secret of better UI composition

I was at a customer site, and we were analyzing their business processes with the intent of building a new component of their system. The customer is basically a logistics company, whose responsibility is to deliver goods on behalf of their customers and, when applicable, to charge the recipient of the goods for the delivery and handling process. During this discussion, the accounting manager raised an interesting issue.

One of the internal departments is responsible for monitoring the overall state of customers' orders. This department is currently overwhelmed by the amount of work that arises from information they need being scattered across different applications. They end up wasting a lot of time gathering this information instead of doing their actual work.

The manager and I began to discuss options for removing all of this friction. We began to talk of building a single front-end application capable of aggregating all of this information.

What we immediately realized is that there seems to be no canonical guide to designing UIs that retrieve and aggregate data from different sources. Sure, there are a lot of resources and discussions about Service Oriented Architectures, Domain Driven Design and the process related to correctly identifying bounded contexts; finding guidance for correctly designing your services and their interactions is no problem. But when it comes to designing an aggregator UI, there isn't much, so it is really easy to create a UI that couples otherwise unrelated data and services. Or, as in this scenario, it's easy to couple services owned by different departments or even by third parties.

Client side UI Composition

UI composition work should be done in client-side code. This is the one place that can connect the dots and glue together pieces of data coming from different endpoints. Composing data at the client is, in my experience, a two step process:

  • Compose the client side ViewModel;
  • Display the ViewModel;

The technology used for the composition is not that important. Using JavaScript is much easier due to the dynamic nature of the language. But using a language with "no compiler" to help you understand what's going wrong introduces some long-term solution management challenges. On the other hand, using C#, in a strongly typed manner (so no use of dynamic at all), has some advantages. But C# can introduce tons of boilerplate code for something that, in JavaScript, is very straightforward.

The following concepts apply also to a pure MVC client application in which all the composition, applying the same techniques, is done server-side before rendering HTML pages.

Assumptions and requirements

Let's assume that data have identifiers, are segregated, and are owned by services that expose a way to consume them. An example consumption mechanism might be an HTTP endpoint.

For example, we can try to model the following requirement:

As user I want to view a list of customers where, per customer, there is a summary of order status and payment status.

We have 3 different services involved in the aforementioned scenario: registry, orders and accounting. The initiator of the orchestration is the registry, because the user is querying for customers, but in this case registry is just a detail.

The following picture shows a UI mockup outlining information sources and responsibility:

UI Mockup

The goal is to be able, given the customer id, to query the registry system, the order system, and the accounting system to retrieve data related to a given customer (or a list of them), and then compose the results into a single UI component.

Client side composition process

At the client side we have components -- JavaScript classes for example -- that belong to each service. In the case of third parties, the components are an anti corruption layer (ACL) that acts as a proxy, and that behaves as a facade to those services. These components are deployed to the client application by each service.

At client application start-up time, each of the components registers itself, with an infrastructure component, as capable of providing composition services for a given scenario or in general. I'll call this infrastructure component the BackendService.

As the user interacts with the application two things happen:

ViewModel Composition

When the application needs to retrieve either a list of customers or a single one given its id, instead of going directly to the remote endpoints, the application goes instead to the BackendService. The BackendService then notifies all of the registered components of the incoming request, and each actor interested in participating issues the corresponding HTTP request to its respective back-end endpoint. The BackendService then receives a promise (or a callback in more generic terms) that the BackendService will use to wait for all the actors to complete their own requests.

Once all the requests are completed, the BackendService will stick all the returned data into one single object instance that is sort of a dictionary (a JSON object in this case is perfect) and return the composed ViewModel to the UI-composition component of the system.

The following picture shows a diagram of the ViewModel composition process:

View Model Composition

One important thing to highlight is that it is a ViewModel rather than just a model. A ViewModel, in MVVM terms, has both data and behaviors. It is not a simple DTO with no business logic. Endpoints may return DTOs that will be mapped to client side ViewModels.

ViewModel Templates

The previous step in this composition pipeline returned a composite ViewModel that represented everything the client application needs to display. When it is time to display it, the client application cannot make any assumptions about the shape of the returned data. Otherwise, we are coupling services at the last step, and thus preventing UI modules from changing or evolving without breaking the entire UI.

At this level we can leverage a template engine. WPF has the amazing DataTemplate concept. Building one for AngularJS is not that complex if you start from the ngInclude directive. And ASP.Net MVC has partials and model templates.

What happens is that at the UI level (in the HTML markup for example) we can bind the composite ViewModel with the element, in the elements tree, that will display it. The only assumption unique to the root element is that its ViewModel is a sort of dictionary that can be enumerated. The root element will enumerate the sub-ViewModel(s) and for each of them it will load a specific template to display the corresponding data.

NOTE: Templates and all graphical representations are the responsibility of the Branding service; business-centric services shouldn't own model templates.

The mapping between the ViewModel and its template can be made by convention or by an actor in the composition pipeline that can put some logic in place. This logic will decide which template to use to display a specific piece of data in a specific scenario.

The only responsibility of the root element is to organize templates in the UI, so the root element is a sort of a “grid” with holes that will be filled at run-time by the rendered templates. The root element owns the design of the grid, not the content design and not how it is displayed.

Since they are rich ViewModels each one can now be rendered in the UI as preferred without having any dependency on other pieces of the system. If it is required I use events to bring the power of publish/subscribe even at the UI level; when something happens in some portion of the UI a “client” event is published so that other portions can react accordingly.

A couple of weeks later...

The very first version of the process monitoring system was deployed to a well-known set of users, triggering an extensive user acceptance test process.

Following the above guidance, the accounting department succeeded in solving their daily issues and, from a technical point of view, they were able to build a composite UI with a really thin dependency on the services exposing the required data. This will allow the entire system to evolve as needed to handle the inevitable behavior changes of service providers. And this evolution can happen without causing the system's developers endless headaches.

If you're interested in more information about ViewModel composition, check out the ASP.NET Core demo from our SOA Done Right Workshop, which demonstrates the basic principles of ViewModel composition. If you're interested in attending a live workshop where a trainer can answer all your questions, have a look at our upcoming events page.

About the author: Mauro Servienti is a solution architect at Particular Software, a Microsoft MVP for Visual C#, and is passionate about DDD, CQRS and Event Sourcing.

Don't miss a thing. Sign up today and we'll send you an email when new posts come out.
Thank you for subscribing. We'll be in touch soon.
We collect and use this information in accordance with our privacy policy.
Need help getting started?