abc-ai/profiles/cdk-ts.md
Tiara Rodney 6120b71aca
new
2026-02-05 14:47:26 +01:00

378 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CDK TypeScript Profile
## Concept → CDK Mapping
| ABC Concept | Meaning | CDK Mapping |
|---------------|-------------------------|-----------------------------------------|
| ABCC0 | Construct | `Construct` subclass |
| ABCC1 | Application Stack | `Stack` subclass |
| ABCC2 | Logical Unit (LU) | `Construct` subclass with LU semantics |
| ABCC3 | Resource Group (RG) | `Construct` subclass with RG semantics |
| ABCC4 | Input Contract | TypeScript interface (`InputContract`) |
| ABCC5 | Output Contract | TypeScript interface (`OutputContract`) |
| ABCC6 | Instantiation Interface | Constructor (`scope`, `id`, `inputs`) |
| ABCC7 | Capturing Down | Passing inputs to children |
| ABCC8 | Bubbling Up | Exposing outputs upward |
*ABC* itself remains namingagnostic; this profile defines CDKidiomatic
conventions.
## Rules
These are profilespecific rules for CDK-Typescript users.
### ABC-PROFILE-CDKTSR1 (SHOULD)
The directory structure SHOULD reflect the ABC hierarchy:
Application Stack → Logical Units → Resource Groups.
### ABC-PROFILE-CDKTSR2 (SHOULD)
Each Logical Unit SHOULD reside in its own toplevel directory:
```default
src/
app-stack.ts
data/
logic/
presentation/
```
### ABC-PROFILE-CDKTSR3 (SHOULD)
Each Resource Group SHOULD reside inside its parent Logical Unit:
```default
src/data/storage/
src/data/database/
```
### ABC-PROFILE-CDKTSR4 (SHOULD)
Each construct directory SHOULD contain an index.ts exporting the construct.
### ABC-PROFILE-CDKTSR5 (SHOULD)
File and directory names SHOULD follow CDK naming conventions.
### ABC-PROFILE-CDKTSR6 (MAY)
Modules MAY split into multiple files if needed, as long as they remain in the
same directory.
### ABC-PROFILE-CDKTSR7 (SHOULD)
Each construct module SHOULD define its own `InputContract` and
`OutputContract`.
### ABC-PROFILE-CDKTSR8 (SHOULD)
Within a module, contracts SHOULD be named simply `InputContract` and
`OutputContract`.
### ABC-PROFILE-CDKTSR9 (SHOULD)
When importing contracts from another module, they SHOULD be aliased:
### ABC-PROFILE-CDKTSR10 (MUST)
Constructs MUST be instantiated using a single InputContract object.
Also see ABCR30/32.
### ABC-PROFILE-CDKTSR11 (MUST)
All crossconstruct wiring MUST occur in the parent construct.
Also see ABCR44.
### ABC-PROFILE-CDKTSR12 (SHOULD)
Imported contracts SHOULD be aliased to descriptive names.
### ABC-PROFILE-CDKTSR13 (SHOULD)
Contracts SHOULD be colocated with their construct modules.
### ABC-PROFILE-CDKTSR14 (SHOULD)
Generated code SHOULD mirror the ABC hierarchy in structure and composition.
### ABC-PROFILE-CDKTSR15 (SHOULD)
Construct internals SHOULD NOT be accessed except through outputs.
### ABC-PROFILE-CDKTSR16 (SHOULD)
Constructs SHOULD be generated in ABC order: Resource Groups → Logical Units →
Application Stack.
## Base ABC Classes for Typescript-CDK
### Core Types
```typescript
export interface InputContract {}
export interface OutputContract {}
export interface HasOutputs<TOutputs extends OutputContract> {
readonly outputs: TOutputs;
}
```
### Application Stack Base
```typescript
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
```typescript
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<TOutputs> {
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
```typescript
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<TOutputs> {
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 3tier example implemented in CDK.
### Data Logical Unit
```default
src/data/index.ts
src/data/storage/index.ts
src/data/database/index.ts
```
```typescript
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<InputContract, OutputContract> {
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<InputContract, OutputContract> {
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<InputContract, OutputContract> {
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
```typescript
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<string, string>;
}
export interface OutputContract {
frontendUrl: string;
apiEndpoint: string;
}
export class AppStack extends ABCApplicationStack<OutputContract> {
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,
};
}
}
```