Marmelab Blog

Admin GUIs in the Microservices Era: Introducing ng-admin Multi Backend

House on stilts in Hmong village, Vietnam

Following microservices leads to a virtuous architecture, but to a nightmare for human users. What if we could do something about it, using a web application dedicated to backend administration?

The Problem

Take, for instance, an e-commerce microservice architecture. It makes sense to separate several parts of the domain into standalone services, each offering a web API. Applications relying on the domain, such as a web frontend, can use these APIs:

Things get a little bit more complicated once you need to manage the data in each microservice. The usual answer is to develop one backend application for each service. And since the services are often developed in different languages, it's usually the only solution.

But then, what a nightmare for backend users: they must constantly change the application they use according to the domain they interact with! Although it makes sense from an architecture standpoint, this is a not a usable solution.

The right solution is to apply the same rules to the backend application as to the frontend application: each must rely on the microservice APIs the same way.

Great, but how to implement an admin GUI talking to multiple backends through REST APIs?

Introducing ng-admin Multi Backend

We've been working on ng-admin, an open-source admin GUI consuming any RESTful API and written in AngularJS, for several months now. But we've just added the ability to customize the API endpoints per entity. Let's see how it works:

var app = new Application('eShop Admin')
    .baseApiUrl('http://backend1.eshop.com/'); // default backend for entities

var item = new Entity('items');
// customize item CRUD here

var customer = new Entity('customers')
    .baseApiUrl('http://backend2.eshop.com/');
// customize customer CRUD here

var command = new Entity('commands')
    .baseApiUrl('http://backend3.eshop.com/');
// customize command CRUD here

app.addEntity(item);
app.addEntity(customer);
app.addEntity(command);

Load the web app, and there you go: an application allowing you to manage several domains under a single GUI.

For each resource, the API endpoint will use a different backend:

  • Items: http://backend1.eshop.com/items
  • Customers: http://backend2.eshop.com/customers
  • Commands: http://backend3.eshop.com/commands

It doesn't matter if one uses a PostgreSQL persistence, and the second uses a MongoDB persistence.

For the admin user, ng-admin becomes the enabler to a true microservice architecture.

Multi Backend for Views

Even further: ng-admin lets you customize the backend API URL at the view level. This allows you to use a specialized index for the list view (e.g. an ElasticSearch index), and another (not optimized for search) REST API for edition.

var item = new Entity('items');
item.listView()
    .baseApiUrl('http://elasticsearch.eshop.com/items')
    .addField(...)
    // continue listView customization here
    ;
item.editView() // All views use the API endpoint defined in the Entity by default
    .addField(...)
    // continue editView customization here
    ;

app.addEntity(item);

Being able to rely on another piece of infrastructure for specific commands can be very liberating. We already used this technique in the past, while building a large CMS using JackRabbit for persistence. For better response time, all lists (frontend and backend) were using an index powered by ElasticSearch. That's the reason why we build the SonataElasticaBundle in the first place.

API Endpoint Configuration Cascade

So ng-admin offers three levels of customization for API endpoints:

  • at the application level
  • at the entity level
  • at the view level

This configuration cascade allows to specialize endpoints at the lower levels, but it also allows to generalize endpoints at the upper levels. If you've ever used ng-admin, you probably had to repeat the pagination configuration for each entity. Now, you can simply do it once at the application level, and all entities will inherit from this configuration.

var app = new Application('eShop Admin')
    .transformParams(function(params) {
        // by default, ng-admin sends pagination information
        // using two query parameters: page, and per_page
        // translating these into offset and limit for all entities
        // because that's what the backend APIs expect
        if (params.page) {
            params.offset = (params.page - 1) * params.per_page;
            params.limit = params.per_page;
            delete params.page;
            delete params.per_page;
        }

        return params;
    });

This cascade should reduce the code you had to repeat in each entity configuration.

Conclusion

Microservices are hard; we're trying our best to make them simpler, and ng-admin is one of the pieces in our toolset for that purpose.

The multi-backend feature was developed by Emmanuel Quentin, and it will be available in the next version of ng-admin (v0.5), currently under development. As the feature is already merged in the master branch, we invite you to test it, and send us your feedback if you meet any issue with it.