ServiceComposer is a ViewModel Composition Gateway.
Designing a UI when the back-end system consists of dozens (or more) of (micro)services is challenging. We have separation and autonomy on the back end, but this all needs to come back together on the front-end. ViewModel Composition stops it from turning into a mess of spaghetti code and prevents simple actions from causing an inefficient torrent of web requests.
For a technical introduction and an overview of the problem space, refer to the following presentation on YouTube.
Imagine an elementary e-commerce web page where it's needed to display details about a selected product. These details are stored in two different services. The Sales service owns the product price, and the Marketing service owns the product name and description. ServiceComposer solves the problem of composing information from different services into one composed view model that downstream clients can later display or consume.
To start using ServiceComposer, follow the outlined steps:
CompositionGateway
in an empty or existing solution.ServiceComposer.AspNetCore
NuGet package and configure the Startup
class as follows:public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddViewModelComposition();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseRouting();
app.UseEndpoints(builder => builder.MapCompositionHandlers());
}
}
Sales.ViewModelComposition
.ServiceComposer.AspNetCore
NuGet package.public class SalesProductInfo : ICompositionRequestsHandler
{
[HttpGet("/product/{id}")]
public Task Handle(HttpRequest request)
{
var vm = request.GetComposedResponseModel();
//retrieve product details from the sales database or service
vm.ProductId = request.HttpContext.GetRouteValue("id").ToString();
vm.ProductPrice = 100;
return Task.CompletedTask;
}
}
Marketing.ViewModelComposition
, and define a composition request handler like the following:public class MarketingProductInfo: ICompositionRequestsHandler
{
[HttpGet("/product/{id}")]
public Task Handle(HttpRequest request)
{
var vm = request.GetComposedResponseModel();
//retrieve product details from the marketing database or service
vm.ProductName = "Sample product";
vm.ProductDescription = "This is a sample product";
return Task.CompletedTask;
}
}
<url-of-the-web-application>/product/1
The HTTP response should be a JSON result containing the properties and values defined in the composition handler classes.
NOTE: ServiceComposer uses regular ASP.NET Core attribute routing to configure routes for which composition support is required.
In this brief sample, the view model instance returned by GetComposedResponseModel()
is a C# dynamic
object. dynamic
objects are handy because they allow request handlers to be entirely independent of each other; they share nothing. ServiceComposer supports using strongly typed view models if they are preferred. They have the advantages of strong typing and compiler checks and the disadvantage of a little coupling. Refer to the view model factory documentation for more information.
ServiceComposer is available for the following platforms:
When building systems based on SOA principles, service boundaries are key, if not THE key aspect. If we get service boundaries wrong, the end result risks being a distributed monolith in the best case and a complete failure in the worst case.
Service boundary identification is a challenge on its own; it requires extensive business domain knowledge and confidence in high-level design techniques. Technical challenges, such as the lack of technical solutions to problems foreseen while defining service boundaries, might drive the solution design in the wrong direction.
The transition from the user mental model, described by domain experts, to the service boundaries architectural model in the SOA space raises many different concerns. If domain entities, as defined by domain experts, are split among several services:
This type of question leads systems to be designed using rich events, not thin ones, to share data between services and with cache-like things, such as Elastic Search, to satisfy UI query/visualization needs.
This is the beginning of a road that can only lead to a distributed monolith, where data ownership is a lost concept and every change impacts and breaks the whole system. In such a scenario, it’s easy to blame SOA and the toolset.
ViewModel Composition techniques are designed to address all these concerns. They bring the separation of concerns, designed at the back end, to the front end.
For more details and the philosophy behind a Composition Gateway, refer to the ViewModel Composition series of article available on milestone.topics.it.