# CDK TypeScript Profile ## Concept → CDK Mapping | ABC Concept | Meaning | CDK Mapping | |---------------|-------------------------|-----------------------------------------| | ABC‑C0 | Construct | `Construct` subclass | | ABC‑C1 | Application Stack | `Stack` subclass | | ABC‑C2 | Logical Unit (LU) | `Construct` subclass with LU semantics | | ABC‑C3 | Resource Group (RG) | `Construct` subclass with RG semantics | | ABC‑C4 | Input Contract | TypeScript interface (`InputContract`) | | ABC‑C5 | Output Contract | TypeScript interface (`OutputContract`) | | ABC‑C6 | Instantiation Interface | Constructor (`scope`, `id`, `inputs`) | | ABC‑C7 | Capturing Down | Passing inputs to children | | ABC‑C8 | Bubbling Up | Exposing outputs upward | *ABC* itself remains naming‑agnostic; this profile defines CDK‑idiomatic conventions. ## Rules These are profile‑specific rules for CDK-Typescript users. ### ABC-PROFILE-CDKTS‑R1 (SHOULD) The directory structure SHOULD reflect the ABC hierarchy: Application Stack → Logical Units → Resource Groups. ### ABC-PROFILE-CDKTS‑R2 (SHOULD) Each Logical Unit SHOULD reside in its own top‑level directory: ```default src/ app-stack.ts data/ logic/ presentation/ ``` ### ABC-PROFILE-CDKTS‑R3 (SHOULD) Each Resource Group SHOULD reside inside its parent Logical Unit: ```default src/data/storage/ src/data/database/ ``` ### ABC-PROFILE-CDKTS‑R4 (SHOULD) Each construct directory SHOULD contain an index.ts exporting the construct. ### ABC-PROFILE-CDKTS‑R5 (SHOULD) File and directory names SHOULD follow CDK naming conventions. ### ABC-PROFILE-CDKTS‑R6 (MAY) Modules MAY split into multiple files if needed, as long as they remain in the same directory. ### ABC-PROFILE-CDKTS‑R7 (SHOULD) Each construct module SHOULD define its own `InputContract` and `OutputContract`. ### ABC-PROFILE-CDKTS‑R8 (SHOULD) Within a module, contracts SHOULD be named simply `InputContract` and `OutputContract`. ### ABC-PROFILE-CDKTS‑R9 (SHOULD) When importing contracts from another module, they SHOULD be aliased: ### ABC-PROFILE-CDKTS‑R10 (MUST) Constructs MUST be instantiated using a single InputContract object. Also see ABC‑R30/32. ### ABC-PROFILE-CDKTS‑R11 (MUST) All cross‑construct wiring MUST occur in the parent construct. Also see ABC‑R44. ### ABC-PROFILE-CDKTS‑R12 (SHOULD) Imported contracts SHOULD be aliased to descriptive names. ### ABC-PROFILE-CDKTS‑R13 (SHOULD) Contracts SHOULD be co‑located with their construct modules. ### ABC-PROFILE-CDKTS‑R14 (SHOULD) Generated code SHOULD mirror the ABC hierarchy in structure and composition. ### ABC-PROFILE-CDKTS‑R15 (SHOULD) Construct internals SHOULD NOT be accessed except through outputs. ### ABC-PROFILE-CDKTS‑R16 (SHOULD) Constructs SHOULD be generated in ABC order: Resource Groups → Logical Units → Application Stack. ## Base ABC Classes for Typescript-CDK ### Core Types ```javascript export interface InputContract {} export interface OutputContract {} export interface HasOutputs { readonly outputs: TOutputs; } ``` ### Application Stack Base ```javascript import { Stack, StackProps } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { OutputContract } from './core-types'; export abstract class ABCApplicationStack< TOutputs extends OutputContract = OutputContract > extends Stack { public abstract readonly outputs: TOutputs; protected constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); } } ``` ### Logical Unit Base ```javascript import { Construct } from 'constructs'; import { InputContract, OutputContract, HasOutputs } from './core-types'; export abstract class ABCLogicalUnit< TInputs extends InputContract, TOutputs extends OutputContract > extends Construct implements HasOutputs { public abstract readonly outputs: TOutputs; protected readonly inputs: TInputs; protected constructor(scope: Construct, id: string, inputs: TInputs) { super(scope, id); this.inputs = Object.freeze({ ...inputs }) as TInputs; } } ``` ### Resource Group Base ```javascript import { Construct } from 'constructs'; import { InputContract, OutputContract, HasOutputs } from './core-types'; export abstract class ABCResourceGroup< TInputs extends InputContract, TOutputs extends OutputContract > extends Construct implements HasOutputs { public abstract readonly outputs: TOutputs; protected readonly inputs: TInputs; protected constructor(scope: Construct, id: string, inputs: TInputs) { super(scope, id); this.inputs = Object.freeze({ ...inputs }) as TInputs; } } ``` ## Logical Units & Resource Groups (Canonical Example) Below is the full 3‑tier example implemented in CDK. ### Data Logical Unit ```default src/data/index.ts src/data/storage/index.ts src/data/database/index.ts ``` ```javascript import { Construct } from 'constructs'; import { ABCLogicalUnit } from '../abc/logical-unit'; import { StorageGroup } from './storage'; import { DatabaseGroup } from './database'; export interface InputContract { environment: string; region: string; storageClass: string; dbEngine: string; dbInstanceSize: string; } export interface OutputContract { storageBucketName: string; databaseEndpoint: string; } export class DataUnit extends ABCLogicalUnit { public readonly outputs: OutputContract; constructor(scope: Construct, id: string, inputs: InputContract) { super(scope, id, inputs); const storage = new StorageGroup(this, 'Storage', { environment: inputs.environment, region: inputs.region, storageClass: inputs.storageClass, }); const database = new DatabaseGroup(this, 'Database', { environment: inputs.environment, dbEngine: inputs.dbEngine, dbInstanceSize: inputs.dbInstanceSize, }); this.outputs = { storageBucketName: storage.outputs.storageBucketName, databaseEndpoint: database.outputs.databaseEndpoint, }; } } ``` #### Storage Resource Group ```default import { Construct } from 'constructs'; import { ABCResourceGroup } from '../../abc/resource-group'; export interface InputContract { environment: string; region: string; storageClass: string; } export interface OutputContract { storageBucketName: string; } export class StorageGroup extends ABCResourceGroup { public readonly outputs: OutputContract; constructor(scope: Construct, id: string, inputs: InputContract) { super(scope, id, inputs); this.outputs = { storageBucketName: 'bucket-name-placeholder', }; } } ``` #### Database Resource Group ```default import { Construct } from 'constructs'; import { ABCResourceGroup } from '../../abc/resource-group'; export interface InputContract { environment: string; dbEngine: string; dbInstanceSize: string; } export interface OutputContract { databaseEndpoint: string; } export class DatabaseGroup extends ABCResourceGroup { public readonly outputs: OutputContract; constructor(scope: Construct, id: string, inputs: InputContract) { super(scope, id, inputs); this.outputs = { databaseEndpoint: 'db-endpoint-placeholder', }; } } ``` ### Logic Logical Unit ```default src/logic/index.ts src/logic/compute/index.ts src/logic/messaging/index.ts ``` Compute Resource Group and Messaging Resource Group follow the same pattern. ### Presentation Logical Unit ```default src/presentation/index.ts src/presentation/webapp/index.ts src/presentation/cdn/index.ts ``` WebApp Resource Group and CDN Resource Group follow the same pattern. ## Application Stack ```javascript import { Construct } from 'constructs'; import { ABCApplicationStack } from './abc/application-stack'; import { DataUnit } from './data'; import type { InputContract as DataUnitInput } from './data'; import { LogicUnit } from './logic'; import type { InputContract as LogicUnitInput } from './logic'; import { PresentationUnit } from './presentation'; import type { InputContract as PresentationUnitInput } from './presentation'; export interface InputContract { environment: string; region: string; globalTags?: Record; } export interface OutputContract { frontendUrl: string; apiEndpoint: string; } export class AppStack extends ABCApplicationStack { public readonly outputs: OutputContract; constructor(scope: Construct, id: string, inputs: InputContract) { super(scope, id); const dataInputs: DataUnitInput = { environment: inputs.environment, region: inputs.region, storageClass: 'STANDARD', dbEngine: 'postgres', dbInstanceSize: 'db.t3.micro', }; const dataUnit = new DataUnit(this, 'DataUnit', dataInputs); const logicInputs: LogicUnitInput = { environment: inputs.environment, region: inputs.region, computeSize: 'small', messageRetention: 1209600, databaseEndpoint: dataUnit.outputs.databaseEndpoint, }; const logicUnit = new LogicUnit(this, 'LogicUnit', logicInputs); const presentationInputs: PresentationUnitInput = { environment: inputs.environment, region: inputs.region, frontendAssetsBucket: dataUnit.outputs.storageBucketName, apiEndpoint: logicUnit.outputs.apiEndpoint, }; const presentationUnit = new PresentationUnit(this, 'PresentationUnit', presentationInputs); this.outputs = { frontendUrl: presentationUnit.outputs.frontendUrl, apiEndpoint: logicUnit.outputs.apiEndpoint, }; } } ```