What your mom didn’t tell you about microservices

If you can’t build a well-structured monolith, what makes you think microservices is the answer?

Simon Brown

The reality

We are all building microservices these days. Whether we need them or not. There is plenty of material on the internet talking about what dangers and pitfalls microservices bring, what to avoid, what to look out for. There is also a lot of theory on good practice. However, in reality, our microservices are not micro at all. Nor are they giant monoliths. We usually build something in between – medium-sized services.

Trendy buzzwords for our architecture

In almost every project I have been involved in, certain goals were defined for our system and architecture to meet. Almost all of them looked like this:

  • Modularity
  • High Cohesion
  • Low Coupling.
  • Scalability
  • Maintainability
  • Testability
  • Performance
  • Usability
  • … (a few more buzzwords)

They all look very good. It is impossible to disagree with them. So we can say that in theory everything looks perfect. However, as I mentioned earlier we usually build medium-sized services. Are we able to achieve such ambitious goals on a system-wide level without implementing them on an individual service level? Personally, I think not. Moving on to specifics, in practice, I have almost always seen something like this:

  • Modularity. The services whose codes I have reviewed are structured in the same way, the so-called layered way. When dealing with a mid size service it kills modularity.
  • Cohesion. Classes organised around layers do not fulfil the condition of good cohesion. They need dependencies, to other layers, very often to other services, creating a complex mesh of dependencies. In the long term, this leads to a so-called big ball of muds. High cohesion within packages and low coupling between packages are essential for a good system design.
  • Coupling. Another consequence is strong coupling between packages and classes. Which leads to another problem which is
  • Scalability. If, in the future, we decide that a feature should (let’s leave aside for what reasons) be in a separate service, it takes a lot of work to cut it out. Or vice versa if we decide that we want to remove a feature it should be as easy as “one click”.
  • Maintainability. In the long term, working with a system without a clear separation into well-crafted domains/features is very difficult. The lack of proper modularisation causes considerable problems with the readability of the code itself and the translation of business requirements into technical language.
  • Testability. We are able to test each module in isolation, going through the business paths clearly.

Consequently, at a single service level, we see a big ball of mud, with an anaemic domain model and infrastructure dependencies mixed throughout the code. At a whole-system level, we see a distributed big balls of mud and a failure to meet any of our architectural goals.

Can we do something about it?

In my opinion, we should pay more attention to the quality of solutions at the individual service level. The first step towards better is to organise features into appropriate packages. The next step, if necessary, is to get rid of the anaemic data model. If our domain is sophisticated and quite large, we can go a step further and apply the so-called ports and adapters architecture.

1 Comment

Leave a Comment