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
- Display the
The technology used for the composition is not that important. 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
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:
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:
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 --
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
As the user interacts with the application two things happen:
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 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:
One important thing to highlight is that it is a
ViewModelrather than just a model. A
MVVMterms, has both data and behaviors. It is not a simple
DTOwith no business logic. Endpoints may return
DTOsthat will be mapped to client side
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
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
Brandingservice; 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.