Search code examples
amazon-web-servicesaws-lambdaarchitecturemicroservices

AWS Serverless Microservice Communication


For a project I'm building multiple AWS Serverless microservices and I'm wondering about the best option for communication between different microservices.

In my project I have 2 different contexts/microservices: Orders and Returns. Both these microservices exists out of an ApiGateway + Cognito Authorizer and Lambda integrations to perform CRUD operations. When a return is requested I want to retrieve the original order for additional information from the Order microservice. The problem is that to request the information I have to provide Credentials to resolve the Authorizer.

I have been looking for a while for a solution in the form of Service accounts but it looks like that's not a feature with AWS Cognito. I also found a solution where, though the aws-sdk, you are able to call a lambda directly from within a lambda but that feels like a work around.

Hoping for some insights!


Solution

  • Option 1: lambda calling lambda

    You Invoke a lambda from another lambda architecture using boto3 (python) or the aws-sdk (javascript/other langs.)

    For that you will need to invoke lambda by using the ARN/Function name and setup IAM permissions.

    Pros:

    • Very simple to implement

    Cons:

    • Higher cost - Triggering 2 lambdas on each API call
    • Coupling - Creating a tight dependencies between lambdas
    • Difficult to debug, track, maintain (in case you end up with a lot of lambdas calling and depending on one another - that might be a hell to debug issues)

    Option 2: Common DAL (Data Access Layer)

    You can have the Returns API and the Orders API obviously in 2 different lambdas but they both can use a lambda-layer which will act as a common Data Access Layer for accessing (CRUD) the DB data (dynamo for instance). Both lambda deployments adds their route to API gateway and both can use the same authorizer.

    This way when invoking GET /my_service/returns/1234 - This will trigger returns lambda API which will refer to the DB via a common DB layer.

    Pros:

    • A LOT more Simplified code maintenance
    • Cost effective - 1 Lambda invocation (instead of 2)
    • Decoupling - Lambdas are decoupled (both using the same lambda layer for CRUD)
    • Common DB access layer is maintained in a separate deployable unit

    Cons:

    • Lambda will need to redeploy when common DB layer is updated

    Option 3 - Cognito Client credentials flow

    Within an separate internal Cognito user pool, You can create a cognito user pool app client that allows generating an client credentials access token for invoking the returns API. (AKA machine-to-machine tokens)

    But then you'll need to do a whole lot of additional configuration to have the design nice and healthy (follow good practices).

    You will need to:

    • Define a separate cognito user pool (for internal communications)
    • Define a cognito resource server that support relevant scopes.
    • Create a cognito app client that allows client credentials flow and is allowed to use the relevant scopes
    • Setup the authorizer within API gateway to use cognito as the authorizer
    • The returns lambda will need clientId and clientSecret in order to acquire access token and invoke the other lambda - you will need to store that secret somewhere (not in code) - so using AWS secret manager is recommended.

    Pros:

    • Separation - This architecture actually totally separates internal domains
    • Internally secured - effective if you have multiple logic domains refers to a certain API (and 1 internal path requires a certain authorization while another requires a different authorization)

    Cons:

    • Very complex - Not saying that this approach isn't healthy and good practice (actually we also use this approach in several use cases) BUT - this architecture is complex to define and deploy and fits only a big project with complex use cases
    • Cost - this approach requires the use of many AWS services Hence this I not the most cost effective approach (BUT again if this is for a big project and the use-cases requires such separation and security then that is the right approach)
    • Recommended to be familiar with OAuth2.0 standard / knowledge

    Another way (Only if an async operation is needed with no need to read response) - then you can use SNS/SQS to send an event and trigger other lambdas - but this is not your case.

    I would highly suggest to go with option 2 if the APIs are domain related (e.g. orders / returns / users etc..)