Upgrade to Pro — share decks privately, control downloads, hide ads and more …

保守性を高める AWS CDK のセオリー・ベストプラクティス

保守性を高める AWS CDK のセオリー・ベストプラクティス

「JAWS-UG東京 IaC Night 〜入門から上級まで!AWSをコードで構築しよう〜」で発表したスライドになります

https://jawsug.connpass.com/event/344954/

Ren Yamanashi

March 08, 2025
Tweet

More Decks by Ren Yamanashi

Other Decks in Programming

Transcript

  1. AWS CDK とは ? - プログラミング言語でAWSインフラを構築できるIaCツール ▶︎ プログラミング言語で書くことができるメリット - 型情報を用いてインフラ定義ができる(静的型付け言語の場合)

    - モジュール化による再利用性の向上 - インフラ定義にロジック(ループ処理/条件分岐/etc)を持ち込むことができる など ⬇︎ ロジックが複雑化しても、保守性が維持できるように (あるいは、要件が複雑化してもロジックを複雑化させないように)
  2. 保守性 とは ? 変更容易性 保守性 再利用性 モジュール性 ... 参考: JIS

    X 25010: 2013 - 保守性 ≒ システムを修正する有効性や効率性の度合い ▶︎ どれだけ効率的に(バグを発生させず)正確にコードを変更できるか
  3. 保守性 とは ? 変更容易性 保守性 再利用性 モジュール性 ... 参考: JIS

    X 25010: 2013 ❌ CDK での保守性が低い例 - あるConstructを修正したら、別のConstructにも意図しない影響があった - 条件分岐が多すぎて、要件変更時にどこを修正したら良いかわからない
  4. 保守性 とは ? 変更容易性 保守性 再利用性 モジュール性 ... 参考: JIS

    X 25010: 2013 - 保守性を高めるポイント - Construct同士の結合度(租結合) - Constructの安全性(e.g. 不要なConstructの上書きがされない) など
  5. 保守性 とは ? 変更容易性 保守性 再利用性 モジュール性 ... 参考: JIS

    X 25010: 2013 - 保守性を高めるポイント - Construct同士の結合度(租結合) - Constructの安全性(e.g. 不要なConstructの上書きがされない) など TypeScript のコードを例に紹介
  6. 1. Construct ID には変数を使用しない 2. Construct property / props は

    Class 型にしない 3. 外部に公開しないモジュールは private ディレクトリに 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  7. 1. Construct ID には変数を使用しない 2. Construct property / props は

    Class 型にしない 3. 外部に公開しないモジュールは private ディレクトリに 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  8. Construct ID には変数を使用しない - Construct ID: Construct に指定する ID (第二引数)

    - Construct ID は CloudFormation の論理IDを決めるためなどに使用される this.bucket = new Bucket(this, "MyBucket"); export class MyConstruct extends Construct { public readonly bucket: IBucket; constructor(scope: Construct, id: string) { super(scope, id); } }
  9. Construct ID には変数を使用しない ▼ Construct ID に変数を使用する例 this.bucket = new

    Bucket(this, id + "Bucket"); export class MyConstruct extends Construct { public readonly bucket: IBucket; constructor(scope: Construct, id: string) { super(scope, id); } }
  10. Construct ID には変数を使用しない - Construct ID に変数を使用するのは、基本的には良くない - 変数を使用する =

    Construct ID の決定が外的要因に作用される ▶︎ 意図しないところで Construct ID が変わり、論理 ID も変わる 論理 ID の安定性 作成後のリソースの論理IDの変更は避けてください。AWS CloudFormationは論理IDによってリソースを識別します。したがって、リソ ースの論理IDを変更すると、AWS CloudFormationは新しい論理IDで新しいリソースを作成し、既存のリソースを削除します。リソース の種類によっては、サービスの中断、データ損失、またはその両方が発生する可能性があります。 引用(翻訳したもの): https://docs.aws.amazon.com/cdk/v2/guide/identifiers.html this.bucket = new Bucket(this, id + "Bucket");
  11. 1. Construct ID には変数を使用しない 2. Construct property / props は

    Class 型にしない 3. 外部に公開しないモジュールは private ディレクトリに 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  12. Construct property / props は Class 型にしない - Construct property(public):

    Construct が公開する情報(変数) - props: Construct が外部から受け取る情報(変数) export class Ecs extends Construct { public readonly fargate: FargateService; // FargateService を公開 } export interface MyConstructProps { readonly fargate: FargateService; // FargateService を受け取る }
  13. Construct property / props は Class 型にしない - Construct 間の情報交換で、Class

    を使用すると・・・ - Ecs クラスの FargateService と MyConstruct が密結合に 集約 依存 Ecs FargateService MyConstruct const ecs = new Ecs(this, "Ecs"); fargate: ecs.fargateService, const myConstruct = new MyConstruct(this, "MyConstruct", { });
  14. Construct property / props は Class 型にしない - Construct 間の情報交換で、Class

    を使用すると・・・ - Ecs クラスの FargateService と MyConstruct が密結合に - MyConstruct 側で FargateService リソースの変更ができてしまう readonly fargate: FargateService; props.fargate.autoScaleTaskCount({ minCapacity: 2, maxCapacity: 6, }); export interface MyConstructProps { } export class MyConstruct extends Construct { constructor(scope: Construct, id: string, props: MyConstructProps) { super(scope, id);
  15. Construct property / props は Class 型にしない - Construct 間の情報交換は、Interface

    を使用して行う 集約 実装 依存 Ecs IFargateService FargateService MyConstruct export class Ecs extends Construct { public readonly fargate: IFargateService; // IFargateService を公開 }
  16. 1. Construct ID には変数を使用しない 2. Construct property / props は

    Class 型にしない 3. 外部に公開しないモジュールは private ディレクトリに 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  17. 外部に公開しないモジュールは private ディレクトリに - private ディレクトリ配下に置くことで、内部モジュールであることを明示 ▶︎ 不正な import を防ぐ

    & モジュール性の向上 ├── database │ └── private │ └── connection-config.ts lib/constructs │ ├── cluster.ts │ ├── instance.ts ├── api │ ├── ecs.ts
  18. 1. 外部に公開しないモジュールは private ディレクトリに 2. Construct ID には変数を使用しない 3. Construct

    property / props は Class 型にしない 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  19. 環境ごとに異なる設定値を外部化する - Construct 内で環境ごとに応じて条件分岐する const instance = new DatabaseInstance(this, "Instance",

    { instanceType: props.stage === "dev" // 環境に応じた条件分岐をする ? InstanceType.of(InstanceClass.T4G, InstanceSize.SMALL) : InstanceType.of(InstanceClass.T4G, InstanceSize.LARGE), // ... });
  20. 環境ごとに異なる設定値を外部化する - Construct 内で環境ごとに応じて条件分岐する - 環境ごとに異なる設定値は、基本的にはStackに依存する ▶︎ 特定の Stack 専用の

    Construct になる const instance = new DatabaseInstance(this, "Instance", { instanceType: props.stage === "dev" // 環境に応じた条件分岐をする ? InstanceType.of(InstanceClass.T4G, InstanceSize.SMALL) : InstanceType.of(InstanceClass.T4G, InstanceSize.LARGE), // ... });
  21. 環境ごとに異なる設定値を外部化する - Construct 内で環境ごとに応じて条件分岐する ⬇︎ より再利用性 / 変更容易性を高く const instance

    = new DatabaseInstance(this, "Instance", { instanceType: props.stage === "dev" // 環境に応じた条件分岐をする ? InstanceType.of(InstanceClass.T4G, InstanceSize.SMALL) : InstanceType.of(InstanceClass.T4G, InstanceSize.LARGE), // ... });
  22. 環境ごとに異なる設定値を外部化する - 環境ごとに異なる設定値を外部から受け取る - Construct の実装 // lib/constructs/database.ts export class

    Database extends Construct { const instance = new DatabaseInstance(this, "Instance", { constructor(scope: Construct, id: string, props: DatabaseProps) { super(scope, id); instanceType: props.instanceType, // ... }); } }
  23. 環境ごとに異なる設定値を外部化する - 環境ごとに異なる設定値を外部から受け取る - Stack の実装 // lib/my-stack.ts export class

    MyStack extends Stack { super(scope, id, props); const database = new Database(this, "Database", { // ... constructor(scope: Construct, id: string, props: MyStackProps) { instanceType: props.instanceType, }); } }
  24. 環境ごとに異なる設定値を外部化する - 環境ごとに異なる設定値を外部から受け取る - 設定値を生成する関数の実装 // lib/config.ts export const getStackProps

    = (stage: string): StackProps => { switch (stage) { case "dev": { return { env: { account: "123456789012", region: "ap-northeast-1", }, config: { databaseInstanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.SMALL), }, };
  25. 1. 外部に公開しないモジュールは private ディレクトリに 2. Construct ID には変数を使用しない 3. Construct

    property / props は Class 型にしない 4. 環境ごとに異なる設定値を外部化する 5. 命名規則を統一する
  26. 命名規則を統一する - 命名規則の例 - Construct ID ・・・ PascalCase - Construct

    props ・・・ ${ConstructName}Props - TS命名規則: https://typescript-jp.gitbook.io/deep-dive/styleguide new MyConstruct(this, "MyConstruct"); export interface MyConstructProps { // ... } export class MyConstruct extends Construct {