The Strangler Fig pattern was used for several years while refactoring or modularizing Monoliths but in recent years it has become one of the most popular patterns that is used when migrating a piece of functionality out of a monolith to a microservice.
What is it
The name originates from a fig plant which feeds off a parent plant and then eventually takes root while the parent plant dies away.
In the context of a monolith assume you would like to move a piece of functionality out of your monolith into a separate service of it’s own. The strangler fig pattern would help you achieve this by allowing you to proceed incrementally.
How do you go about it
There are 3 steps involved. Firstly identify what needs to be moved out and analyze whether it can be done without disrupting the existing monolith. Secondly come up with a strategy to create this new service and then finally decide when and how you would like to switch your clients to the new microservice.
Identify what needs to be moved out
A recommended way is to identify a small chunk of functionality which ideally is self-contained. You could then either choose to copy this functionality from the monolith or implement it from scratch.
If you have access to the source code you could copy the functionality but if you are using a 3rd party and do not have access to the source code you may want to re-implement the functionality.
One thing to note is that if the functionality you are moving out depends on services or functions embedded deep within the monolith then this would make it rather challenging to create an independent microservice. The strangler fig pattern does not work very well in these cases.
How should you go about the migration
To start with replicate the functionality in the new microservice without adding any new features. One of the main reasons to go with the Strangler Fig pattern is to ensure that any time you can switch back to the old functionality should things not work out the way you planned.
It also pays to deploy the new microservice to the production environment even if it is not released to the customer. This enables testing in the production environment and helps to iron out any deployment issues which may surface only in production.
You could choose to move all or just a portion of the functionality. At the end of the day, the Strangler fig pattern should not be used to introduce new functionality or fix too many bugs. Once customers start using your new service there would be no returning to the old service since a rollback would be needed and no customer likes to go back to a less functional buggy version.
For these reasons changes from the original functionality should be near zero at best and the implementation and cutover to the new microservice should happen as quickly as possible.
As the implementation progresses you could just return a 501 from the new microservice and all calls to the functionality should keep going to the original function in the monolith.
If the functionality being migrated also maintains persistent state you would also have to come up with a strategy to access this state as well either by sharing access to the underlying data layer or replicating the data layer which can then be exclusively used by your new microservice.
How and when do you switch over to the new service
The Strangler Fig pattern works very well with services supporting HTTP. This is because the strategy used in this pattern when moving clients is to redirect all or a portion of the clients to the new service.
You could use a reverse proxy like NGINX to intercept the client calls and redirect them to the new microservice based on the URI. For instance if your new microservice encapsulates the reporting functionality a call to http://someapp/payroll could be routed to the monolith and a call to http://someapp/reports could be routed easily to the new microservice using NGINX as a reverse proxy.
You could also redirect only a portion of the users to the new microservice and this could be achieved by parsing the parameters in the client request and accordingly branching a subset of these requests to the new microservice.
Once again NGINX offers ways to implement the A/B testing scenario described above and you could choose to incrementally add functionality to your new microservice and expose it only to a subset of users who test it. Once you are comfortable with the new microservice the reverse proxy can easily redirect all calls to the new service and effectively the old functionality in the monolith becomes dormant and can be eventually removed.
Yet another way to implement the switch to the new service is to use a feature toggle.
All this ensures that should things not go as planned you always have the old functionality running in the monolith as a fall back option.
Support for other protocols
Even though HTTP may be very amenable to the Strangler Fig pattern, the pattern can still be used with protocols like RPC and even asynchronous messaging systems.
You could intercept the requests at the proxy layer and then map the request to whatever protocol is supported by your new microservice. Alternatively and this is a better approach, you could get your microservice to support gRPC and HTTP endpoints and then implement a gRPC to HTTP mapper inside your microservice. This ensures that your service is able to handle different types of clients and this is a more reasonable approach than adding the intelligence inside the proxy layer. Proxy layers should be kept dumb and should only route requests to the corresponding service without much ado.
If your existing monolithic application sends out messages into a message queue a content based routing system could be used. So for example all messages pertaining to your newly created Reporting microservice could be intercepted and redirected to the messaging queue maintained exclusively for the Reporting service. All other messages could be routed to the queue maintained for the monolith.
This approach introduces another layer in the form of the additional queues so an alternative would be to just read off messages from a centralized queue and then get the monolith to ignore any messages meant for your new service. Consider the pros and cons of implementing either of these solutions before deciding on an approach.
The Strangler Fig pattern helps you to deploy your new functionality in parallel with the existing functionality and release it to users only when you are comfortable. It is highly sought after since it enables phased parallel deployments and releases, a fallback to the older functionality and an eventual switching off of the older functionality when the new service is truly capable of taking on the entire load.