Dodging the Lambda Pinball with DDD

Haiko van der Schaaf
4 min readMay 16, 2023

An approach on how to design your Lambda’s

Photo by Heather McKean on Unsplash

The latest Thoughtworks radar of April 2023 warns of using serverless technology for it increases the complexity and risks ending up with a Lambda pinball architecture.

The problem originates from the fact that when execution and data flows across multiple interdependent Lambdas, at a code level, simple mapping between domain concepts and the multiple Lambdas involved is practically impossible, making any changes and additions challenging. In short, when you don’t properly design your lambda’s you end up with a distributed spaghetti-code jungle, the previously used GOTOs are here replaced with events between the interdependent Lambdas.

Spaghetti Code’ is a phrase, an antipattern for unstructured, difficult-to-maintain code that is every developer’s nightmare. The word ‘spaghetti’ here refers to the flow of the program being tangled like ‘spaghetti’. It is also called ‘code that is difficult to follow by a human’.

As the reference indicates, this is not a new problem. The term already appears in the 1970s. However, the impact is more significant now as we are exposing public interfaces to other services. Luckily, this problem is already solved for us. We only have to know how to apply this to Lambda architecture.

What should we put in a Lambda function?

There are two questions here. How much code fits in a Lambda and which code should go into the Lambda?

Let’s start with how much code fits in a Lambda. Traditionally, you would build an application that contains all the functionality. The application would contain multiple services (aka modules, packages, etc). Each service would contain one or multiple functions (aka methods). You would run the application on hardware, on a VM, or in a Docker container.

application, service, and function relation

A lambda has its limitations regarding deployment size. AWS Lambda size limit is 50 MB when you upload the code directly. With Lambda layers you can have a maximum of 250MB for your package.

With these limitations in mind, we should scope what should fit into a Lambda to either a service or a function.

Which code should go into a Lambda?

Now we are arriving at the most important question to avoid the Lambda Pinball, how to decide which code should go into a Lambda?

The goal here is to organize code in such a way that changing the code somewhere will make the least ripple effect on other parts of your system. To do this you should guide your efforts in organizing your code by the ‘Single Responsibility Principle’.

The single-responsibility principle (SRP) is a computer programming principle that states that “A module should be responsible to one, and only one, actor.” It was coined by Robert C. Martin aka Uncle Bob.

Martin defines a responsibility as a reason to change, and concludes that a class or module should have one, and only one, reason to be changed (e.g. rewritten).

Domain Driven Design

A lambda function therefore should have a single responsibility. But at what granular level should we apply this? An effective way to organize responsibilities is to look at the concepts of a business domain as used in ‘Domain Driven Design’ (DDD)

Domain-driven design (DDD) is a major software design approach, focusing on modeling software to match a domain according to input from that domain’s experts.

DDD aims to group concepts and functionality into a single (business) domain. An e-commerce application could have for example an Inventory domain, Invoice domain, and Shipping domain.

e-commerce domains

These domains are too big for a single function. Therefore for using Lambda, we should match the concept of a Service with a Domain. The Service should contain all functions related to the particular domain.

Domains, services, and Lambda’s

Organizing code in this way will decrease dependence between Lambda’s and makes it easy to map domain concepts. Changing code for a single business domain will be easier and maintainability is increased. This will avoid tight coupling and cut down on unnecessary inter-lambda communication using events.

Conclusion

A naive approach to putting code into Lambda will result in a distributed spaghetti code jungle making any changes and additions challenging.

Using the ‘Single Responsibility Principle’ with Domain Driven Design concepts your code you can organize your code across Lambda functions in a maintainable way. Following this recipe to design your system allows you to experience pinball at the right venue, the arcade hall 🕹️👾.

Have fun coding!

Haiko van der Schaaf

Related stories

--

--