Granularity of microservices and containers

Let’s imagine I have a single web app containing features which are topically related, but have highly differing implementation requirements.

Let’s imagine that this web app is about fruit, and contains the following features:

  • A fruit encyclopedia giving details about various kinds of fruit, including full 3D models that can be inspected.
  • A game where you can throw fruit at a wall and watch it smush, again using 3D models but also full physics.
  • A marketplace for purchasing different kinds of fruit.

This seems like a prime opportunity for a microservices-based architecture. As microservices are meant to be separated into bounded contexts each of which implements a cohesive set of functionality, it makes sense to have one microservice for each of the three above features, providing its backend. A fourth microservice is then added to provide the actual web app UI frontend, to be filled in with data from the other three services. Finally, we want some sort of API gateway / load balancer which handles all client requests and redirects them to the appropriate service.

This will look something like the following: Four services plus gateway diagram

However, these services aren’t as easy to separate as it first appears. I see two main issues:

Issue 1: code reuse

Both the encyclopedia and the game require 3D models of fruit, although the encyclopedia wants to add wider information, and the game wants to add physics simulation. According to traditional DRY, one would factor out the duplicated functionality as a shared library. However, in microservices this can lead to tight coupling as changing the shared library affects both services.

I can think of three solutions:

  1. Ignore DRY and just implement the same functionality in both. This keeps them nicely separated, but causes duplication of work if e.g. a bugfix in the common functionality needs to be applied to both.
  2. Embrace DRY and use a shared library. If a change is needed, upgrade the version used by each of the services separately as necessary. Accept that the services may end up running different versions of the same library, or you’ll often be making changes to both together.
  3. Embrace microservices and create yet another one to serve 3D models of fruit. If the implementation is hidden behind a generic API, then implementation changes and bugfixes shouldn’t affect either service using it, as the API contract is still fulfilled. However, depending on the technologies in use, generalising the 3D models in this way may not be feasible or performant, leading to tight coupling and effectively devolving into a slower and less flexible option (2).

Under what situations would each of these methods be appropriate? Is there another method I have not thought of?

Issue 2: containers

Containers are an obvious and powerful tool for implementing microservices. However, they are not synonymous with microservices, and as far as I’ve been able to determine, the relationship between the two is hazy at best. From my research, best practices state both that one container should implement one microservice, but also that one container should only house a single process or responsibility.

A single microservice likely still contains several components; for example, the encyclopedia and marketplace likely both want some sort of database as well as their business logic.

If the logic and the database are placed in separate containers, then there is no longer a 1:1 mapping between containers and microservices. Proliferation of containers also leads to lots of inter-container communication, which slows things down. Containers cannot necessarily be guaranteed to be co-located, so requests between must be encrypted in case they pass over the internet (I think, correct me if I’m wrong). The formation of requests, translation between different APIs, encrpytion, and the transmission time itself all add overhead.

If the logic and database are placed in the same container, then there is no longer a 1:1 mapping between containers and processes. This makes it harder to scale them independently, in case the logic is very simple but requires enormous data storage, or vice versa. One must also build and deploy them together.

How should microservices be divided into containers under this scenario? Are there genuinely good alternatives to containers?

Microservices – Are retries enough?

I have been asked to look at moving our current architecture to microservices. I am aware of the warning to always assume a request could fail.

So I am aware we should always be prepared to retry the request. However, when designing this, I am also assuming that the retry can fail.

So with that in mind we have been looking at a pattern where either all the processing in committed or it always rollback. This is achieved via message Outbox (and Inbox) Outbox pattern. The services stored the functional changes in their database, then within the same transaction stores the event messages in their database in an Outbox. A separate dispatcher service then dispatches the messages from the Outbox and sends it to a messaging system. It is detailed in this series of articles Life Beyond Distributed Transactions: An Apostate’s Implementation

To me this is the safest option because if the dispatcher fails to send the message, it is available for a retry.

However one of my colleagues thinks that although we need to retry, the solution will be resilient enough that the message will always be successfully sent to the messaging system. E.g. the issue that causes the need for the retry will always be transient, and will be cleared by in time for one of our retries to succeed.

I’m looking for a opinions on whether I’m being over cautious and retries should be enough. Therefore I do not need the dispatcher or the outbox pattern.

I guess the main problem is not that a service I am calling cannot be reached, but the server my service is running on shuts down.

How to keep authorization model maintainable on calls between different microservices?

In our environment (as in many others), it is often the case that one microservice must call another in order to accomplish a task.

In our environment, authentication is clear enough – we have a signed JWT containing a list of permissions and roles, as well as a user ID, client ID, and so on.

What we’re less clear on is authorization – ensuring that the authenticated client can (or can’t) do the right stuff, but that the underlying services have all the access they need to do their jobs (even if the client wouldn’t be able to do the same things directly).

We’ve examined a few different options:

  1. Each service does all of its own authorization, and if a privilege escalation is needed, it generates a “God mode” token with an otherwise unchanged payload and a different keypair and makes the call using that. The main concern here is copy/pasted authorization code, and the fact that there’ll be a strong incentive to just always enable God mode when making cross-service calls (which makes it somewhat redundant).
  2. Each service does all of its own authorization, and just forwards the user’s token if it needs to make a call. The concern here is code duplication like in option 1, and also the fact that this is likely to cause a complex interdependent web of permissions that imply other permissions that imply other permissions that… (ad nauseam), creating a maintenance headache as the number of services grows.
  3. A lightweight API gateway service that does “simple” authorization (nothing more advanced than “is this client allowed to use this endpoint at all?”), attaches a user object to the payload, and leaves more specific behaviours to the underlying services, which accept any call as being authorized out of the gate. The major concern with this option is performance and stability – the API gateway service creates a single point of failure that can make the entire system inaccessible if it malfunctions, plus creating a frequently-changing dependency for every service.

The question here is twofold:

  1. Are there any additional pitfalls to the three patterns described above that we haven’t considered?
  2. Which of these is the most common in the wild?

Note that this question is not about service mesh offerings like Istio, as we consider them to be somewhat orthogonal to this issue.

Cache maintained by caller app or by provider app in microservices inter-app communication?

Suppose there are app1 and app2 in a microservices. app2 needs to call app1‘s RESTful APIs. Cache is needed since the call will be frequent. So after a successful RESTful call, there will be cache available for app2.

My question is who maintains the cache, app1 or app2? Any comparison between the two? Is there industry best practices?

In addition, are there any differences if there’s a app3 also needs to call the app1‘s APIs?

DDD and Infrastructure micro-Services – how should the interface be designed?

We’ve extracted our email sending into an EmailService – this is a microservice that provides resiliency and abstracts the email logic into an Infrastructure service. The question is how the interface to the EmailService should be defined, with respect to the information it requires about the [User] domain

Approach 1:
EmailService exposes an interface that takes all the fields of the [User] domain that it requires.

Approach 2:
The EmailService interface takes only the UserID. The EmailService then queries the UserService using this ID to fetch the fields that it requires

There are some obvious pros/cons with each approach.
Approach1 requires the calling service to know everything about a User that the EmailService requires, even if its not part of the callers Domain representation of a User. On the other hand the contract between the services is explicit and clear.

Approach2 ensures that the [User] fields are fetched as late as possible, minimising consistency problems. However this creates an implicit contract with the UserService (with its own problems)

I’ve been doing a lot of research here and on SO, but I haven’t been able to find anything around this specific interface design problem. Keeping DDD principles in mind, which approach is correct?

What can I do to prevent or reduce message loss in a microservices system?

Quite often I have methods that do the following:

  1. Process some data
  2. (frequent, but optional) Save some state to database
  3. Publish a message to a queue / topic

What options do I have to protect myself against transient errors (but not only transient) with #3? Implementing a retry / repeat mechanism is one approach, but it probably won’t work if the issue that prevents the message from being sent lasts longer than a few seconds or a few minutes.

Micro-services architecture for Data Ingestion/Transformation pipeline project

I am working on designing a brand new Data Ingestion Pipeline with the Key highlights of the new project are as follows:

  • Download and Update data to/from SharePoint using SharePoint APIs
  • Download and Update data to/from JIRA/incident management application using JIRA APIs
  • Download and Update data to/from SQL sources using provided APIs
  • Download and Update data to/from external custom applications using APIs

I am considering the micro-services architecture for the above project where I will be looking at creating 4 separate services for each of the above purpose.

And finally, a batch processing client that would execute all these API services using C# .NET

But I have been wondering if implementing micro-services architecture will be overkill, and rather all I really need is a single client calling all these APIs directly without having to create above individual services.

And additionally, regarding setting up the project in Visual Studio, should all these services be in their own separate solutions or rather be a part of one .NET solution with multiple projects in it.

Comparison between central logic or distributed logic in microservices?

I’m designing an application in a microservices architecture. The app has its own logic but also needs to collaborate with other services.

For example, the app is A, and other apps are B and C. Whenever there’s an event “X” happens in A, The B and C also need to do something like sth-B(), sth-C().

There are two ways that I can think of:

  1. Keep the logic in A. So A will know all the things to do when event “X” happens. It will do sth-A(), then call restful sth-B() and sth-C(). B and C are simply passive.
  2. Distribute logic to B and C. So A will only do sth-A() and publish the event "X" into a message queue. B and C will subscribe the event and do sth-B() and do sth-C() respectively. In this way, A doesn’t need to care about what subscribers do, but it’s hard to know what in the whole system are really being done when the event happens.

My questions are 1) are the above two design ways common? Do they have formal names? 2) Which one should I choose in a microservices architecture?

How to query data from multiple microservices

I have micro service architecture application. One of the services contains all user related information. Another service contains a set of business objects. The Angular UI calls rest APIs of user-service and business-service to perform operations like user management, business management. The tables in the business-service contain user ID from user-service for example owner of a business object. Since they are different databases, there is no referential constraint.

I want to display a list of business objects in UI. I want to be able to filter the results by the owner’s first name. Note that the business-service does not know user’s details except user ID.

What is the best way to query data belonging to multiple services ?

Some options in mind

  • store redundant data in business service – but this could go out of sync. May have to run a cron job to keep it in sync with user-service. But the solution is ugly.
  • First, get list of user ID’s matching the user’s first name. Pass those IDs to the business service to get the list of business objects there by achieving filtering.

Both solutions don’t seem to be clean…

Command Waiting On Event Sourcing Micro-services

I’m very new to micro-services but am trying to learn, so apologies for any ignorance or incorrect information.

I have been looking at event-sourcing architecture for microservices and I have a question when it comes to making a command/post requests and having to wait for their response.

Take this example: I want to create an order but in order to create the order it must be checked to see if the customer has enough money to place it. So I make a post request. This might be straight to the order microservice or might be routed or handled via an api gateway.

From an event sourcing architecture this is my understanding on how it might be created.

  1. Order service would receive order information via a post method and add the order in a pending state.
  2. The order service would publish a order created event.
  3. The pricing service has subscribed to the event. So it will check how much that user/customer has. 3a. If the user has enough money, it will reserve the amount. It will then publish a credit reserved event. 3b. If the user doesnt have enough money it will publish credit exceeded event
  4. The order service will have subscribed to both of these events and will either put the order into placed or deny it. (I dont know exactly what it would do if it is denied but it will not be allowed)

enter image description here (Image is just for reference, this doesnt not imply they world talk directly to each other)

Now a few questions come off of the back of this which I hope someone can please help me.

  1. The user is going to call this to place an order. They are going to want a response when they place this to know if it was placed or not. How do you handle waiting for command operations that you need a response to, and that response relies on waiting on other microservices potentially. If my api is to wait for a change in the order, how is an event picked up via an endpoint?

  2. How does cross microservice validation work. For instance, say before I create the order I want to know if the user it is for actually exists. is this handled by the api gateway which will do initial validation based on the user info?

I understand there is a idea of anonymous microservices (instead of authoritity where each service owns a data set). For instance, the order service would have a copy of all the users and their wallet, so it can check its copy. (same with the users to check it exists). is this the way to go.

Is any of this even correct. Any help would be much appriciated.