This week I had a discussion with one of the developers in my organization on pass-through services. A Pass-through service is a service that wraps an existing service without much logic. Its job is to delegate to the downstream service. The existing service could be legacy service or an external third party API. The developer was questioning the purpose of pass-through services and the amount of development effort that goes in writing and maintaining them. In this post I am sharing the reasons that I gave to the developer to help him understand why pass-through services are not a bad idea and they can be helpful in the long run.
Reason 1: Encapsulate logic in one place.
On the surface it looks like the job of pass-through service is to take the request, call the downstream service, and return the response. But if you scratch the surface you will find a few concerns that you need to handle specific to your application. Some of these concerns are:
- Exception handling. Your application might want to handle exceptions in a specific way. Downstream service will not oblige to your requirement.
- You might want to cache the downstream service response and store it in some local database or cache. You don’t want all consumers of downstream service to duplicate this code. It is good to centralize it at one place.
- You might want to add pagination. Downstream service might not support it.
- You can retry idempotent operations of downstream services. Or you might make an non-idempotent operation idempotent in your pass-through service.
- You can protect yourself against the changes in downstream system API contract. I prefer to not confirm to the downstream service API contract.
- You might want to expose different request and response formats. You don’t want to abide by the contract of downstream service in your pass-through service. You can ask a few fields if they can be derived, you can follow your naming convention, you can only share fields that are required, etc. Your pass-through service can provide an improved and simplified contract that matches your domain model and ubiquitous language.
- You might want to add logging and auditing that is not present in downstream service
- You can publish a well documented contract that other consumers can use. Many time I have experienced that downstream services lack any form of contract. It all boils down to guess work. It is you chance to do a good job.
You don’t want all the calling services to implement these concerns. A pass-through service can encapsulate logic in one place. In my view, pass-through services act as an Anti-corruption layer. Anti-corruption layer. prevents a downstream system/service domain model from polluting the domain model of a new service. Anti-corruption layer is a concept from Domain-driven design.
Reason 2: Buys you an option for future refactoring or rewrite.
The second reason that I think it is ok to write a pass-through service is that it gives you freedom to change the implementation in future.When all the new systems/services start using your new pass-through service you can easily replace the underlying implementation. You can replace a third-party provider with a new provider or you might move the legacy downstream service logic to your new pass-through service. This is the recommended approach when you want to refactor an existing monolithic system in a piecemeal manner. You can write pass-through service in the tech stack that is suitable for it.
Reason 3: PAGNIs.
A couple of months back I read an article by Simon Willison on PAGNIs. PAGNIs stands for Probably Are Gonna Need Its. It is the opposite of YAGNI – You Ain’t Gonna Need It. YAGNI is a statement that some capability we presume our software needs in the future should not be built now because “you aren’t gonna need it”.
PAGNIs are YAGNI exceptions. Examples include logging, api versioning, pagination, timestamps columns, etc. You can read them in Simon Willison’s post.
In my view pass-through services are also YAGNI exceptions. A pass-through service will not remain pass-through in future as well. So, having a centralized service will save a lot of time in future.
There are no right or wrong answers in design. It is mostly about patterns and experience with design trade-offs. In my experience pass-through service pattern can give high return if used for right downstream services.