Layout overview
While specifics can vary based on .projenrc.ts, the starter kit ships with the structure below:
.
├── .github/workflows/ # Generated CI/CD pipelines
├── .projenrc.ts # Single source of truth for project config
├── docs/ # Docusaurus docs (this site)
├── 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
src/main.ts: bootstraps the CDK app, wires in environment variables, and instantiatesFoundationStack/StarterStackwith environment-aware names. This is the file you update when adding or reordering stacks.src/stacks/: holds deployable stacks.foundation-stack.tsprovisions GitHub OIDC;starter-stack.tsis where you begin adding resources. Export them throughsrc/stacks/index.ts.src/constructs/: reusable building blocks that extendBaseConstructfor consistent naming and context.src/bin/: helper utilities (env-helper.ts,cicd-helper.ts,git-helper.ts) consumed by.projenrc.tsto generate tasks and workflows. See Helpers reference.src/aspects/: guardrails applied across stacks (permissions boundaries, S3 policies, VPC CIDR checks). Documented in Aspects reference.src/assets/: sample Lambda and ECS code. Replace or expand this directory when shipping infrastructure + application code together.docs/: edit these MDX files to keep project documentation in sync with the repo.
Key concepts
- Constructs vs. stacks: Encapsulate resources in constructs under
src/constructsand compose them in stacks undersrc/stacks. - Environment-aware naming:
src/main.tsandcreateEnvResourceNamesuffix stack and resource IDs with the selected environment or branch for safe multi-account deployments. - Projen tasks everywhere:
npm run <task>delegates to Projen-generated tasks so the same commands power local and CI workflows.
Add a new stack
- Create a new file in
src/stacks/, e.g.src/stacks/app-stack.ts, and define yourStackclass. - Export the stack from
src/stacks/index.ts. - Instantiate the stack in
src/main.ts, ideally withcreateEnvResourceNameto keep names environment/branch aware. - Run
npm run <environment>:synthto validate CloudFormation output before deploying.
// 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
}
}
// src/main.ts
import { AppStack } from './stacks';
new AppStack(app, createEnvResourceName('AppStack'), { env: awsAccountConfig });
Add a reusable construct
Place constructs under src/constructs/, export them from src/constructs/index.ts, and consume them inside stacks. Extending BaseConstruct gives you access to the 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 });
}
}
// src/stacks/starter-stack.ts
import { StorageBucket } from '../constructs';
new StorageBucket(this, 'AssetsBucket');
Remember to run npx projen after updating .projenrc.ts so new directories or tasks are tracked, and keep related documentation under docs/ up to date.