Optimize your AWS CDK Project Structure for Growth

Write, schedule, and publish great Twitter tweets & threads. Grow your following on Twitter with Typefully!

As your AWS Cloud Development Kit (CDK) applications grow, it becomes increasingly important to maintain a well-organized project structure. A modular and maintainable project structure not only simplifies management but also enables easy scaling.

In this blog post, we’ll walk you through an optimized AWS CDK project structure and explain the features and decisions behind organizing it in this manner.

Organizing your AWS CDK Project structure for maintainability and scalability

When working on smaller projects using infrastructure as code, where you deploy single applications that don’t demand extensive maintenance or collaboration from multiple teams, it’s recommended to structure your AWS CDK project in a way that enables you to deploy both the application and infrastructure using a single stack.

However, if your use case involves multiple microservices and numerous stateful resources, such as databases within a single AWS CDK application, it’s wise to organize your AWS CDK project to ensure easy extensibility and seamless deployment of the components you’ve built.

Here’s a look at how you can structure your AWS CDK project code for optimal maintainability and scalability.

├── assets
│   ├── ecs
│   │   ├── service1
│   │   └── service2
│   └── lambda
│       ├── function1
│       └── function2
├── bin
│   ├── nameStacks.ts
│   └── README.md
├── lib
│   ├── aspects
│   │   ├── index.ts
│   │   ├── README.md
│   │   ├── permissionBoundaryAspect.ts
│   │   ├── s3Aspect.ts
│   │   └── vpcAspect.ts
│   ├── constructs
│   │   ├── index.ts
│   │   └── ecsCronTasks.ts
│   │   └── ecsALBTasks.ts
│   ├── base
│   │   ├── index.ts
│   │   └── vpcStack.ts
│   ├── database
│   │   ├── index.ts
│   │   └── rdsStack.ts
│   ├── storage
│   │   ├── index.ts
│   │   ├── s3Stack.ts
│   ├── compute
│   │   ├── index.ts
│   │   ├── lambdaStack.ts
│   │   └── ecsStack.ts
│   └── api
│       └── index.ts
└── main.ts

We’ll go over this structure to explain the decisions behind the chosen setup:

Assets folder

We’ve organized the assets by creating subdirectories for specific resources within ecs and lambda. This approach simplifies managing different component assets, making it easier to find and update assets as needed.

Bin folder

The bin folder is important for managing utility scripts and executable files that extend or modify the functionality of your AWS CDK application, without containing the actual AWS resources themselves.

Aspects folder

The aspects folder in an AWS CDK project is used to store custom aspect classes that are designed to extend or modify the behavior of your CDK constructs. Aspects in the CDK provide a powerful mechanism to perform cross-cutting operations on constructs within your application.

The aspects are part of the application library, hence the decision to move the files in the lib folder.

Constructs folder

The constructs folder in an AWS CDK project is designed to store reusable, custom constructs that represent combinations of one or more AWS resources, encapsulating specific functionality or patterns.

These constructs can then be easily imported and used across different stacks in your CDK application.

Lifecycle-based grouping in the lib folder

We used descriptive directory names, such as base, database, storage, compute, and api, which correspond to different stack components and group AWS resources based on their lifecycles.

This practice helps you to quickly identify the purpose of each directory and locate the relevant components within the project.

Additionally, it facilitates better management of resources in IaC projects, as resources with similar lifecycles are more likely to have related dependencies and require similar maintenance tasks.

However, there are two ways to bundle resources in a single stack.

  1. Option 1: Separate stateless resources from stateful resources
  2. Option 2: Combine stateless resources with stateful resources

The structure shown in this blog post is using option 1 as the foundation. You can decide to combine both options as long as it’s clear to the team who develops and maintains the AWS CDK application how the distinction is being made per stack (this can be done by modifying the names of the subdirectories in the lib folder).

We’ll explain the pros and cons of each option, so you can best decide how you plan to manage the resources in the AWS CDK Project.

Option 1: Separate stateless resources from stateful resources

Combining stateful and stateless resources in a single stack can lead to challenges when it comes to maintainability as your AWS CDK project grows over time.

For example, you might have a stateful resource such as an S3 bucket or RDS instance that serves multiple stateless consumers like ECS containers or AWS Lambda functions.

So, here are some reasons why this can be problematic:

  • It’s harder to reason about the behavior and requirements of individual resources. Separating resources based on their stateful or stateless nature allows for better isolation and separation of concerns, making it easier to understand, test, and maintain your infrastructure.
  • If the dependencies between resources are not well managed, it could lead to cascading failures or difficulties in updating resources independently. Separating resources into different stacks based on their stateful or stateless nature simplifies dependencies and makes it easier to manage and update resources.

Therefore separating these resources in multiple stacks provide the following benefits:

  1. Independent deployments: By separating stateful and stateless resources, you can manage and deploy them independently. This allows for greater flexibility in updating or scaling stateless resources without affecting stateful resources, minimizing the risk of unintentionally impacting the stateful resources or the data they store.
  2. Easier rollback: If an update to stateless resource results in an error or issue, rolling back to a previous version can be simpler when the resources are in separate stacks. This separation minimizes the impact of the rollback on the stateful resources, reducing the risk of data loss or corruption.
  3. Improved fault tolerance: Separating stateful and stateless resources into different stacks can improve the fault tolerance of your application. If an issue arises with one stack, it is less likely to affect resources in the other stack, ensuring that the impact of the issue is isolated and easier to troubleshoot and resolve.
  4. Clearer dependencies: By organizing resources with different lifecycles and external dependencies in separate stacks, you create a clearer understanding of the relationships and dependencies between resources. This separation can make it easier to manage and maintain your infrastructure, particularly as your application grows and evolves.

Option 2: Combine stateless resources with stateful resources

There are certain scenarios where combining stateful and stateless resources in a single CloudFormation stack can be beneficial, especially when the resources share the same lifecycle and there are no dependencies outside the lifecycle of the objects.

For example, you want to deploy an ECS Fargate container or an EC2 instance with EFS storage mounted on it. These resources are tightly coupled and don’t have any dependencies outside their relationship.

So, here are some reasons to combine these resources in a single stack:

  1. Simplified management: Combining stateful and stateless resources with the same lifecycle in a single stack makes it easier to manage and monitor them. This can result in a more streamlined deployment process, as you only need to manage one stack instead of multiple ones.
  2. Clearer relationships: By grouping resources with the same lifecycle together, you create a clearer relationship between those resources. This can make it easier to understand the overall architecture of your application and how the resources interact with each other.
  3. Reduced complexity: When resources share the same lifecycle and have no external dependencies, combining them in a single stack can reduce the overall complexity of your infrastructure. Fewer stacks can be easier to manage, understand, and troubleshoot.


The updated AWS CDK project structure shown in this blog post is more organized, modular, and easier to navigate. It groups AWS resources based on lifecycles, which is essential when managing resources in Infrastructure as Code projects.

This approach helps maintain and scale your application more efficiently by providing a clear separation of concerns for different resources and components. By following these recommendations, you can better manage your AWS CDK applications and ensure long-term maintainability and scalability.

To prepare your infrastructure for expansion, it’s generally advisable to separate stateful and stateless resources into distinct CloudFormation stacks. This strategy helps reduce update risks, simplify resource dependencies, enhance deployment speed, and encourage improved isolation and scalability for your application.

Elevate Your AWS CDK App with Expert Review & Guidance

Unlock the full potential of your AWS CDK app with our Expert AWS CDK App Code Review Service, conveniently delivered through AWS IQ.

Gain invaluable insights, minimize risks, and set a clear path forward for your project’s success.

Danny Steenman

A Senior AWS Cloud Engineer with over 9 years of experience migrating workloads from on-premises to AWS Cloud.

I have helped companies of all sizes shape their cloud adoption strategies, optimizing operational efficiency, reducing costs, and improving organizational agility.

Connect with me today to discuss your cloud aspirations, and let’s work together to transform your business by leveraging the power of AWS Cloud.

Write better tweets.
Grow your audience.
Join 90,000+ creators to write, schedule & publish on Twitter, without distractions

Now with AI and LinkedIn cross-posting.

Sign up now!