One problem you face when working on a product that connects to multiple external services is managing all those external API connections.
This particular post is based on a unique experience. This product does not only read from multiple external services. It also writes to those services.
Some of the problems that quickly become obvious from this are:
- How do you ensure your code remains clean, easily maintainable and scalable?
- How do you keep your options open if you decide to switch any of those services in the future? Should you rewrite some part of your product core codebase or, maybe worse, the entire product?
- How do you ensure a broken connection of one service does not affect the others?
There are likely much deeper issues, but I want to discuss these things in this post.
There have been conversations around the concept of product scalability and what it really means; many, like me, believe that scalability is highly relative – if you don’t first access the need for decoupling components before diving in, you might end up building a fragmented system instead of service based system. A fragmented system might not be so bad if that’s what you are going for, but it would be a bad idea in this case.
In this instance, though, scalability revolves around usage and code maintenance. If you get a new team member, would they have to spend months to set up locally and fully understand your system? In this case, a scalable codebase means you can easily replace or add new components without having to rewrite your product completely.
To paint a more vivid picture:
Imagine a product that has the following components:
- Central API (containing auth, user, and some other core objects)
- Invoicing (that controls invoices for users)
- Purchases (that controls product types, pricing, subscriptions etc)
Invoicing must know product purchases, payment methods, payment types, users, and auth. Purchases must know about auth and user. Purchases must also know when payment is completed and the invoice is cleared. In turn, purchases then connect with the courier services for delivery, and invoicing connects with a bank, accounting software and possible Notification system.
This is hypothetical but imagine such a system.
One way to do this is to connect directly. While this will work, in the case of the product I am describing, some of the external services might change in future, and a direct connection might change things. So, we built a micro-service that serves as a central connector between all the other connections that need to be done.
Although it is called a micro-service in-house, that’s not what a micro-service is. Anyway, the service also has a tracker so that we can see the status of each external service in one space. We added a neat page to show the status of external services: green: active, yellow: issues/errors and red: down.
The service keeps things consistent. Regardless of the external service’s response structure, the central microservice keeps things consistent. And even if things change from the perspective of any of the external services, we don’t have to change much in our core codebase.
And that’s how we solved that issue.
In addition, this method also allowed us to create fallbacks for services and easily switch between services if something goes wrong with one. Obviously, we also keep a lot of data for decisions and up-time tracking.
This is an opinion and a growing solution. If you have a contrary opinion or suggestions, don’t hesitate to contact me on Twitter.
Happy Coding!