When we started building out Microservices, we designed each Microservice around bounded contexts and designed the REST APIs to be around resources. And exposed them “as is” to clients. We quickly realized this is a bad idea.
Because what the front end or clients need is different from back-end design considerations
- Front end or client’s perspective is around a page or a use case. They couldn’t care less about bounded context and cleanly defined resources.
- They prefer to get all data required for a view in one shot. Usually, this will be a combination of multiple resources. For ex, a Product page on an e-commerce site will require information from different resources like products, recommendations etc. Making a call for each leads to chattiness and impacts performance.
- Based on the front end, what attributes they require about a particular resource will also differ. For instance, if you are rendering a Product page, you might need a shorter version of the product resource for mobile and a full version for the web.
- Clients don’t want to deal with the complexities of the backend like what if a Microservice is not available.
- In the good old days of Monoliths and Server-side MVC, the controllers will be page-centric and acted like a façade that will call the required backend services, compose them all and give it in one shot to the client. This kept things simple and helped in performance as it kept the chattiness down.
So how do we address back-end design considerations and front-end requirements?
Enter Edge Server Pattern A.k.a, Micro-proxy pattern. It’s also referred to as an API gateway pattern, but I prefer not to use it as it brings other concerns into play like throttling, metrics, etc.
It’s also referred to as API gateway pattern, but I prefer not to use it as it brings other concerns into play like throttling, metrics etc.
Edge server pattern helps in designing Microservices cleanly around bounded context and yet addresses front-end requirements by putting an Edge server in front of this.
Following are some of the ways we have implemented the Edge Server pattern in our projects.
The way to do this is to keep the MS and granularity, designs, responsibilities, and REST API design very specific to what the backend needs. Design them cleanly about bounded context
There are 3 options:
1. Roll your own
Implement our own UI server which is basically a Spring boot app or Node/Loopback app which is a single point of contact for all front ends. This server implements facades that are client-centric. In one shot, it will call the Microservices needed, compose the results and serve it back to the client.
This façade is also responsible for looking at the user agent and determining what this particular client might need and projecting only those attributes needed. Once again referring to the product page example might return a product name, price, and thumbnail for mobile and a full resource for the web.
Usually, this UI server also handles the responsibility of serving the UI assets.
- Simple — doesn’t introduce any new technology
- The server decides what the client needs.
- But the reality is clients know what data they need.
- This problem is precipitated when the types of clients increase.
2. Leveraging an Edge server like Zuul
Zuul is an edge service that provides dynamic routing, monitoring, resiliency, security, and more.
Here all the clients are pointing to Zuul. Based on the inbound request, Zuul looks up the appropriate services and essentially implements the façade pattern.
A couple of niceties include calling the Microservices with the Hystrix to include the circuit breaker design pattern and using a service registry like Eureka to make your MS location agnostic. Optionally Ribbon can be used for client-side load balancing.
RxJava is a good candidate for implementing the façade in Zuul.
Powerful Edge service with sophisticated routing capabilities.
Can be leveraged for addressing other concerns like Authentication, Monitoring, etc.
Again server decides what the client needs.
Complexity because of additional technologies. Don’t underestimate the complexity of running a HA Zuul cluster.
Increased adoption of Backend for Frontend patterns often leads to chatty inefficient interactions between client and backend and a lack of flexibility to adapt to changing client needs. Rich domain modeling will solve some of the issues but still, client and server-side concerns are not going to match.
Though not a complete Edge Server, GraphQL addresses these concerns. It follows the client-directed query approach.
GraphQL is described as a query language for the API layer by Facebook which developed an open-source it. Though not necessarily a full-fledged Edge server, it addresses the concerns stated earlier.
GraphQL is to REST API what an ORM (only the red part) is to the DB layer. As with an ORM, once you define your entity model and map the REST API, clients can specify what they want.
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
Here is how a typical interaction looks like:
In addition to clients controlling what they want, they can also get many resources in a single request and each resource can be a partial or full representation.
If your client is built using React framework, you are in luck. Facebook has also released Relay which makes declarative data management possible for React front ends. This is a very nice abstraction of data instead of calling imperative APIs.