16

At my work we have a typical microservice architecture, but one of the issues we are running into is sharing data across multiple services. We have certain data that is used by multiple services and so we have to share the data across services. Our databases are separated out by service, for reference.

To give an example, we have a user service, communication service, and reward service. The user service stores all the user data including language preference and reward tier. The communication service and reward service need that data. The data is stored in the user service because it's fundamentally a property of the user model. This causes us to have to access the data outside of the microservice it's stored in. Even if it were stored in the appropriate service, running a GET to the user model would need to return this data and so we'd run into the same problem.

Right now the data is shared by making GET queries to the services for that data. However, this seems really naive, and it was largely done as an "easiest solution" answer.

On top of this, we have certain requirements on some of the data that require us to maintain consistency. We have a legal obligation to communicate to users in their preferred language. Similarly, rewards being applied from the wrong tier sounds like a problem (though perhaps that's not a critical problem).

How do we properly handle sharing this data?

One thought process is to share a read connection to the database. Reads scale very well and only the user service needs to update the data. It would be faster than running a GET between services that's for sure. I'm not overly sure I'm a fan of sharing database connections, but perhaps just for reads it's really not that bad.

Another is to make a shared service that stores data and publish to it, but having this data incur propagation delay seems problematic. We have obligations to have some data in sync, and any problems with the propagation can be huge issues. It's possible that if our data is only out of sync for a few seconds then it's likely not a big deal. Like if someone happens to get the wrong reward because of a timing issue it may not be that bad, or the communication is in the wrong language if it's basically right as it was sent out. I'm not sure if there are any that are critical that it absolutely has to be shared.

A third is to use a cache service that's shared. We have redis for our caching so we could potentially store data in there and only query services on cache misses. This causes us to have significantly faster lookup for data we're querying multiple times, but I don't know if we need that much data in our cache, and if we don't store enough we may miss too often.

Another is to store the data in the microservice where said data is critical. The problem with this is that the truth store is implied to be the user model. So if the data gets updated by an API call, it would update the microservice but then GETs to the data would temporarily return outdated data.

I'm sure this isn't a new problem but we're not sure about the best way to approach this. I'm fairly certain our current solution isn't ideal and won't scale, but I'm not sure which approach is the best.

13
  • 1
    Sounds to me like there is another service sitting in there waiting to break out. Either each piece of that shared data is different (its a perspective) in which case its not shared, Or each piece of data is identical and copied to each service verbatim in which case listen to the data and break it out. Take a hard look at why it is fundamentally a member of each service and see how it is used. If you could redraw the boundaries between services would it naturally arise? You might surprise yourself.
    – Kain0_0
    Commented Feb 5, 2019 at 23:28
  • 1
    The data belongs to a user, it's a property of them. It makes sense for the get and set for that data to be hitting the user model. A user has a reward tier, that just naturally makes sense. The data is used for calculating rewards by the reward service. The data is definitely identical. You can't join them together without creating a monolithic service. Making more services is just option 2. If querying the data has to hit a middle service then it's no better than what we already have. Commented Feb 6, 2019 at 16:24
  • 1
    Does the User Service actually do anything with the data. Besides providing CRUD operations? If it does not actually do anything, why could that data not be referred too? Even if it does do something, are those actions almost identical/similar to the rewards service? Also natural sense, who's natural sense? Yours? How did you get this natural sense? Take a step back, look at the data and behaviours of your system. This duplication is telling you that the current services are wrong and do not fit. So treat it like a monolith, then figure out how to decompose it back into good services.
    – Kain0_0
    Commented Feb 6, 2019 at 22:28
  • 1
    No, it doesn't do anything other than CRUD. The issue comes from a concern of how CRUD accesses that data. Let's suppose for a minute that I put it entirely in the rewards service, how do I access it? By hitting /rewards/<user_id>/something? Does that make more sense than doing /users/<user_id>/update and giving it a new reward tier? From the perspective of an API, you end up with too many endpoints doing far too little if you take this approach. Commented Feb 7, 2019 at 13:55
  • 2
    Fair enough, but again you are mixing up how data is consumed, with how it is stored and processed. Unfortunately the lingo for both is service, but they are by no means the same thing. A well designed micro-service contains a single cohesive set of data and behaviour, it is identical in every sense of the word to a module. It alone exposes that data, and that data is not replicated elsewhere (except via caching). If you need to present data from several services in a single call you effectively have a presentation problem, essentially making the published API a separate aggregating service.
    – Kain0_0
    Commented Feb 12, 2019 at 2:04

2 Answers 2

10

One of the solutions i've seen is to move shared data to eventsourced aggregates and make it publicly availible to any service that need this data. You may use Kafka or Eventstore to store them. In case of Kafka eventsourced topics are used by another microservices through streams API for example in form of tables with most actual state and new events that change state also come naturally as stream messages to any client. This means no direct queries to another services, no data duplication and data updates become reactive in some sense.

But this pattern is unfamiliar and foreign to many programmers, Kafka is a heavy service and brings ops burden and cognitive load to programmers, so, as an alternative i like the idea of shared database. It just demands strict limitations as - single writer principle. Other microservices may query all the tables but can only write to tables that belongs to them. Database permissions may help to enforce access rules.

I'm myself fairly new to microservices world so please take my words with a ton of salt. :)

0

It sounds like you may be running into this issue because your services are actually too small. As you mentioned, the user service stores all the user data including language preference and reward tier. This indicates to me that communication preference and rewards are child entities of your user context, and therefore the functionality contained in those services should be moved to your user service. In DDD terminology, the user is your aggregate root, and communication preferences and rewards are value objects in the sense that they don't make sense without the associated user. Therefore, it makes sense to have one user service which handles operations against communication preferences and rewards.

2
  • The advice in this answer is overall good; but maybe not quite hitting the mark for this question. The user's language is clearly something that is intended to propagate all over the microservices, and cannot just be pulled into a single service without recreating the monolith (in practice or in spirit).
    – Flater
    Commented Mar 16, 2022 at 13:54
  • @Flater that's fair - I was approaching the question from the perspective that a microservice should maintain its own state. In the context that monoliths are considered an anti-pattern, nanoservices should be considered one too. Commented Mar 16, 2022 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.