cdkでDynamoDB*Lambda*golangでタスク管理アプリケーションを作成してみる　DynamoDB作成編（第二回）

さて、今回は第一回でDynamoDBを設計したので、それに基づいてcdkで構築していきたいと思います。

https://qiita.com/YokoYokoko/items/c1fecc3278197ca43050

まずはcdkの初期化から始めていきます。詳しくは上記で説明しているのであまり分からない方は参考にしてください。

```
mkdir task-management-app
cd task-management-app
cdk init --language go
```

上記を実行するとファイルが色々と作成されますが、`cdk`ディレクトリを作成し、`main.go`を作成します。
```
mkdir cdk && cd cdk && touch main.go
```

で、ちょっと面倒ですが、cdkディレクトリに`cdk.json`を移動し、2行目を以下のように変更してください。
cdk initで作成される際には、`go run task-management-app.go`となっているので、このままだとデプロイするファイルがこのファイルになってしまいます。

```
{
  "app": "go mod download && go run main.go",
```

続いて、作成した`cdk/main.go`にDynamoDBをデプロイするためのコードを記述していく。
若干雑な設定がありますがご容赦ください🙇
```cdk/main.go
package main

import (
	"log"
	"os"

	"github.com/aws/aws-cdk-go/awscdk/v2"
	"github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/constructs-go/constructs/v10"
	"github.com/aws/jsii-runtime-go"
	"github.com/joho/godotenv"
)

type DynamoDBStackProps struct {
	awscdk.StackProps
}

func NewDynamoDBStack(scope constructs.Construct, id string, props *DynamoDBStackProps) awscdk.Stack {
	var sprops awscdk.StackProps
	if props != nil {
		sprops = props.StackProps
	}

	readCapacity := float64(2)
	writeCapacity := float64(2)

	stack := awscdk.NewStack(scope, &id, &sprops)
	table := awsdynamodb.NewTable(stack, jsii.String("TaskManagement"), &awsdynamodb.TableProps{
		TableName: jsii.String("TaskManagement"),
		PartitionKey: &awsdynamodb.Attribute{
			Name: jsii.String("id"), 
			Type: awsdynamodb.AttributeType_STRING},
		SortKey: &awsdynamodb.Attribute{
			Name: jsii.String("DataType"),
			Type: awsdynamodb.AttributeType_STRING},
		DeletionProtection: aws.Bool(true),
		ReadCapacity: &readCapacity,
		WriteCapacity: &writeCapacity,
	})

	table.AddGlobalSecondaryIndex(&awsdynamodb.GlobalSecondaryIndexProps{
		IndexName: jsii.String("GSI1"),
		PartitionKey: &awsdynamodb.Attribute{
			Name: jsii.String("DataValue"),
			Type: awsdynamodb.AttributeType_STRING,
		},
		SortKey: &awsdynamodb.Attribute{
			Name: jsii.String("id"),
			Type: awsdynamodb.AttributeType_STRING,
		},
		ReadCapacity: &readCapacity,
		WriteCapacity: &writeCapacity,
	})

	return stack
}

func main(){
	err := godotenv.Load()
	if err != nil {
		log.Fatalf("Error loading .env file")
	}

	app := awscdk.NewApp(nil)

	NewDynamoDBStack(app, "taskManagementStack", &DynamoDBStackProps{
		awscdk.StackProps{
			Env: env(),
		},
	})

	app.Synth(nil)
}

func env() *awscdk.Environment {
awsAccountId := os.Getenv("AWS_ACCOUNT_ID")
awsRegion := os.Getenv("AWS_REGION")

return &awscdk.Environment{
	Account: jsii.String(awsAccountId),
	Region:  jsii.String(awsRegion),
}
}

```

awsdynamodb.NewTablbeで作成したテーブルに`AddGlbalSecondaryIndex`を使用して追加します。
`AddGlbalSecondaryIndex`の引数には`(props *GlobalSecondaryIndexProps)`の以下の項目を渡します。
もちろん、以下に `GlobalSecondaryIndexProps` 構造体の各フィールドとその説明を表形式でまとめました。

| フィールド名            | 必須/オプション | 説明                                                                                      |
|---------------------|------------|-----------------------------------------------------------------------------------------|
| `IndexName`         | 必須         | セカンダリインデックスの名前。                                                             |
| `NonKeyAttributes`  | オプション    | セカンダリインデックスに投影される非キー属性。デフォルトは追加属性なし。                             |
| `ProjectionType`    | オプション    | セカンダリインデックスに投影される属性のセット。デフォルトは `ALL`。                                    |
| `PartitionKey`      | 必須         | パーティションキーの属性定義。                                                             |
| `SortKey`           | オプション    | ソートキーの属性定義。デフォルトはソートキーなし。                                                |
| `ReadCapacity`      | オプション    | グローバルセカンダリインデックスのリードキャパシティ。テーブルの `billingMode` が `Provisioned` または未定義の場合のみ設定可能。デフォルトは5。 |
| `WriteCapacity`     | オプション    | グローバルセカンダリインデックスのライトキャパシティ。テーブルの `billingMode` が `Provisioned` または未定義の場合のみ設定可能。デフォルトは5。 |

上の表の項目はよくある設定なので大体分かったのですが、`ProjectionType`がイマイチ分からないので調べてみると、SQLで言うと以下のような感じみたいです。

`ProjectionType.INCLUDE`の場合のみ、パーティションキーとソートキーに加えてどの属性を取得するか`NonKeyAttributes`で指定する必要があります。

今回は全属性欲しいのでデフォルトのALLのまま使用します。

| DynamoDBのProjectionType | SQLのクエリ |
|:-:|:-:|
|ProjectionType.ALL|SELECT * |
|ProjectionType.KEYS_ONLY|SELECT partition_key, sort_key|
|ProjectionType.INCLUDE|SELECT partion_key, sort_key, designated_attribute|


これで以下を実行すればデプロイ完了です！（初回の場合はbootstrapが必要です。）
```
cdk synth
cdk deploy 
```

今回、`No Bucket Name`というエラーでデプロイに失敗しました。。。
以下の記事が全く同じ状況で試したところ別のエラーが発生したのでbootstrapし直したところ、無事デプロイできました。
ありがとうございます🙌
https://qiita.com/ryome/items/6ae0ec1c2ae894954059


![スクリーンショット 2024-06-05 14.49.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324306/af4e5362-0b22-68f3-901d-647e884947fa.png)

期待通りGSIも付与できていました！
![スクリーンショット 2024-06-05 14.50.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324306/63608a9d-52af-7c26-3b1d-c8ef45a4adea.png)

次回は、`lambda`をスタックに追加して、lambda関数の中で読み込みや書き込みを行えるように実装していきます。