Don't Build Distributed Monoliths!
Think you're building microservices the right way? Think again.
Join the DZone community and get the full member experience.Join For Free
Microservices have been a hype topic for the last several years, and many developers are using this concept when structuring and implementing code nowadays. However, as always, every technology has advantages and disadvantages. So when I’m asked whether microservices architectures make sense, my answer is: It depends!
Cloud-native architectures and microservices clearly have a lot of benefits. One benefit is the simplicity of smaller modules. For example, I used to work on a product that had grown for many years and it had several million lines of code. Developers were scared to change even a few lines, since the effects were not predictable. As a result, productivity was very low. Smaller services would certainly have helped a lot to handle the complexity.
Because of this, breaking down monoliths in multiple microservices might make sense. The key question is how to do this.
One anti pattern I’ve often seen is to have too many microservices, where each microservice basically represents a single entity with typical CRUD operations, in many cases with pure RESTful APIs. In order to communicate between microservices, synchronous REST invocations often are used.
This does not lead to service-oriented architectures, but to distributed monoliths. There are too many dependencies, which are even harder to handle in distributed environments than in monoliths.
One approach that helps to define microservices is domain-driven design. Additionally rather than using microservices, another option is to use macroservices, which are more coarse-grained. Especially when applications need to handle transactions these approaches should be considered.
Even when using more coarse-grained micro- or macroservices, the main challenge is how to handle dependencies between them. Synchronous invocations can easily lead to distributed monoliths again.
As always, there is not one single solution. But one possible solution you should consider is an event-driven architecture, rather than synchronous REST invocations.
Basically, there are fewer dependencies and less or no blocking, which leads to more loosely coupled services. However, as long as multiple services need to work together in bigger systems there are obviously some dependencies.
There are a number of great talks and resources available describing the different alternatives. I especially like these talks:
- The Many Meanings of Event-Driven Architecture – Martin Fowler
- How Events Are Reshaping Modern Systems – Jonas Bonér
- Event-Driven Microservices – not (just) about Events! – Allard Buijze
Here are some of the patterns you should be familiar with when developing event-driven architectures.
- Event notifications: Events are sent to notify other services, which can call back to find out details if necessary
- Event notifications with state transfer: Events are sent to other services, including the new states of objects
- Event sourcing: All events are made persistent, and the overall state of systems are aggregations of these events (which also allows time travels)
- CQRS: Read operations are optimized in separate components. More expensive write operations are handled separately and need to be synchronized.
- Command pattern: Commands are sent asynchronously and responses are sent back as events as well
This article is part of a series of articles that documents how to modernize a sample Java EE application from 2010 with modern technologies.
The sample application is a simple e-commerce application. The original application and the source code of all subsequent modernization steps are available open source on GitHub.
In a future article, I’ll describe how I’ve used events to de-couple two services.
Published at DZone with permission of Niklas Heidloff, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.