Software is changing at
a sickening speed. Only a few years ago, we were thrilled that Spring saved us
from the bureaucratic EJB, now a 3-tier Spring/Hibernate/Relational DB
application is considered classic (euphemism for old).
Luckily, some ideas in software
never grow old. I do not like pitting micro service against monolithic, because
we have long learned our lessons and evolved from writing monolithic
applications. At the lowest level, we use object-oriented ideas to define
objects, we assemble objects into components, and we assemble components into
an application. At every level, we try to adhere to the basic abstraction rule:
loose coupling with tight cohesion. With this rule comes heuristics, such
as: single responsibility - one abstraction should have only one
responsibility. What makes an application monolithic, compared with micro
service, is in its deployment. The 3-tier classic application is typically
bundled into a war and deployed to some web server. While for a micro service
application, a single web page might get information from different services,
each is deployed and running independently.
As long as you live on the
earth, even in the cloud, there are some basic physical laws that you can’t
break. “loose coupling with tight cohesion” is one of them. A micro-service
architecture helps enforce this law, but if you get it wrong, it will punish
you even more. When everything is bundled in a big ball (monolithically), even
with well-intended interface design, there is nothing in the language itself
(e.g. java) that prevents you from sneaking around those well- intended
interfaces and directly invoke things behind interfaces. Often, an architect
can draw a beautifully structured design diagram with layers of boxes clearly
separated and communicating through well-defined channels, but in reality,
those boxes of layers might talk to each other under the radar, and you’d have
to trace into code to find out those “small” talks, so the beautiful
architecture diagram is deep down a big ugly smelly mess. With a micro-service
architecture, because services are separate processes, you have no other
choices but to talk through the well-defined channels. That is good, right?
Well, if you always have to deploy multiple services all together, you are
virtually having a monolithic application, the fact that this monolithic
application is divided into multiple services only increase overhead.
So good micro service
architecture has to embody “loose coupling with tight cohesion” so that one
change inside one service won’t have domino effect on other services. How to
divide services is both a functional, technical and organizational question and
has to be taken seriously.
The second law that you can’t
break is “Conway's law”: organizations which design systems ... are
constrained to produce designs which are copies of the communication structures
of these organizations. I first felt this law when I was working on a project management system. This system has resource module, time module, project module,
request module etc, I thought it was strange, because it was a project
management system, resource, time, project, none of these domains alone meant
anything, they had to work together to accomplish any business value. Then I
read about this law, and all became clear: this team was structured into resource
team, time team, project team etc, so the system structure reflected the team
structure perfectly. And also the system suffered from the problem described
above: although these modules were supposed to have well-defined interfaces, in
reality, inside code, calls were made haphazardly and over the years, the
system devolved into a big mess. Late on, this team felt financial pain and had to combine teams, so time and resource teams were combined, project and portofoli teams became one team -- and you could image how the supposed interfaces of these modules became dissolved. With micro service architecture, you are still bound by conway's law. How you design services will be partly decided by
organizational factors, and if communications among teams are vague, slow and
chaotic, communications among services will be vague, slow, and chaotic.
Managing multiple services presents many challenges, and those challenges are
not only technical, but also organizational (or managerial).
- Service evolution
Now that teams are organized
around services, they have much more freedom to evolve their services. But no
service lives in an island, they have to consider how to handle dependent
services. If a service marches on its evolution path without consideration of
its neighbors, the hell will break loose. Even with great technical
design patterns, some form of communication, negotiation and compromise is
required.
- Troubleshooting
With a monolithic application,
when something goes wrong, you know the culprit is that monolithic application.
Ok, the culprit is some module (or modules) inside that monolithic application,
so responsibility shifting, finger pointing can happen. With micro service
architecture, even with good logging and monitoring systems, it is hard to pin
down the culprit, and teams have to work closely together to troubleshoot.
- Architecture safety
Services have to be good
citizens. If for some reason, they malfunction (e.g. can’t handle requests,
dead-lock, hang, memory exhaustion etc), they need to let their neighbors know,
they shouldn’t bring their neighbors down. This requires teams understanding
and negotiating usage scenarios and communication etiquettes, for instance, how
many requirement at peak, how to behave in peak time.
In bad days, I feel old, and it makes me happy to think that even in this rapid changing world, some laws still hold.
(In my opinion, Microservice poses more management challenges, if a team can't manage monolithic software, it is going to be more challenging to manage a Microservice software, so be careful).
(In my opinion, Microservice poses more management challenges, if a team can't manage monolithic software, it is going to be more challenging to manage a Microservice software, so be careful).