クラスメソッドのCDK事情大公開スペシャル#1 で「AWS CDK の設計では Construct の構造も意識しよう!」というテーマで登壇しました

クラスメソッドのCDK事情大公開スペシャル#1 で「AWS CDK の設計では Construct の構造も意識しよう!」というテーマで登壇しました

Clock Icon2024.11.02

こんにちは、製造ビジネステクノロジー部の若槻です。

2024/11/01 にクラスメソッド日比谷オフィスで開催された勉強会イベントクラスメソッドのCDK事情大公開スペシャル#1 で「AWS CDK の設計では Construct の構造も意識しよう!」というテーマで登壇しました。

https://classmethod.connpass.com/event/332020/

登壇資料

この資料では AWS CDK の Construct の構造について、設計時に意識するべきポイントを紹介し、以下のようにまとめています。

  • CloudFormation のツリービューを活用しよう
  • コンストラクト分割は、リソース種類単位とサービス単位を組み合わせて行うと良さげ
  • 上記を意識するとコンストラクト構造が直感的になり、開発・運用上の認知負荷を低減できる
    • マネコンからのリソース探索時
    • CDK コードによる開発時

サンプルコード

資料中で紹介した「リソース種類単位とサービス単位でのコンストラクト分割による実装」のサンプルコードです。

lib/construct/resource/dynamodb.ts
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

export class DynamoDBConstruct extends Construct {
  public readonly tableA: dynamodb.Table;
  public readonly tableB: dynamodb.Table;
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const tableA = new dynamodb.Table(this, 'TableA', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
    });
    this.tableA = tableA;

    const tableB = new dynamodb.Table(this, 'TableB', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
    });
    this.tableB = tableB;
  }
}
lib/construct/resource/index.ts
import { Construct } from 'constructs';

import { DynamoDBConstruct } from './dynamodb';

export class ResourceConstruct extends Construct {
  public readonly dynamodb: DynamoDBConstruct;
  constructor(scope: Construct, id: string) {
    super(scope, id);

    this.dynamodb = new DynamoDBConstruct(this, 'DynamoDB');
  }
}
lib/construct/service/fuga-batch.ts
import * as aws_lambda from 'aws-cdk-lib/aws-lambda';
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as aws_scheduler_alpha from '@aws-cdk/aws-scheduler-alpha';
import * as aws_scheduler_targets_alpha from '@aws-cdk/aws-scheduler-targets-alpha';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export interface FugaBatchServiceConstructProps {
  readonly tableA: dynamodb.Table;
  readonly tableB: dynamodb.Table;
}

export class FugaBatchServiceConstruct extends Construct {
  constructor(
    scope: Construct,
    id: string,
    props: FugaBatchServiceConstructProps
  ) {
    super(scope, id);

    const { tableA, tableB } = props;

    const batchFunction = new lambda_nodejs.NodejsFunction(this, 'Function', {
      entry: 'src/lambda/scheduled-batch.ts',
      runtime: aws_lambda.Runtime.NODEJS_20_X,
      environment: {
        TABLE_A_NAME: tableA.tableName,
        TABLE_B_NAME: tableB.tableName,
      },
    });

    new aws_scheduler_alpha.Schedule(this, 'Scheduler', {
      schedule: aws_scheduler_alpha.ScheduleExpression.cron({
        minute: '0',
        hour: '3',
        day: '*',
        month: '*',
        year: '*',
        timeZone: cdk.TimeZone.ASIA_TOKYO,
      }),
      target: new aws_scheduler_targets_alpha.LambdaInvoke(batchFunction, {}),
    });
  }
}
lib/construct/service/hoge-api.ts
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

export interface LambdaConstructProps {
  readonly tableA: dynamodb.Table;
}

export class HogeAPIServiceConstruct extends Construct {
  constructor(scope: Construct, id: string, prop: LambdaConstructProps) {
    super(scope, id);

    const { tableA } = prop;

    const apiFunction = new lambda_nodejs.NodejsFunction(this, 'Function', {
      entry: 'src/lambda/enduser-api.ts',
      environment: {
        RAW_DATA_TABLE_NAME: tableA.tableName,
      },
    });

    new apigateway.LambdaRestApi(this, 'RestAPI', {
      handler: apiFunction,
    });
  }
}
lib/construct/service/index.ts
import { Construct } from 'constructs';

import { HogeAPIServiceConstruct, LambdaConstructProps } from './hoge-api';
import {
  FugaBatchServiceConstruct,
  FugaBatchServiceConstructProps,
} from './fuga-batch';

export class ServiceConstruct extends Construct {
  constructor(
    scope: Construct,
    id: string,
    props: LambdaConstructProps & FugaBatchServiceConstructProps
  ) {
    super(scope, id);

    const { tableA, tableB } = props;

    new HogeAPIServiceConstruct(this, 'HogeApi', {
      tableA,
    });

    new FugaBatchServiceConstruct(this, 'FugaBatch', {
      tableA,
      tableB,
    });
  }
}
lib/main-2-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

import { ResourceConstruct } from './construct/resource';
import { ServiceConstruct } from './construct/service';

export class Main2Stack extends cdk.Stack {
  constructor(scope: Construct, id: string, prop: cdk.StackProps) {
    super(scope, id, prop);

    const { dynamodb } = new ResourceConstruct(this, 'Resource');

    new ServiceConstruct(this, 'Service', {
      tableA: dynamodb.tableA,
      tableB: dynamodb.tableB,
    });
  }
}

参考 URL

資料中で紹介した参考 URL です。

https://speakerdeck.com/winteryukky/aws-cdk-construct-partition-strategy-implementing-level-oriented-practice

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.