I would like to have an API gateway per microservice and I wish to have the lamda-authorizer shared across all the API gateways.
I was reading here https://www.serverless.com/framework/docs/providers/aws/events/apigateway/#sharing-authorizer and it seems possible but I am not sure
- how to ensure that lambda authorizer gets deployed first so that we have the arn to use in all of the APIGateways.
- Also, If MicroserviceA calls MicroserviceB, How do we ensure that MicroserviceB is deployed first?
- Similarly, where/how should the “shared-services” in the project be structured/placed so that inter-service communication is easy.
I’m not an expert, but I’m facing the same problems and below are my possible solutions.
I identified that there are some common resources, used by different microservices (domain name, authorizer, …). I have a stack for these resources, deployed using serverless stack toolkit (SST): GitHub - serverless-stack/serverless-stack: Serverless Stack Toolkit allows you to deploy CDK and Serverless Framework apps together.
This stack is first deployed, and then the microservices.
I think that it is an anti-pattern to call a microservice synchronously from an other microservice. To avoid this I used two patterns. For asynchronous calls between microservices, I use SNS topics (created using SST) on which some events are published with their payloads, and other microservices subscribe to the interesting events. For example, it is useful when a data is deleted and other microservices have relations to this data (I notified them that the data has been deleted). For synchronous calls between microservices, I use an “orchestrator”. Some complex API methods require to communicate with several microservices. Then, the “orchestrator” is responsible to these complex calls and it orchestrates the communication between the microservices.
I’m not sure to understand correctly the question. We use a mono-repo approach for the service layer in order to permit reusability of code that is not service specific, but it is forbidden to communicate directly between services. The other advantage is that the orchestrator do not have to call deployed lambda, it use directly the code of the service, and it avoid too many cold starts.
I hope it helps you, and I’m interesting to any suggestion about improving this management.
@Patchoulin Thank you for your response.
- Why SST for the common resource. We can have nested stacks in the serverless framework for example (or even individual stacks that we can decide to deploy before microservices). Does that not do the same thing? Curious if I am missing out on something here.
I am considering using lerna instead and adding the downstream services as dev-dependencies
2. Could you share an example of the orchestrator, please? For example, we are just starting off and we have 3 microservices to register a tenant. Now one of these services calls two other services. I imagine this to be happening a lot for synchronous communication. I would love to have a better idea of your orchestrator that would facilitate this without making it an anti-pattern.
@Patchoulin Cognito pools will be created on-fly for each new customer.
That’s correct. Service A needs data from Service B let’s say. So I am guessing that
Request → Service A → Service Orchestraror → Service B
Does that look right? So every time service needs data from another service, there will an additional call to Orchestrator service which will further call the service we want the data from?
The orchestrator is used to orchestrate the communication between microservices, such as on the image below. I am interested about feedbacks on this approach.
@Patchoulin So the client will hit the Orchestrator microservice. I don’t like that very much.
For ex: If I have an endpoint “customerReg”. This endpoint needs to call “userReg” which owns users table, to get data from it.
I would still want my client to hit customer/customerReg and not the orchestrator/reg as in that case I will need to put in all of the logic in the orchestrator.
I would have thought that it would be:
What do you think? @Patchoulin
I would not do this, because in complex situation you could have something like that : Client —> CustomerReg —> OrchestratorReg —> UserReg —> OrchestratorReg —> OtherServiceReg —> …
This could imply spaghetti code, really difficult to debug/understand.
With the approach that I suggest, all microservice are called from the orchestrator and it simplifies the understanding of the process.
I understand what you don’t like in this approach, and I agree with you. But it is the solution that I found to keep the business complexity as low as possible and to avoid to search in all microservices wher to update the dependancies when an interface evolves.
Do you see the advantages ? What do you think about ?
I would not do this because in a complex situation you could have something like that: Client —> CustomerReg —> OrchestratorReg —> UserReg —> OrchestratorReg —> OtherServiceReg —> …
In that situation, I’d rather:
Client —> CustomerReg —> OrchestratorReg —> UserReg
Instead of UserReg calling OrchestratorReg further, I’d call them sequentially/parallelly from Orchestrator Reg itself and then return to CustomerReg.
This could keep the business complexity low as well while most of the logic will still lie in the respective services instead of Orchestrator service. What do you think?
@Patchoulin Bump. What do you about the above mentioned approach
Sincerely, I think I’m not enough skilled to have a really relevant opinion, so don’t take my opinion as a source of truth, but more as a debate about possible approaches.
In your flow there is a part that I don’t really understand : “UserReg → OtherServiceReg”.
What I suggested is to use an orchestrator to “orchestrate” the sequence of synchronous calls to microservices in order to restrict synchronous calls between microservices. The goal is to avoid strong coupling between microservices.
An other advantage is that the orchestrator can prevent microservice failure and react accordingly, because the flow is totally managed by the orchestrator and it is clearly visible. In your flow, if a microservice fail then the chain of calls is broken and it is difficult to determine how to manage failure because the links between microservices is opaque (one microservice doesn’t know how other microservice works and how they are linked).
What do you think about it ?