-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtenant-table.ts
More file actions
46 lines (42 loc) · 2.27 KB
/
Copy pathtenant-table.ts
File metadata and controls
46 lines (42 loc) · 2.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { RemovalPolicy } from 'aws-cdk-lib';
import { AttributeType, BillingMode, Table, TableEncryption } from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
export interface TenantTableProps {
/** Prod is retained on stack deletion; dev/stage are disposable (ephemeral PR/dev stacks
* per ADR-0010's three-account environment-promotion model) so tearing one down never needs
* a manual DynamoDB console detour. */
readonly retain: boolean;
}
/**
* Single table backing every tenant-owned entity: settings, orgs, seen, postings,
* subscriptions, sends, applications, budget ledgers, credentials, entitlements, metering,
* source-yield, users (`openjobradar.tenancy.entities.ENTITIES`).
*
* Partition key `userId` and sort key `sk` mirror `entities.scoped_key` exactly — the Python
* layer already computes `sk` as the entity-prefixed string (`f"{entity}:{value}"`), so this
* table's key schema is the storage-layer mirror of a contract that already exists and is
* merge-gate tested (`tests/test_repository_isolation.py`), not a new design surface.
*
* One table, not one-per-entity (roadmap Appendix B's original sketch, written before
* `TenantRepository`/`Store` existed): every entity shares the identical access pattern —
* get/put/delete/list-by-prefix scoped to one tenant — so N copies of the same table would add
* operational surface without a different access pattern to justify it. See ADR-0021.
*
* Credentials rows carry an *additional* per-user KMS data-key envelope at the application
* layer (ADR-0007, `tenancy/vault.py`); this table's own encryption is the baseline at-rest
* protection every row gets, not a substitute for that.
*/
export class TenantTable extends Construct {
public readonly table: Table;
constructor(scope: Construct, id: string, props: TenantTableProps) {
super(scope, id);
this.table = new Table(this, 'Table', {
partitionKey: { name: 'userId', type: AttributeType.STRING },
sortKey: { name: 'sk', type: AttributeType.STRING },
billingMode: BillingMode.PAY_PER_REQUEST,
encryption: TableEncryption.AWS_MANAGED,
pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
removalPolicy: props.retain ? RemovalPolicy.RETAIN : RemovalPolicy.DESTROY,
});
}
}