Since 2014, I have been involved with several projects with a goal to convert an existing application to utilize a microservice equipped with a RESTful API. In every case, the desire was to allow a client to make RESTful API calls (as needed) to the microservice for retrieval, persistence and other data processing needs.
I fully believe I am not the only one who has ventured down this course. In fact, in a personal adventure, I did the very same thing for my mother-in-law as documented in my ” New Application Journey
Does this mean that such a strategy is challenge-free? Absolutely not.
This article is the first of a series and will talk about common pitfalls I have learned while adopting a microservice strategy.
Monolith -> Microservice Challenge
The business sponsor stepped up to the plate, pleaded the C-Suite for funds and won. That old application that everyone was tired of using is now going to be replaced. The front-end developers are off trying to figure out what framework they will use for the client, while your team needs to figure out how to re-do everything in a RESTful manner.
There’s one known requirement:
Requirement 101: Because there will be a phased adoption, the database design cannot be altered
That one requirement really doesn’t faze anyone on the team — even if it should, because everyone is excited to do something new, something current, something exciting.
Teams emerge and begin taking segments of the existing application and build RESTful APIs which will be utilized by the new client being developed for the front-end team. After some serious development, that old monolithic application (that everyone has been ready to shut down) has been replaced with a RESTful API.
Well…it turns out, the team likely just replaced one huge service with another one.
In all of the cases that I have witnessed since 2014 when teams reach this point, they realize that a majority of the complexities and challenges with the new RESTful API still exist.
Some common items:
- Special circumstances which cause non-standard procedures that delay or extend deployments
- There is still a very large codebase to support, often difficult to navigate when debugging
- Duplicated business logic across classes, due to the number of teams and a lack of communication
There are more, but these three things often have everyone wondering what was really accomplished with the project.
The Underlying Database
I noted above, with Requirement 101 that the database design was not allowed to be changed.
This is often a common approach — because everyone wants to be agile (or rather flexible). They want to introduce new features slowly or to a limited audience. So, the old system and the new system both need to continue to function alongside each other. I get it…it sounds great in theory.
The reality is that over time the original design of the database no longer matches a design that can provide better services for the business. When you consider microservices adoption, a common approach is to break things into smaller segments. This translates to the data layer as well.
Consider a huge monolithic application that tracks information for some form of a lease. In this example, the leasing application could be an office building, an apartment complex, or even a shopping mall.
The system itself might have the following segments of data:
— the person wanting to lease the space
- Property Owner
— the entity which owns the lease which is available
- Property Data
— information about the property and space itself
— data regarding the lease itself (terms, conditions, etc.)
- Accounting Data
— the transactional data for the life of the lease, tenant, property or space
Based on the lessons learned since 2014, I would consider taking a stance that the new service, would be a collection of smaller microservices. Perhaps, those five items above would be individual services. The key is that the data aspect would be connected to a new database instance — which only houses the required data for that particular service.
Then, building and deployment of a messaging tier would allow the services to communicate as needed. As a result:
- The services can run in a stand-alone mode, with a laser-focused data tier and adhere to CI/CD strategies
- The codebase is much smaller since it is focused on a given aspect of the business
- The potential for duplicated logic should be lessened, because of the focus of the service and adoption of a shared library
Monolith -> Multiple Microservices Challenges
In the example above, multiple microservices can be adopted, along with a messaging tier, to avoid replacing one monolithic application with another. This approach is not a perfect approach either.
The key for this kind of implementation is a solid messaging tier to make sure cross-service communication flows as expected. Conversely, having a strategy in place when things do not go as expected is actually more important than the common/happy path.
Using the example above, there may be a case where the Accounting Data service receives information for a Tenant which is not already in the system. As a result, before the information can be correctly posted, the Tenant service will need to perform some necessary actions.
Additionally, the Tenant system may require data regarding another service, like a Property service or even the Lease service, before the Tenant service can complete the necessary task. All of a sudden, the simple request to post some Accounting Data leads to a series of requests — all of which can encounter an unexpected exception.
The other challenge with this approach is trying to understand and document the cross-service communications. Just as how things can become complex in a single microservice, the complexity is moved to a different level — now relying on different codebases, services, a messaging tier, and CI/CD strategies.
In this first part of my series, the focus has been on design strategies that are often employed, along with the potential consequences which can emerge. While there isn’t a silver-bullet approach that can apply to all situations, there are three things I recommend always keeping in mind:
- Keep it Simple
— Complexity for the sake of complexity is never the correct answer. In fact, I recommend reviewing my ” I Want My Code to Be Boring
” article for additional details.
- Keep it Lean
— A microservice should always provide a payload that matches the intent of the URI being requested. See my ” When You Don’t Want the Whole World Returned to You
” article for more information.
- Keep it Focused
— Your microservice should provide value that is focused on aspects of the business where the API is the owner of the respective domain data. The ” What Is the Value of Your RESTful API?
” article I published in December 2016 still rings true today.
In my next article, I will focus on the following items:
- Challenges with a Loose Contract
- Shared Components Between Microservices
- A Session-Free World
Have a really great day!