Overview
The starter kit follows an opinionated structure designed for maintainability and scalability. Everything flows from .projenrc.ts, which generates configuration files, scripts, and workflows.
Directory layout
.
├── .github/workflows/ # Generated CI/CD pipelines
├── .projenrc.ts # Single source of truth for project config
├── src/
│ ├── aspects/ # Governance aspects applied across stacks
│ ├── assets/ # Lambda/ECS sample source packaged with stacks
│ ├── bin/ # Projen helpers for tasks and workflows
│ ├── constructs/ # Reusable L2/L3 patterns built on BaseConstruct
│ ├── main.ts # CDK app entry point and stack instantiation
│ └── stacks/ # Deployable stacks (Foundation, Starter, etc.)
├── test/ # Jest unit tests and snapshots
├── package.json # npm scripts (delegating to Projen tasks)
└── README.md
Key directories
| Directory | Purpose |
|---|---|
src/main.ts | CDK app entry point. Wires environment variables and instantiates stacks with environment-aware names. |
src/stacks/ | Deployable stacks. foundation-stack.ts provisions GitHub OIDC; starter-stack.ts is your starting point. |
src/constructs/ | Reusable building blocks extending BaseConstruct. |
src/bin/ | Helper utilities (env-helper.ts, cicd-helper.ts) consumed by .projenrc.ts. See Helpers reference. |
src/aspects/ | Guardrails applied across stacks. See Aspects reference. |
src/assets/ | Sample Lambda and ECS code. Replace when shipping infrastructure + application together. |
Key concepts
Constructs vs. stacks
- Constructs - Encapsulate resources in reusable units under
src/constructs/. See our guide on CDK constructs. - Stacks - Compose constructs into deployable units under
src/stacks/. See our guide on CDK stacks.
Environment-aware naming
src/main.ts and createEnvResourceName suffix stack and resource IDs with the environment or branch name. This enables safe multi-account and multi-branch deployments.
Projen tasks everywhere
npm run <task> delegates to Projen-generated tasks. The same commands power local and CI workflows. See Projen reference.
For a deeper dive, see our guide on optimizing your AWS CDK project structure.
Add a new stack
- Create the file in
src/stacks/:
// src/stacks/app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class AppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Add resources here
}
}
- Export from
src/stacks/index.ts:
export { AppStack } from './app-stack';
- Instantiate in
src/main.ts:
import { AppStack } from './stacks';
new AppStack(app, createEnvResourceName('AppStack'), { env: awsAccountConfig });
- Validate:
npm run test:synth
Add a reusable construct
Place constructs under src/constructs/ and extend BaseConstruct for environment, account, region, and branch helpers.
// src/constructs/storage-bucket.ts
import { Bucket, BucketProps } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import { BaseConstruct } from './base-construct';
export class StorageBucket extends BaseConstruct {
public readonly bucket: Bucket;
constructor(scope: Construct, id: string, props?: BucketProps) {
super(scope, id);
this.bucket = new Bucket(this, 'Bucket', { versioned: true, ...props });
}
}
Use in stacks:
import { StorageBucket } from '../constructs';
new StorageBucket(this, 'AssetsBucket');
Important: Run
npx projenafter updating.projenrc.tsso new directories or tasks are tracked.
Next steps
- Environments - Configure your account/region matrix
- CI/CD Workflows - Understand deployment automation
- Constructs reference - Deep dive into BaseConstruct