Blame it on monoliths - Part 2
Moving to micro-services will solve all my deployment and maintainability issues.
Story Behind this Post
This post is further discussion of the post that can be found here where we discussed two most prominent features about micro-services and tried to explain how they are not exclusive to Micro-Services actually they are attributes that can be achieved with good architecture and following basic SOLID
principles.
Link to the first post of this series is here Blame it on Monoliths - Part 1
In this post(Part 2
) we will take an example of an application that looks like have a micro-service architecture and try to see how having a micro-services
alone will not help with Deployability
and Maintainability
of the software.
The example in this Post is inspired from the Book Clean Architecture by Robert C Martin
. It’s a great book and must read for any developer to understand the basic principles of Software Construction. So all the credit for this post goes to the book.
Flight Finder System (Flight Finder)
This system knows about many airline companies, and allows customers to search for flights. Let’s assume that the customers select flights based on a number of criteria, such as date/time, cost, luxury. We wanted our system to be scalable
, so we chose to build it out of lots of small micro-services
. We subdivided our development staff into many small teams, each of which is responsible for developing, maintaining, and operating a correspondingly small number of services.
Here are few Micro-Services we have in our fictitious
architecture and their purposes from the above diagram.
FlightFinderUI
is front end application used by customers to search/book flights using our system.FlightSearch
Service is used by Flight Finder UI to search the flights available for provided criteria and combination of User preferences.FlightAggregator
Service is responsible to scan through various supplier inventories and find all suitable flights for provided user criteria.FlightSelector
service takes user criteria of cost and time and then further filter down the available flights.FlightBooking
service order the selected flight for the Customer and communicate the booking toAirline
system.
The FlightFinderUI
is front end used by the customers, who use mobile devices/web browser to search flights. The FlightSearch
takes the user selection criteria as input from FlightFinderUI
and call FlightAggregator
service which then examines the inventories of the various Airlines
and determines which flights are available for the user and then return results back to service FlightSearch
which keep it in some local cache.It then passes all the flights available from different airlines to the Service FlightSelector
. The FlightSelector
service takes the user’s criteria of cost, date/time, luxury, and so forth, and then call the UserPreference
service to fetch additional user preferences and further filter down the list of available flights. Then it returns results back to FlightFinderUI
. Then customer select one of the available options and FlightFinderUI
invokes the FlightBooking
service, which book the appropriate flight.
Warning: I am no Architect and lot of people may not like the architecture but keep in mind this architecture is fictitious and used for example sake only.
Marketing Happens
One bright and cheerful day, the marketing department holds a meeting with the development team. In this meeting, they announce their plans to find flights for Kittens customers can look up flights that are Kitten friendly.
I am just making up this requirement for example sake.
- You must have figured that out already :)
Only one of the Airline Company have agreed to provide this service but others will follow in future. Some airline companies may decline. Some of the passenger will have Cat allergies so a Flight that has been used to deliver kittens should not be selected for customers who declare such allergies and so on.
What needs to change
How many of those services will have to change to implement this feature? All of them. Clearly, the development and deployment of the Kitty feature will have to be very carefully coordinated.
In other words, the services are all coupled, and cannot be independently developed, deployed, and maintained.This feature request involved cross cutting concerns that run through the most the services. How we could have made such feature request least painful
or more manageable
?
SOLID to the Rescue
Let’s assume for a moment we don’t have micro-services we have a monolith with component based(Plugin based) architecture instead of multiple services we have multiple components that make up the system.
Component is something equivalent to DLL in .net world, jar in java world or GEM in Ruby world.
Specially if we look at the Open Closed Principle
it would have suggested us to create a set of classes that could be polymorphically extended to handle new features.
We should create a abstract class to define the behavior of FlightSearch
class and then we implement classes for Passengers
and Kittens
that will be polymorphically used in place of the abstract FlightSearch
class.
To add new feature for Kittens
we will add new component(DLL/jar) that override the Abstract FlightSearch
class. So The new feature for kittens has been placed into a Kittens component. These two components override the abstract base classes in the original components.
So we will end up having two components Passenger
and Kittens
both follows the dependency rule dictated by SOLID
principles. The FlightFinderUI
will have a factory which will decide which override of Base abstract should be created to handle a request depending upon if user is searching for Passenger
flight or Kittens
Flight. If we need to add another feature in the future we add another component that override the abstract base class for FlinderFinderSearch
.
This way in future if we add new feature we just update FlightFinderUI
to load another DLL dynamically so the factory can crate instances of it.But nothing else needs to be changed. Rather, a new jar file, or Gem, or DLL is added to the system and dynamically loaded at runtime.
So if we have to show the Component diagram for the FlightSearch
.Note: I omitted other classes from the digram for brevity. It will looks something like below. The empty arrow
head show inheritance(Abstract override). Red rectangle shows the component boundaries.
So the abstract base class for FlightSearch
is overridden polymorphically for Kittens
and Passengers
. If in future they want to find flights for Parcels
also they can add another polymorphic override for the Parcels
.
In this way we can add new functionality to the system by just adding new code instead of editing existing code. This is what exactly the Open Closed Principle if this this principle is followed new changes can be made with least pain.
It doesn’t even matter if you are using Micro-Services
or just using plain component or plugin
based architecture
The effort needed to add new feature will be similar.
What we learned
What we have learned is that architectural boundaries do not fall between services. Rather, those boundaries run through the services, dividing them into components. To deal with the cross-cutting concerns that all significant systems face, services must be designed with internal component architectures that follow the Dependency Rule,
Here is what our micro-services diagram will look something like if we stick to basic SOLID principles.
See you later
This post was part of the 2 post series. In the first post we discussed the Two Major benefits of the Micro-Services
in details and how they are not exclusive to the micro-services
and then we discussed the theory about the what core SOLID
concepts actually help with Maintainability
and Deployability
of the code. Then in this post we looked at the example using an interesting application. It’s time for me to say goodbye will see you soon with some other post. Enjoy your life and Be humble. I will see you soon new post.