init
This commit is contained in:
commit
41481636d8
12 changed files with 2126 additions and 0 deletions
378
profiles/cdk-ts.md
Normal file
378
profiles/cdk-ts.md
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
# 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<TOutputs extends OutputContract> {
|
||||
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<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
|
||||
|
||||
```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<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 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<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
|
||||
|
||||
```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<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,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
21
profiles/index.md
Normal file
21
profiles/index.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Profiles
|
||||
|
||||
Profiles define how the ABC Pattern maps onto specific IaC tools. Each profile
|
||||
introduces additional rules, conventions, and examples tailored to the target
|
||||
system.
|
||||
|
||||
Profiles use identifiers of the form:
|
||||
|
||||
```default
|
||||
ABC-PROFILE-<PROFILE>-R#
|
||||
```
|
||||
|
||||
Current profiles include:
|
||||
|
||||
* imperative, object‑oriented implementations
|
||||
* declarative module‑based implementation
|
||||
|
||||
Profiles demonstrate that ABC is not tied to any particular paradigm.
|
||||
|
||||
* [CDK TypeScript Profile](cdk-ts.md)
|
||||
* [Terraform Profile](tf.md)
|
||||
199
profiles/tf.md
Normal file
199
profiles/tf.md
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
# Terraform Profile
|
||||
|
||||
## Concept → Terraform Mapping
|
||||
|
||||
| ABC Concept | Meaning | Terraform Mapping |
|
||||
|---------------|-------------------------|----------------------------------------------------|
|
||||
| ABC‑C0 | Construct | Terraform module |
|
||||
| ABC‑C1 | Application Stack | Root Terraform module |
|
||||
| ABC‑C2 | Logical Unit | Child module representing a domain |
|
||||
| ABC‑C3 | Resource Group | Submodule representing a cohesive resource cluster |
|
||||
| ABC‑C4 | Input Contract | variables.tf in a module |
|
||||
| ABC‑C5 | Output Contract | outputs.tf in a module |
|
||||
| ABC‑C6 | Instantiation Interface | `module "" { ... }` block |
|
||||
| ABC‑C7 | Capturing Down | Passing variables from parent to child module |
|
||||
| ABC‑C8 | Bubbling Up | Exposing outputs from child modules to parent |
|
||||
|
||||
## Proile Rules
|
||||
|
||||
Terraform profile rules follow the canonical identifier format:
|
||||
|
||||
```default
|
||||
ABC-PROFILE-TF-R#
|
||||
```
|
||||
|
||||
These rules are profile‑specific, not core ABC rules.
|
||||
|
||||
### ABC-PROFILE-TF-R1 (SHOULD)
|
||||
|
||||
Each ABC construct SHOULD be implemented as a Terraform module.
|
||||
|
||||
### ABC-PROFILE-TF-R2 (SHOULD)
|
||||
|
||||
The directory structure SHOULD reflect the ABC hierarchy:
|
||||
|
||||
```default
|
||||
root/
|
||||
main.tf
|
||||
data/
|
||||
main.tf
|
||||
storage/
|
||||
main.tf
|
||||
database/
|
||||
main.tf
|
||||
logic/
|
||||
main.tf
|
||||
presentation/
|
||||
main.tf
|
||||
```
|
||||
|
||||
### ABC-PROFILE-TF-R3 (SHOULD)
|
||||
|
||||
Each module SHOULD contain:
|
||||
|
||||
* main.tf
|
||||
* variables.tf (InputContract)
|
||||
* outputs.tf (OutputContract)
|
||||
|
||||
### ABC-PROFILE-TF-R4 (MUST)
|
||||
|
||||
Module inputs MUST be declared exclusively in variables.tf.
|
||||
|
||||
### ABC-PROFILE-TF-R5 (MUST)
|
||||
|
||||
Module outputs MUST be declared exclusively in outputs.tf.
|
||||
|
||||
### ABC-PROFILE-TF-R6 (MUST)
|
||||
|
||||
Modules MUST NOT reference parent or sibling modules directly; all data MUST
|
||||
flow through variables and outputs.
|
||||
|
||||
(This enforces ABC‑R22, ABC‑R40, ABC‑R42.)
|
||||
|
||||
### ABC-PROFILE-TF-R7 (MUST)
|
||||
|
||||
Modules MUST be instantiated using a module “<name>” { … } block with explicit
|
||||
variable assignments.
|
||||
|
||||
### ABC-PROFILE-TF-R8 (MUST)
|
||||
|
||||
Modules MUST NOT read Terraform state from other modules except via outputs.
|
||||
|
||||
### ABC-PROFILE-TF-R9 (MUST)
|
||||
|
||||
Capturing Down MUST be implemented by passing parent variables or outputs into
|
||||
child module inputs.
|
||||
|
||||
### ABC-PROFILE-TF-R10 (MUST)
|
||||
|
||||
Bubbling Up MUST be implemented by exposing child module outputs and re‑exposing
|
||||
them in the parent module if needed.
|
||||
|
||||
### ABC-PROFILE-TF-R11 (MUST)
|
||||
|
||||
Resource definitions MUST reside only in Resource Group modules (ABC‑C3).
|
||||
|
||||
### ABC-PROFILE-TF-R12 (MUST)
|
||||
|
||||
Logical Units MUST NOT contain Terraform resources directly.
|
||||
|
||||
### ABC-PROFILE-TF-R13 (SHOULD)
|
||||
|
||||
Logical Units SHOULD only orchestrate child modules and expose aggregated
|
||||
outputs.
|
||||
|
||||
## Canonical Example
|
||||
|
||||
A minimal 3‑tier ABC architecture in Terraform.
|
||||
|
||||
### Application Stack
|
||||
|
||||
```hcl
|
||||
module "data" {
|
||||
source = "./data"
|
||||
environment = var.environment
|
||||
region = var.region
|
||||
}
|
||||
|
||||
module "logic" {
|
||||
source = "./logic"
|
||||
environment = var.environment
|
||||
region = var.region
|
||||
database_endpoint = module.data.database_endpoint
|
||||
}
|
||||
|
||||
module "presentation" {
|
||||
source = "./presentation"
|
||||
environment = var.environment
|
||||
region = var.region
|
||||
frontend_assets_bucket = module.data.storage_bucket_name
|
||||
api_endpoint = module.logic.api_endpoint
|
||||
}
|
||||
|
||||
output "frontend_url" {
|
||||
value = module.presentation.frontend_url
|
||||
}
|
||||
|
||||
output "api_endpoint" {
|
||||
value = module.logic.api_endpoint
|
||||
}
|
||||
```
|
||||
|
||||
```hcl
|
||||
variable "environment" { type = string }
|
||||
variable "region" { type = string }
|
||||
```
|
||||
|
||||
### Data Logical Unit
|
||||
|
||||
```default
|
||||
module "storage" {
|
||||
source = "./storage"
|
||||
environment = var.environment
|
||||
region = var.region
|
||||
storage_class = var.storage_class
|
||||
}
|
||||
|
||||
module "database" {
|
||||
source = "./database"
|
||||
environment = var.environment
|
||||
db_engine = var.db_engine
|
||||
db_instance_size = var.db_instance_size
|
||||
}
|
||||
|
||||
output "storage_bucket_name" {
|
||||
value = module.storage.bucket_name
|
||||
}
|
||||
|
||||
output "database_endpoint" {
|
||||
value = module.database.endpoint
|
||||
}
|
||||
```
|
||||
|
||||
```default
|
||||
variable "environment" { type = string }
|
||||
variable "region" { type = string }
|
||||
variable "storage_class" { type = string }
|
||||
variable "db_engine" { type = string }
|
||||
variable "db_instance_size" { type = string }
|
||||
```
|
||||
|
||||
#### Storage Resource Group
|
||||
|
||||
```hcl
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "${var.environment}-storage"
|
||||
}
|
||||
```
|
||||
|
||||
```hcl
|
||||
variable "environment" { type = string }
|
||||
variable "region" { type = string }
|
||||
variable "storage_class" { type = string }
|
||||
```
|
||||
|
||||
```hcl
|
||||
output "bucket_name" {
|
||||
value = aws_s3_bucket.bucket.bucket
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue