Along the previous parts of the tutorial we have talked quite a lot about the benefits of the microservice architecture. It is essentially a loosely coupled distributed system which provides a particularly important ability to pick the right tool for the job. It could mean not just a different framework, protocol or library but a completely different programming language.
Table Of Contents
In this part we are going to discuss the monoglot and polyglot microservices, the value each choice brings to the table and hopefully come up with the rational conclusions to help you make the decisions. Also, we will present the architecture of the reference application we are about to start developing. Its main purpose is to serve as the playground for numerous further topics we are going to look at.
Over the years many organizations have accumulated tremendous expertise around one particular programming language and its ecosystem, like for example Java, the subject of our tutorial. They have skilled developers, proven record of successful projects, in-depth knowledge of certain libraries and frameworks including the deep understanding of their quirks and peculiarities. Should all of that be thrown away in order to adopt the microservice architecture? Is this knowledge even relevant or useful?
Those are very hard questions to answer since many organizations get stuck with very old software stacks. Making such legacy systems fit the microservice architecture may sound quite impractical. However, if you have been lucky enough to bet on the frameworks and libraries we have discussed in the previous part of the tutorial, you are pretty much well positioned. You may certainly look around for better, modern options but starting with something you already know and familiar with is a safe bet. And frankly speaking, things do evolve over time, you may never feel the need to get off the Java train or your favorite set of frameworks and libraries.
There is nothing wrong with staying monoglot and building your microservices all the way on Java. But there is a trap where many adopters may fall into: very tight coupling between different services which eventually ends up with a birth of the distributed monolith. It stems from the decisions to take a shortcuts and share Java-specific artifacts (known as JARs) instead of relying on more generic, language-agnostic contracts and schemas.
Even if you prefer to stay monoglot, please think polyglot!
Beside just Java, there are many other languages which natively run on JVM, like for example Scala, Kotlin, Clojure, Groovy, Ceylon to mention a few. Most of them have an excellent level of the interoperability with the code written in the plain old Java so it is really easy to take the polyglot microservices route staying entirely on JVM platform. Nonetheless, since everything is still packaged and distributed in JARs, the danger to build the distributed monolith remains very real.
In the spirit of the true innovation, the GraalVM opens whole new horizons for the JVM platform. It is not ready for production use yet (still in the release candidate phase as of today) but it has all the potential to revolutionize the way we are building the applications on the JVM, especially the polyglot ones.
In the industry driven by hype and unrealistic promises, the new shiny things appear all the time and the developers are eager to use them right away in production. And indeed, the microservice architecture enables us to make such choices regarding the best language or/and framework to solve the business (or even technical) problems in a most efficient manner (but certainly does not mandate doing that).
In the same vein of promoting responsibility and ownership it looks logical to let the individual teams make the technological decisions. The truth is though in reality it is quite expensive to deal with the zoo of different languages and frameworks. That is why if you look around, you will see that most of the industry leaders bet on 2-3 primary programming languages, an important observation to keep in mind while evolving your microservices implementations.
To shift our discussions from the theory to practice, we are going to introduce the reference project we are about to start working on. Unsurprisingly, it is going to be built following the guiding principles of the microservice architecture. Since our tutorial is Java-oriented, most of our components will be written in this language but it is very important to see the big picture and realize that Java is not the only one. So let us roll up the sleeves and start building the polyglot microservices!
Our reference project is called JCG Car Rentals: a simplistic (but realistic!) application to provide car rental services to the various customers. There are several goals and objectives it pursues:
- Demonstrate the benefits of the microservice architecture
- Showcase how the various technology stacks (languages and frameworks) integrate with each other to compose a cohesive living platform
- Introduce the best practices, emerging techniques and tools for every aspect of the project lifecycle, ranging from development to operation in production
- And, hopefully, conclude that developing complex, heterogeneous distributed systems (which microservices are) is really difficult and challenging journey, full of tradeoffs
The JCG Car Rentals is quite far from the modern large-scale systems with hundreds of the microservices deployed in production. However even an application as simple as that posses many questions and problems. Let us take a look on the JCG Car Rentals architecture diagram below.
You may notice that there are quite a few contractions to some principles we have talked before. For example, each service uses own programming language or/and framework, the same happens to apply for frontends as well. Isn’t it the language zoo we have been cautiously warned before? Yes, indeed, we have deliberately weakened some of the criteria in order to cover larger set of topics and interoperability scenarios. Please do not adopt this architecture as the blueprint for your applications but focus on the parts which work best for you.
With that, it would be useful to go over each box we have drawn and briefly explain the reasoning behind the choices we have made.
The responsibility of the customer service would be to manage the personal data of the JCG Car Rentals customers. This service has no upstream dependencies and we are going to implement it in Java as the RESTful web API, using one of the JAX-RS implementations (to be precise, we are going to rely on Apache CXF framework).
Why: objectively, JAX-RS is a number one choice in the world of enterprise Java. The Apache CXF is not only JAX-RS compliant but provides a lot of additional must-have features for building successful microservice architectures.
The inventory service is going to be an authoritative source of the cars and quantities available in the stock for rentals. It is also has no upstream dependencies and our implementation choice for it is going to be RESTful web service built on top of Scala and Akka HTTP.
The payment service would handle all the customer’s charges for the services provided by JCG Car Rentals. We are picking a completely different technology stack here by building this service in Go and using gRPC protocol over HTTP/2 to communicate with it. This is an example of the purely internal service which we may not want to expose externally.
The reservation service is the core of the JCG Car Rentals microservice architecture. It is solely dedicated to manage the car reservations and depends on payment service, customer service and inventory service. Since it is most critical and important piece, we are going to implement it as RESTful web APIs using Spring Boot and Spring WebFlux.
Why: Spring Boot, and more generally Spring Platform, absolutely dominates the Java ecosystem. Built on proven foundation of Spring Framework, it offers exceptional productivity, smart configuration capabilities and seamless integrations with most popular libraries and frameworks. The choice of Spring WebFlux (versus traditional Spring Web MVC) may not be so obvious at first but hopefully the next part of the tutorial is going to clarify that.
The API gateways secured their firm place in the microservice architecture since the early days. It turns out that exposing all (or most) of your services to be publicly accessible is in fact easy but not a very good idea. We are going to talk about API gateways in great details later on in the tutorial but just to highlight a few key issues they help to address:
- discoverability: the consumers do not need to know where the upstream dependencies live (for example hosts and ports)
- partitioning: the consumers do not need to know what are the exact services application constitutes of since the architecture may change over time
- unified protocol: the consumers do not need to worry about all kinds of different protocols each service speaks
- client friendliness: different clients may need to shape data differently
The backend for frontend (or just BFF) came into the view not so long ago as an alternative to general-purpose API gateways. In short, each frontend is unique. At some point it became clear that the demands of the mobile frontends are quite different from let say a full-fledged desktop ones. To address this disparity, the BFF basic promise is to provide the outstanding support for one particular frontend, hence the name – backend for frontend.
There are quite a few frameworks to help us develop efficient BFFs but arguably the possibilities which are provided by GraphQL make the latter a particularly appealing foundation to build BFFs upon. And who can do the job better than Apollo platform.
The admin web portal is the back-office for JCG Car Rentals platform. It will expose the ability to perform certain administrative functions, including the ones needed for customer support. Since this is an internal component, we are going to build it in Scala using Play Framework.
Why: this is a typical frontend stack for modern, single-page web applications (SPA). The preference to Vue.js (and not React or Angular for example) is given because of its simplicity and ease of use.
In this section we have talked about the opportunities the microservice architecture provides with respect to having a freedom to make technical choices. We have discussed the pros and cons of monoglot versus polyglot microservices and some pitfalls you should be aware of.
In the next section of the tutorial we are going to have a conversation about different programming paradigms which are often used to build microservices and modern distributed systems.