MMS • RSS
A couple of years ago, Vladik Khononov and his team decided to start using microservices, but just a few months later they found themselves in a huge mess. The reason was that they concentrated on new cool technologies without paying due attention to more fundamental things like modularity and how to achieve it, he explained at the recent DDD eXchange 2018 conference at Skills Matter, London. They invested in serverless frameworks, platforms and messaging mechanisms, but too little in thinking how to decompose a system into microservices — finding the boundaries and where different functionalities should be located among these boundaries.
For Khononov, CTO at Internovus, and his team, the belief from the beginning was that the smaller a service is, the better it will be. That led them straight into building a distributed monolith, and during the coming years they struggled to move away from their tiny services while evaluating different decomposition strategies.
Khononov notes that ubiquitous language is a cornerstone practice in Domain-Driven Design (DDD), and a way to practice this is to talk to domain experts in their own language. Sometimes you will find that they have different mental models for the same business concepts or use the same terms to describe different concepts and if so, it’s an indication that these concepts belong to different bounded contexts. From the beginning Khononov and his team used these discovered boundaries to define services, with each boundary becoming a service. He notes though that these services represent quite wide business areas, sometimes resulting in a bounded context covering multiple business subdomains.
As their next step, they instead used these subdomains as boundaries and created one service for each business subdomain. In Khononov’s experience, having a one-to-one relationship between a subdomain and a service is a quite common approach in the DDD community, but they didn’t settle for this, instead they continued and strived for even smaller services.
Looking deeper into the subdomains, they found business entities and processes and extracted these into their own services. From the beginning this final approach failed miserably, but Khononov points out that in later projects it has been more successful.
Looking at these three strategies, Khononov notes that working with bounded contexts has helped them find the boundaries of the largest valid monolith, however, although it’s a viable working model, for him it doesn’t quite fit the ideas of microservices. When choosing between business subdomains and entities, he claims that the best level of decomposition depends on the system you are building together with its use cases. He emphasizes that microservices is not really about the inside of a service, but rather about the interaction and coupling between services.
The threshold upon which a system can be decomposed into microservices is defined by the use cases of the system that the microservice is part of.
Khononov has still not found an easy way to evaluate a system’s design, but he believes that we now have accumulated enough design heuristics to help in streamlining the process of decomposing a system into microservices. The ones he has found most useful include:
- Always decompose to the level of bounded contexts. Don’t decompose further, unless you have good reasons to. Distributed systems have their own challenges.
- Core subdomains is where a company is making money. Hold any decomposition until you have gained domain knowledge and ensured you have the right subdomains.
- Buy or adopt generic subdomains. They are already solved problems and there is no competitive advantage in implementing them yourself.
- Supporting subdomains are needed for supporting the core domains but don’t add any competitive advantage. They are often so stable and simple that they can be further decomposed at an early stage, maybe down to entity services.
- Use consistency requirements to help in finding functions or methods that must stay in the same service.
- Ensure that events are explicit and self-describing. Consider using private events as an implementation detail within a service, and a more restrained set of public events as part of a service’s public interface.
- Look for services that change at the same rate; maybe they can be merged to reduce complexity.
- Evaluate each service’s interface. If it’s too wide, maybe the service can be decomposed into smaller ones, with a large part targeting integrational aspects, then consider reassessing the boundaries to simplify the overall system design.
Khononov concludes by noting that as the average size of services in a system gets smaller, you will move from a monolithic big ball of mud, through relatively large services based on bounded contexts towards microservices. But he emphasizes that if you continue towards even smaller services, you will end up with a distributed big ball of mud.