7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LaravelでDynamoDB Local環境を構築してCRUDを実装してみた

7
Last updated at Posted at 2026-03-03

こんにちは、ソーイの髙﨑です。

業務でLaravelとDynamoDBを連携する機会があり、
ローカル環境の構築からCRUD実装までを一通り行いました。

本記事では、その際に行った設定手順や実装方法についてまとめます。

目次

1.はじめに
2.DynamoDBとは
3.全体アーキテクチャ構成
4.事前準備
5.CRUD操作の実装例
6.DynamoDB特有のハマりどころ
7.まとめ

はじめに

この記事で分かること

・LaravelからDynamoDBへ接続する方法
・DynamoDB Local環境の構築手順
・AWS SDK for PHPを用いたCRUD実装例
・DynamoDB特有の設計上の注意点

本記事では、「DynamoDBとは何か」というところからAWS SDK for PHP(DynamoDbClient)を用いたLaravel環境との連携方法の手順についてまとめています。
本記事を読むことで、LaravelからDynamoDBへ接続し、基本的なCRUD処理を実装できるようになります。

本記事は下記環境での実装を想定しています。

php:8.3
laravel:11
aws-sdk-php:3.1

DynamoDBとは何か

DynamoDBはAWSが提供するフルマネージドなNoSQLデータベースです。 RDB(MySQLなど)とは異なり、

  • スキーマレス
  • JOINが存在しない
  • アクセスパターンを先に設計する

といった特徴があります。

パーティションキーとは

DynamoDBでは、データを一意に識別するために Partition Key(主キー)を設定します。 今回の例では id がそれに該当します。

Global Secondary Index(GSI)とは

主キー以外の属性で検索を可能にする仕組みです。 RDBの「インデックス」に近い概念ですが、 DynamoDBでは後付けではなく 設計段階で考慮する必要があります。

全体アーキテクチャ構成

今回はDynamoDBと連携し、基本的なCRUD操作を実装します。
アーキテクチャ構造は下記になります。
あああ.png

Laravelアプリケーションから、サービスクラス(DynamoDbService)を通じて AWS SDK for PHP を利用し、DynamoDBへアクセスします。

HTTPリクエストはControllerで受け取り、内部でDynamoDbServiceを呼び出すことで、
PutItem / GetItem / UpdateItem / DeleteItem といった操作を実行します。

開発環境では Docker 上の DynamoDB Local に接続し、
本番環境では AWS 上の DynamoDB に接続する構成になります。

事前準備(DynamoDB localコンテナの生成・ライブラリの導入)

本記事では、ローカル環境で動作確認を行うために、以下の3つのコンテナを利用します。

  • dynamodb-local
    → ローカルで動作するDynamoDB本体

  • dynamodb-setup
    → 起動時にテーブルを自動作成する初期化用コンテナ

  • dynamodb-admin
    → DynamoDBをGUIで確認できる管理画面

DynamoDB Localを利用することで、AWS上のDynamoDBへ接続せずに開発・テストを行うことができます。

AWS SDK for PHP のインストール

LaravelからDynamoDBへ接続するために、AWS SDK for PHPをインストールします。

composer require aws/aws-sdk-php

docker-composeの設定

docker-compose.yml の services に以下を追加します。

dynamodb-local:
    image: amazon/dynamodb-local:latest
    ports:
      - "8080:8000"
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath /home/dynamodblocal/data"
    volumes:
      - "./docker/dynamodb/data:/home/dynamodblocal/data"
    working_dir: /home/dynamodblocal
    environment:
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
      - AWS_REGION=ap-northeast-1
dynamodb-setup:
    image: amazon/aws-cli:latest
    depends_on:
      - dynamodb-local
    environment:
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
      - AWS_DEFAULT_REGION=ap-northeast-1
      - APP_ENV=${APP_ENV:-local}
    volumes:
      - "./docker/dynamodb/init-tables.sh:/init-tables.sh"
    entrypoint: >
      /bin/sh -c "
      chmod +x /init-tables.sh;
      /init-tables.sh;
      echo 'DynamoDB setup completed';
      "
dynamodb-admin:
    image: aaronshaf/dynamodb-admin:latest
    ports:
      - "8081:8001"
    environment:
      - DYNAMO_ENDPOINT=http://dynamodb-local:8000
      - AWS_REGION=ap-northeast-1
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
    depends_on:
      - dynamodb-local
      - dynamodb-setup

dynamodb-setup部分にテーブルを生成するinit-tables.shを指定しています。

dynamodb-setupコンテナでは、AWS CLIを使用してsimple-logsテーブルを自動生成しています。
Local環境では --endpoint-url を指定することで DynamoDB Local に接続しています。

※initスクリプトを使用しない場合は、dynamodb-admin 画面から手動でテーブルを作成することも可能です。

init-tables.sh

#!/bin/sh

aws dynamodb create-table \
  --table-name simple-logs \
  --attribute-definitions \
      AttributeName=id,AttributeType=S \
  --key-schema \
      AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST \
  --endpoint-url http://dynamodb-local:8000

.envファイルに下記を追加します。

AWS_ACCESS_KEY_ID=dummy
AWS_SECRET_ACCESS_KEY=dummy
AWS_DEFAULT_REGION=ap-northeast-1
DYNAMODB_ENDPOINT=http://dynamodb-local:8000

DYNAMODB_ENDPOINTを指定することで、
AWS上のDynamoDBではなくローカルのDynamoDBへ接続することができます。

本番環境ではこの値を設定せず、
AWSのエンドポイントへ接続する構成になります。

Dockerコンテナを起動します。

docker compose up -d

http://localhost:8081/にアクセスするとdynamodbの操作画面が表示されます。
Monosnap e8c0a21ef0e0f5101a0d9e590435203ca7bf96af 2026-03-01 14-04-57.png

以上で準備は完了です。次からは実際にDynamoDBテーブルへのCRUD操作を実装していきます。

CRUD操作の実装例

今回は、簡素なsimple-logsテーブルのデータを操作することを想定したCRUD処理の実装例を紹介します。

下記はsimple-logsテーブルの構成です。

属性名 説明
id String パーティションキー(主キー)
action String 実行されたアクション内容
created_at String 作成日時(ISO8601形式)
updated_at String 更新日時(ISO8601形式)

まず、DynamoDbService.phpを作成し、その中に処理を記入していきます。

<?php

namespace App\Services;

use Aws\DynamoDb\DynamoDbClient;
use Carbon\Carbon;
use Aws\Exception\AwsException;
use Illuminate\Support\Facades\Log;

class DynamoDbService
{
    protected DynamoDbClient $client;
    private string $table;

    public function __construct()
    {
        $this->client = new DynamoDbClient([
            'region' => env('AWS_DEFAULT_REGION'),
            'version' => 'latest',
            'endpoint' => env('DYNAMODB_ENDPOINT', null),
            'credentials' => [
                'key' => env('AWS_ACCESS_KEY_ID'),
                'secret' => env('AWS_SECRET_ACCESS_KEY'),
            ],
        ]);

        // テーブル名を設定
        $this->table = 'simple-logs';
    }

    // 以下に処理を追加
}

データの追加

データを追加するCreateメソッドを実装します。
DynamoDBでは型を明示する必要があり、S(String)、N(Number)、BOOLなどを指定します。
また、timestamp(created_at/updated_at)の自動追加・更新がないため、自前で実装します。

/**
 * Create: ログを1件追加
 */
public function create(string $id, string $action): void
{
    $now = Carbon::now()->toIso8601String();

    try {
        $this->client->putItem([
            'TableName' => $this->table,
            'Item' => [
                'id'         => ['S' => $id],
                'action'     => ['S' => $action],
                'created_at' => ['S' => $now],
                'updated_at' => ['S' => $now],
            ],
        ]);
    } catch (AwsException $e) {
        Log::error('DynamoDbService::create failed', ['error' => $e->getMessage()]);
        throw $e;
    }
}

データの取得

idで指定したデータを1件取得するfindメソッドを実装します。

/**
 * Read: id で1件取得
 */
public function find(string $id): ?array
{
    try {
        $result = $this->client->getItem([
            'TableName' => $this->table,
            'Key' => [
                'id' => ['S' => $id],
            ],
        ]);
    } catch (AwsException $e) {
        Log::error('DynamoDbService::find failed', ['error' => $e->getMessage()]);
        throw $e;
    }

    if (!isset($result['Item'])) {
        return null;
    }

    $item = $result['Item'];

    return [
        'id'         => (string) $item['id']['S'],
        'action'     => (string) $item['action']['S'],
        'created_at' => (string) $item['created_at']['S'],
        'updated_at' => (string) $item['updated_at']['S'],
    ];
}

データの更新

データの更新を行うupdateActionメソッドを実装します。
updated_atを一緒に更新しています。

/**
 * Update: action を更新(updated_at も更新)
 */
public function updateAction(string $id, string $action): void
{
    $now = Carbon::now()->toIso8601String();

    try {
        $this->client->updateItem([
            'TableName' => $this->table,
            'Key' => [
                'id' => ['S' => $id],
            ],
            'UpdateExpression' => 'SET action = :action, updated_at = :updated_at',
            'ExpressionAttributeValues' => [
                ':action'     => ['S' => $action],
                ':updated_at' => ['S' => $now],
            ],
        ]);
    } catch (AwsException $e) {
        Log::error('DynamoDbService::updateAction failed', ['error' => $e->getMessage()]);
        throw $e;
    }
}

データの削除

idで指定したデータを削除するdeleteメソッドを実装します。

/**
 * Delete: id で1件削除
 */
public function delete(string $id): void
{
    try {
        $this->client->deleteItem([
            'TableName' => $this->table,
            'Key' => [
                'id' => ['S' => $id],
            ],
        ]);
    } catch (AwsException $e) {
        Log::error('DynamoDbService::delete failed', ['error' => $e->getMessage()]);
        throw $e;
    }
}

DynamoDB特有のハマりどころ

① timestamp(created_at / updated_at)は自動で更新されない

LaravelのEloquentでは、$timestamps = true を設定することで
created_at / updated_at が自動更新されます。

しかし、DynamoDBにはそのような仕組みはありません。

DynamoDBはORMを持たないスキーマレスなデータベースであり、
自動的に日時を更新する機能は提供されていません。

そのため、アプリケーション側で明示的に日時を設定する必要があります。
また、更新時にも updated_at を自分で更新する必要があります。

② 検索のためにはIndex(GSI)を事前に設定する必要がある

DynamoDBでは、主キー(Partition Key)以外での検索は基本的にできません。

例えば、今回のテーブル構成では id が主キーとなっているため、
id 以外での高速検索はできません。

もし actioncreated_at で検索したい場合は、
Global Secondary Index(GSI)を事前に作成する必要があります。

Scanを使用すれば検索自体は可能ですが、
Scanはテーブル全件を読み込む処理のため、大量データがある場合は
パフォーマンスやコストの観点から推奨されません。

そのため、DynamoDBでは「どのキーで取得するか」を事前に設計し、
必要に応じてGSIを作成することが重要です。

まとめ

本記事では、Laravel環境からDynamoDBへ接続し、
AWS SDK for PHPを用いて基本的なCRUD処理を実装する方法を紹介しました。

業務では、ビデオチャットに用いるチャンネルデータを高スループットで扱う必要があったため、スケーラビリティに優れたDynamoDBを採用しました。

DynamoDBはRDBとは設計思想が大きく異なり、「テーブル設計」よりも「アクセスパターン設計」を先に考える必要があります。
特に、主キー以外で検索する場合はGSIの設計が重要になります。

実装当初は「あとから検索すればよい」と考えていましたが、DynamoDBではアクセスパターンを事前に設計しなければScanに頼る構成になってしまうと気付きました。

NoSQLに初めて触れる中で、timestampの自力実装やGSIの作成、キー設計など、RDBとは異なる視点で設計する重要性を学ぶことができました。

お知らせ

技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii

参照

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?