One major reason that many enterprises are thinking about migrating from monolith, on-premises applications to cloud native microservices is the cloud. The cloud provides many useful services, such as load balancers, monitoring and tracing tools, and auto-recovery—services that you’d have to implement and manage yourself if you didn’t use the cloud. Microservices are, in a way, designed for cloud, providing both scalability and portability. But should you convert your Java-based monolithic applications?
When Microservices Might Not Be the Answer
Choosing microservices involves trade-offs. Before making a choice to move your monolith application to microservices, developers need to understand that microservices architecture might not be a good fit for them.
Are You Building Netflix?
Scalability is one of the main advantages of using microservices. Each service can scale independent of other services, which makes them even more flexible. Often, the monolith application is created for a limited number of office users. In this case, using microservices could be an overhead. Even if scaling is required, it’s solvable by adding another cluster node to your application server.
Are You a Polyglot?
Another advantage of microservices is the ability to use different programming languages for different services. Sounds great, right? You can use a language that suits your business logic most effectively. But there are also a few drawbacks.
One is the financial consideration. Using multiple languages requires hiring developers with expertise in these languages. With high probability, these developers can’t support services written in languages other than the ones in which they have expertise. Developers capable of programming in different languages are more expensive. So, a higher budget is required.
Another reason against polyglot is the legacy code. Breaking a monolith written in Java EE to microservices written in Java is easier. They share a programming language, and you can copy some pieces of code to the new service without serious modifications. Rewriting these pieces in different programming languages is another story.
No More Shared Code, No More One Database
Usually, monolith applications are designed to work with a single database and share some code, such as data transfer objects and entities, among all its components. In microservices architecture, however, the best practice is to keep all services independent. They shouldn’t share any code and the database. Each microservice should use its own data storage, independent of other services. So, you should carefully redesign the database structure and the database application layer of your monolith to allow multiple-storage support without breaking the data consistency. In my opinion, you should place the most effort here.
Transactions May Become a Problem
If your application is in banking or another financial area, transactions can be one of the requirements. Java EE offers a solid support for transactions, and it’s not a problem for monolith applications. In microservices, the situation is different. Supporting distributed transactions between microservices is a more complicated task and requires more effort. It’s not as easy as adding @Transactional annotation and everything else is done by your application server behind the scenes. It’s not impossible, but it is more complicated. Use Saga Pattern or long-running actions (LRA).
Are You Ready for Microservices Dependency Hell?
The number of services you need to manage in applications designed using the microservices architecture is higher than in monoliths. If the number of dependencies is high, managing them can become difficult especially, too.
Testing May Become Problematic
It’s not a problem to test individual services. In fact, each service in microservices application is better tested than individual monolith components. On the other hand, integration testing becomes more difficult simply because application logic is now split between multiple services owned by different teams.
Another problem is unused code. It’s easy to detect it in a monolith. In microservices, it’s hard to say which API methods are used and which re obsolete. Teams should think about some tracing methods to detect it.
When the Decision Is Made
Now that you’re aware of the potential issues, and you know how to solve or avoid these problems, you are ready to break your monolith. Congratulations! I’d like to give you some advice.
Stick with One Programming Language, If Possible
Assuming that your monolith is written in Java, I recommend using Java for your microservices, too. As I mentioned, keeping the same language allow you to easily reuse some of your code, and the same teams who worked on the monolith can work on the new application. You don’t need to hire developers in other languages. However, it doesn’t make sense if some code is a perfect fit for another language.
Some design patterns were developed to help with migration to microservices, such as Strangler Pattern and Anti-Corruption Layer Pattern. Explaining how they work is a topic for my next blog article.
Use Frameworks Designed for Microservices
Some modern Java frameworks are designed for developing microservices, such as Helidon, Quarkus, and Micronaut. All of them can produce applications with a small footprint, small memory consumption, and fast start-up time. These parameters are the most important for cloud native services. All of those three frameworks are also capable of building GraalVM native image out of your application. Native images are OS-dependent binaries that can be natively executed. It means that your application will start-up almost instantly and memory consumption will be minimal.
There’s a trade-off in GraalVM. If you use Java Hotspot VM, it knows about the runtime environment and can optimize your code based on the environment. It’s not immediate, but eventually, it can become even faster than the executable code compiled using GraalVM.
So, if your monolith is written using Spring, use Spring Boot for your microservices. If it’s a Java EE application, use Helidon MP because it provides the best compatibility options. It also supports MicroProfile, a modern collection of open source specifications designed to build cloud native services. MicroProfile is hosted at Eclipse Foundation and supported by many vendors, including IBM, Red Hat, Oracle, and others.
A Final Note
If your application doesn’t require it, you don’t need to go the microservices route. However, if it makes sense to convert your existing Java applications to microservices, look for tools that can help make that conversion faster and easier.