This post discusses a specific case study I encountered while working for Here Mobility. It describes why we decided to unify two of our services, and why we initially separated them. I believe it demonstrates well when you should consider unifying two microservices, and hope it will help others make good designs for future products. This is the first in a series of posts. In my next post I will share the plan of how we actually unified the services, while continuing to serve customers.
Background
The product we worked on is called Mobility Dispatch. Mobility Dispatch helps taxi companies manage drivers, rides and passengers easily. It enables matching of riders and drivers through an intuitive admin panel, including real-time visibility and tracking of the fleet and ride statuses. The product includes a web application and a mobile application. For the purpose of this post, I’ll focus on the specific feature of ride creation. The user can create two types of rides - immediate rides, that are sent to drivers to start right away, and pre-booked rides (which we will refer to as prebooks from now on), that are due in the future.
We decided to create separated services to manage rides and prebooks - a Rides service and a Calendar service, respectively. We made this decision due to the following product features and infrastructure circumstance:
Present active rides on a live map showing the current location of all vehicles, to help the shift manager know where his fleet is at all times. This meant that there will be a lot of calls from the clients - the mobile applications reporting current locations, and the web application, polling updated locations. Hence, it made sense to have a dedicated service to handle the load.
Present prebooks on a prebooks-only table, so the shift manager can see all future rides concentrated in one place. This tightened a sense that prebooks and rides are actually different logical entities.
A newly created prebook that contradicts an existing prebook, sends a notification to the web user. Thus, we needed a constraints manager for prebooks. Rides did not need such a thing. This was another reinforcement to the concept of rides and prebooks being different logical entities.
The prebook concept was introduced after we already supported immediate rides, and we already had a live Rides service. Rides service was an outmoded service, working with a tailored state machine for immediate rides. We figured that having a clean service to support the new functionality would require less development time.
The Problem
The separated services architecture served us for about a year. Throughout that year we encountered several difficulties that made us question the design:
Changes in API requires changes to both services’ codebase: if we added a call like “getAllRidesByAWhiteVehicle” we needed to implement it in both services, which made adding new code cumbersome and consumed more time. This actually indicated that rides and prebooks were not alienated entities as we originally thought.
Back and forth network calls between both services: although in the beginning it seemed that there was a clear separation between Rides service and Calendar service, we encountered many situations where the services needed to communicate. The easiest example would be the auditing history of a ride.
Services have a similar codebase: the data models of a Ride and a Prebook were similar — both had similar ride constraints such as number of passengers, wheelchair options etc.. Also, both services worked with a state machine and a scheduler that could be implemented alike.
The last straw — a new business logic: Our product handed requirements for editing an active ride so it became a prebook, and vice versa. This broke the assumption that ride and prebook are alienated entities and having them managed in separate services, and saved in different databases, did not make sense anymore.
The first three of the above made us eager for a chance to unify the two services. Adding the new business logic was the opportunity we needed to refine our design, because we could leverage the addition of new features to an infrastructure change.
I think this study case pinpoints some of the delicate nuances in microservices architecture. When adding new functionality we should question whether our architecture still justifies itself. In this case, at the beginning it seemed that a ride and a prebook have a right to live as separate entities, and therefore have separate services and databases. As more requirements and features arrived, it became clear that the separation of services caused cumbersomeness and needed to be treated.
Top comments (0)