Wojciech Galanciak
Senior Eclipse Developer on the MyEclipse and Webclipse products.
Senior Eclipse Developer on the MyEclipse and Webclipse products.
In the last couple of years, we have been hearing a lot about microservices. There is already a lot of valuable (and also not so much) content regarding defining the term and the general concept behind it. The definition is not always enough to fully understand how to introduce the idea into real life practice. It gets especially complicated when related to a general approach of creation.
After years spent building applications with an architecture approach, usually a monolithic one, switching to microservices is not only a matter of reading one or two articles and/or a book. The human mind gets used to specific schemas and procedures which make it is easier for us to achieve a real efficiency in a specific topic. Often times it takes a new generation to accept new ideas. The problem is not only related to a lack of background knowledge that is required to understand the new concept, but also it is not easy to switch a way of thinking (especially after years of doing it the old way). This issue applies to all fields in engineering, including software.
To start using a new approach, especially if it is related to architecture, there are two requirements for success:
The first is easier to achieve—the second can cause us a lot of trouble. But to be fair to readers who do not completely fulfill the first requirement (and just have a general knowledge about the topic), we are going to introduce them to microservices in this article. In an upcoming second article, we will focus on practical application of microservices architecture. In both articles we are going to focus on Java and its applications to this approach.
Instead of providing an abstract definition of a service, let’s look at a simple use case.
Usually, if we are trying to build something, we do not reinvent the wheel. When we meet a problem, we first try to find an existing solution which solves it for us. This approach saves us a lot of time (and money). Mature solutions are very well tested and even if we have any doubts then we can usually look directly into the code (if this is an open source solution, and it usually is) and try to understand and verify it. For example, if we need a phone prefix management we look for a library (e.g., libphonenumber). This library exposes a set of features related to its main function. In this case it may have its own data source for country codes (stored in the library itself and kept up to date by its creators). We do not have direct access to it. Instead, it may have an external interface which is used to expose it to us. Also, we can easily upgrade it to a new version without changes on our side (except in the case where the API changed). As you can see, our library fulfills the following requirements:
In short, the library is a reusable component which provides an endpoint to consume its capabilities.
The same situation applies to a service. The difference is that it is considered on a higher abstraction level than a library. In this case, we can define its main features which also fulfill the requirements defined above:
Now when we have our service defined, we can define microservices architecture as well. It is nothing more than using services as bricks to build the whole application. For a better perspective, let’s compare it to a more common approach—monolithic architecture. It is quite possible that most applications which you have already built are based on it. In short, we have user interface, business logic and data source. Here is a list of its main features:
Image 1. Monolithic architecture overview
As you can see, monolithic architecture is actually a set of services gathered into one unit which has to be developed, maintained and deployed as a whole. Thanks to this structure, we may actually transform such an application to microservices. This process is not trivial because there are a set of challenges which appear during the process of extraction, in particular, the capability to create a separate service (e.g., data source access).
Image 2. Microservices architecture overview
This is the first question that comes to my mind after reading the text above. The answer is—it depends.
If you are a full stack developer (or a small company) who builds everything by yourself then organizational features are not considered. In such case, do not think about a single application with a set of business capabilities, but instead, think about one application per capability. Then use something like REST APIs to communicate between them. For example, instead of using a direct call to the database to check if the provided access token is valid, you can call an external method from an Identity Management service which does it for you. Congratulations! You just introduced microservices architecture to your development. It is as simple as that. Of course there are other parts like build automation, however they just simplify things and make them more efficient, but are not a part of the core of this approach.
In the case of team development, an additional change has to be introduced at the organizational level. Backend developers get a product(s) to take care of. At the same time, they have more flexibility in a context of delivery time. Crucial bug fixes may be deployed to production as soon as they are fixed and tested. Such change does not interfere with development of other components. Also, smaller service means less issues with knowledge transfer in the case of changes in the team. This is important—especially for huge, mature products.
The common thing for all developers is that microservices are fully technology agnostic. As a result, you can use any language as long as you fulfill all requirements in a context of application architecture. It also means that in one organization each service can be developed in a different technology stack.
Now let’s briefly look at microservices from a Java developer perspective. We can distinguish several different approaches when building services using Java and all the technologies around it. Each of them has a common goal—create and expose a simple service. Actually, we start from the simplest way and then extend to provide more features.
The simplest way to create a service in Java is to just pack our code and its dependencies into one single jar. In such case, a whole communication layer has to be provided by a developer. One is also responsible for providing all necessary libraries. A developer can decide how to expose the interface for other components and has multiple options, from a custom socket server implementation to an embedded HTTP server, with each service instance is a separate Java application.
Another approach is to pack the application as a single unit but build it on top of an application framework. A good example is Play Framework. The result of the building process is a single jar which uses Netty for a communication layer. This approach simplifies and accelerates development in comparison to the first approach because we have all tools available to start building our API.
The third approach is to provide one more layer—a container which wraps one ready-to-use service instance. As a result, we can include our application deployed on any application server (e.g., Wildfly) and all other configuration/tools into a single container. In this way, we start to manage the container (start, stop, scale) instead of the application itself.
In the first two cases we do not need any additional external configuration—service instance is created by copy/paste and execution of a single command.
In this short article we have presented you microservices architecture from a developer’s perspective. This is just a brief look at general features. Right now you should be ready to go deeper into more advanced topics and most importantly, start to build your first microservices application. Stay tuned for more articles about microservices. If you’re not already subscribing to our blogs, why not do it today? Subscribed