adnan_e
adnan_e

Reputation: 1799

Should/Can an utility microservice be merged with all dependants if they cannot function without the utility microservice?

TL;DR: If you're analyzing any given microservices architecture, and you're merging services A and B together to avoid A not working because B broke (because it's heavily dependent on B, and per project spec A cannot complete the request if B is faulty), given enough iterations aren't you just going to end up with a monolithic architecture?


This is a question that was raised in our company recently and I'm afraid they are making a short-sighted decision.

Architecture summary:
- This is our first system using the microservice architecture
- We have an N amount of microservices (specifically, each microservice is a JAR running in a kubernetes pod in a cluster)
- Each JAR exposes a single service (e.g. auth, users, images ...) via an HTTP API
- 99% of our services require user authorization, but no authentication (that is done on another layer, our service assumes that if a request reached it, the request header contains a valid user token
- To translate a user token into something tangible (e.g. user info), we make a request to the users service
- Currently, each service does that to verify whether the user meets some service-specific criteria.

Now, on a meeting several senior developers have raised their concerns that if users fail for some reason (not necessarily something software-related, let's just assume that the machines allocated for these services are faulty), everything will stop working, making this (in their opinion) a quasi-monolithic architecture with extra steps. Their suggestion to avoid this is to convert the users service into a static library and add as dependency to all services that require it, so each service contains this functionality in itself rather than depending on a remote API.

But isn't this more of a specification issue rather than an architectural issue? It's not like the images API (technically speaking) cannot function without users, it's just that by the current project spec it's unspecified what it's supposed to do in that case, thus it simply fails as if the service itself is faulty.

The reason why I personally believe that this is a short-sighted decision is because one could argue that any kind of communication between the microservices can be deemed as such risk. This case is simply a very obvious potential point of failure, and the first iteration of the "what can break" analysis. With enough thought put into it, any endpoint that is used by many services can be treated the same way if there is no "plan B" specified (e.g. in case an image cannot be fetched from the image service use a predefined placeholder image)

Upvotes: 1

Views: 677

Answers (3)

Saptarshi Basu
Saptarshi Basu

Reputation: 9293

With zero understanding of the business use case in question I am inclined to agree with you. I feel that the problem in question can be mitigated in the following way:

  1. Try to reduce the load on the service by caching the response in a centralized caching system accessible to the individual services. Keep the cache updated using event sourcing and change data capture (preferrably log based)

  2. Wherever appropriate explore the possibility of introducing materialized views in the individual service DB with materialized view and event sourcing and change data capture (preferrably log based)

  3. Explore the possibility of using a fallback response with circuit breaker. Note that if a free user wants to watch a premium video when the user service is down, Business might want to allow the free user watch the video rather than restricting a premium user.

Upvotes: 1

Dmytro Mukalov
Dmytro Mukalov

Reputation: 1994

This is quite typical for an architectural decision when there is some trade-off which may potentially improve some quality attribute(s) by costs of another quality attribute(s). In your case the decision to convert users service to a library is going to improve reliability but the question is what quality attributes can be affected by that. There is not lot of information about this service but let's speculate. One of the good properties of micro-services style is ability to sustain isolated development lifecycle for services. That is being a microservice the users component can be delivered independently from other components unless there are some breaking changes in the public contracts. For example the internal fixes or improvements can be delivered with new version of the service and should become available for other services without touching other services release lifecycle. With a library the situation becomes bit different - in order to release some critical (or not critical fix in the library) you'll have to update all components dependent on this library. Even considering that this process is automated it can impose some additional operational issues related to the questions how often this may occur, should this process ensure zero downtime, how to handle failure scenarios when some service update is failed when some services are already updated. Another aspect is scalability - being a service the users component can be scaled independently with regards of typical load generated by all of its consumers. With library it's presumed that it should share the resources with other components and what's worse it should be able to handle load equal to load of a component the library is used by without possibility to configure resources consumption independently. Security also can be an aspect which requires additional attention presuming that users service should use some data source which may store sensitive information with library all other service should be permitted to access this data source potentially compromising the data security. From other hand reliability can be achieved by redundancy of users service sub-components for example by having multiple instances of the service and its data source.

Getting back to the initial statement you have kind of trade-off and decision depends on what quality attributes are more critical for the entire system. If it's difficult to achieve high users service availability due to some limitation or high costs of this process and it's relatively rare scenario when this functionality can be update independently it's worth to consider migration to library or if affected by this process quality attributes are crucial for the system architecture this can indicate that it's worth to pay more attention for tactics of improving users service availability instead of compromising quality attributes achieved by microservices style.

As a side note about specification vs architecture question. I presume by specification you meant requirements. An architecture is always driven by requirements and they are concern of architecture thus it's architectural issue anyway.

Upvotes: 0

Alex Kurkin
Alex Kurkin

Reputation: 1069

I have 2 comments on your topic:

  • Re: specification. You define an "alternative" behaviour for situation when a particular thing fails.

    Example: images service requests author information from users service to display information about who uploaded the image.

    My understanding is that you have chosen to fail images request if images service is unable to get author information. You could've chosen to display author name as "N/A" or simply do not display any user information along with the image if user service is unavailable.

    You define the alternative path. Now, of course there are pros and cons for each way and really depends on a scenario.

  • Re: architecture.

    IMHO, I think one of the benefits of Microservices architecture is isolation of individual services. I don't know your current architecture but in traditional Microservices I'd expect user service to have isolated storage and only expose HTTP (or RPC, etc) interface for other services to get that information.

    If you start using it as a library you'll fall down the path where as soon as you change your "user service" library you'll have to be upgrading it across all of the services, otherwise schema or business logic differences will play out over time. In the worst case creating sequential dependency for upgrade...

    I think you just have to take it that your user service is a critical part of your architecture and really put effort into making it highly-available, i.e. ensure it's deployed at least on a few different hosts (or even regions if you support it)

Upvotes: 1

Related Questions