top of page
Search
  • Writer's pictureDean Hiller

Legacy Monoliths to Microservices

This is a continuation of a previous topic "Monoliths can scale into Microservices?" however this article is about getting through a tougher issue...going from legacy monolith to microservices. If you are here, you are most likely looking at a monolith that looks like the following

The high level steps I am going to take are the following

  • Get dependency injection working to inject deep, very deep with minimal changes

  • Get a test suite in place that is at a high enough level such that anyone can run it as we refactor the monolith to prepare for a move to microservices

  • Push the database transactions down as close to the ORM layer as possible

  • Preferably use an in-memory database for testing

  • Minimally refactor just enough to insert a microservice API or 2 and their implementation (impl 1)

  • Launch a microservice with the implementation (impl 2)

  • Start redirecting some traffic from impl 1 to impl 2

The above steps assumes you have a threadpool of threads serving up requests to your monolith(usually in your webserver framework of choice). If you have a monolith that has threads deep in the system that pull from a queue, that would be a different topic and there is more steps to take into account in that case.


The good thing about this process is

  • You are not rewriting from scratch which ends up missing all the nuances of the original system and a huge amount of detail in the code

  • You are cleaning up the monolith so can always pause work and continue later

The first thing you need to do is identify any remote systems you talk to and make sure there is a very clean API interface of methods that take request DTOs and response DTOs and that they are all asynchronous as well. There are good examples in the article "Can a violation of REST be good?". Once you have this API in place, make sure you inject it from the very top levels (manual injection or using a dependency injection library...either way works). Once you have the few APIs in place and their implementations injected, you are ready to get to adding a test suite phase and you do not need too many tests (I will explain why later). This is the hardest part of the pivot to microservices and I went through this once on a system in Twitter. It is the hardest as there is not much of a safety net for refactoring yet.


Testing

The next step you are going to need to do is create a high level test suite much like my feature testing blog article and it needs to be as high as you can go. Think of this as a smoke-test suite that can run pre-master branch commits. Generally, do one test for each endpoint in your monolith's API. Believe it or not, one test for each is typically good enough to not cause a huge amount of bugs if and only if you concentrate on moving around chunks of code (but not cleaning up the details of the code). Typically, you will also want to use an in-memory database for this as well. One the tests are in place, it gets much easier and there will be some fallout but much less than normal.


Database Transactions and APIs

I suggest creating something like PersistenceHelper(previously called TransactionHelper) in webpieces where developers can easily behind an API write code like this


persistenceHelper.runInTransaction(() -> functionToRun(var1, var2));


You need to then push all database transactions as low as you can and use this function and refactor to insert your 1 or 2 APIs into your system. Your API methods all need to have a signature like the following


public Promise<SomeResponse> methodName(SomeRequest request);


or in some languages


public Future<SomeResponse> methodName(SomeRequest request);


The SomeRequest and SomeResponse are simple data beans while these methods go in an interface as you will have two implementation eventually. The first implementation will just work directly on your database. The second implementation will simply translate from API to a protocol but make sure you have read "Can a violation of REST be good?".


Let's re-use Implementation 1

Now, at this point, you have an API and a library behind that API. You need to create a microservice that uses that same library exactly as-is. Spin up this microservice in production with 0 traffic to start.


Let the fun begin

Next, pick a customer or two and write some code to redirect their traffic in the monolith to use the new implementation(remote) or the old implementation(local). If you are really advanced you can generate a class behind an interface and have a single method. In Java, this is the Proxy.java class and you implement an InvocationHandler. Contact me for more information on that https://www.linkedin.com/in/deanhiller/. For now, just use something like this


if(isCustomerInTestSet(customer)) {

sendRequestToNewRemoteImpl(request, remoteImpl);

} else {

sendRequestToOldLocalImpl(request, localImpl);

}


Work out any bugs and apologize to any issues that customers run into and keep moving customers over until you can remove the original implementation. #win, we are now starting to use microservices.







73 views0 comments

Recent Posts

See All
Post: Blog2_Post
bottom of page