前回は、AP型データベースである「Amazon DynamoDB」の概要を説明した上で、テーブルを構築しました。また、アプリケーションからアクセスするためのユーザーを作成するところまでご説明したかと思います。続く今回は、「Spring Data DynamoDB」を使ってデータベースアクセスするアプリケーションを実装してみます。

Spring Data DynamoDBの概要

Spring Data DynamoDBはSpring Dataのコミュニティプロジェクトで、Spring Data JPAなどと同様にデータベースアクセス処理を抽象化/簡易化し、CRUD操作などの処理実装を提供するフレームワークです。主な特徴は、以下の通りです。

  • DynamoDBテーブルに対するCRUDメソッドの実装の提供
  • キーワードを元にしたクエリメソッドによる動的クエリの生成
  • アノテーションベースの簡易なスキーマ表現
  • ページネーション、カスタムデータアクセス/データ射影
  • Spring Data RestとのRESTサポート

今回はこのフレームワークを用いて、DynamoDBにアクセスするアプリケーションを実装してみましょう。

なお、実際に作成したアプリケーションはGitHub上にコミットしています。以降記載されているソースコードでは、import文など本質的でない記述を省略している部分がありますので、実行コードを作成する場合は、必要に応じて適宜GitHub上のソースコードも参照してください。

Spring Data DynamoDBを使うための設定

Spring Data DynamoDBを使用するには、まず、Mavenプロジェクトのpom.xmlで、spring-data-dynamodbのライブラリを定義します。

ブラウザからDynamoDBへアクセスするアプリケーションを作成するには、Webアプリケーションを作るためのspring-boot-starter-web、thymeleafテンプレートエンジンを使用するためのspring-boot-starter-thymeleaf、DynamoDBテーブルのエンティティクラスの実装を簡易化するためのLombokライブラリの追加が必要です。

<dependencies>
  <dependency>
    <groupId>com.github.derjust</groupId>
    <artifactId>spring-data-dynamodb</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>

今回、アプリケーションは以下のコンポーネントで構成しますが、Spring Data DynamoDBで最低限作成しなければならない必須のクラスは以下の通りです。

なお、thymeleafテンプレートエンジンに関する説明は省略しますが、必要に応じて、Thymeleaf公式ドキュメントMacchinetta Frameworkテンプレートエンジンを参照してください。

コンポーネント 説明 必須
WebApp SpringBootアプリケーションを実行する起動クラス
MvcConfig Webアプリケーションの設定を行うクラス
DomainConfig サービスレイヤの設定を行うクラス
DynamoDBConfig DynamoDBへの接続に関する設定クラス
SampleModel 画面からのインプットパラメータを表すモデル
SampleController 画面からのリクエストを受け付け、Serviceを実行し、結果をテンプレートエンジンに渡す
SampleModelMapper SampleModelとMynaviSampleTableのパラメータをマッピングする
SampleService トランザクション境界となり、Repositoryを実行する
SampleRepository データベースアクセスを実行する
MynaviSampleTable DynamoDBのテーブルを表現するエンティティクラス

以降、各々のクラスについて解説を進めていきますが、事前にAWS公式ページにある「設定ファイルと認証情報ファイル」を参考に、ユーザーホームフォルダに.awsディレクトリを作成し、前回取得したCSV形式の認証キーに記載されているユーザー認証情報を保存してください。ファイル名は「credential」、形式は以下の通りです。

[default]
aws_access_key_id=XXXXXXXXXXXXXXXX
aws_secret_access_key=YYYYYYYYYYYYYYYYYYYYYYYYYYYYY

それでは、実装していくクラスについて説明します。

まず最初が、SpringBoot起動クラスと、各種の設定クラスです。@SpringBootApplicaitonアノテーションが付与された起動クラスは、同一パッケージにある@Configurationアノテーションが付与された設定クラス及び、設定クラス内で@ComponentScanされたパッケージにあるクラスを読み取ります。今回は目的に応じて以下の3つに分類して設定クラスを作成します。

  • MvcConfig:SpringMVCの設定クラスであるWebMvcConfigurerを実装した設定クラス
  • DomainConfig:Serviceをコンポーネントスキャンで読み取る設定クラス
  • DynamoDBConfig:DynamoDBの接続を行う設定クラス

設定クラスは必ずしも複数である必要はなく、1つにまとめても動作上問題ありません。ただし、クラス名と役割を対応付けて作成しておいたほうが、後々混乱することなく、クラス名から設定内容を識別できるのでベターでしょう。

package org.debugroom.mynavi.sample.spring.data.dynamodb.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebApp {

    public static void main(String[] args) {
        SpringApplication.run(WebApp.class, args);
    }

}

同一パッケージに配置するMvcConfigクラスでは、HTMLやCSSなどの静的リソースのURLと実際のリソースの物理配置の対応付けの定義をResourceHandlerRegistryに追加しておきます。また、Controllerクラスを読み取るために、ComponentScanアノテーションに該当のパッケージを指定しておきます。

package org.debugroom.mynavi.sample.spring.data.dynamodb.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@ComponentScan("org.debugroom.mynavi.sample.spring.data.dynamodb.app.web")
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
          .addResourceLocations("classpath:/static/");
    }

}

また、DomainConfigクラスでは、Serviceなどビジネスロジックレイヤに属するコンポーネントがあるパッケージをスキャンする定義を追加しておきます。

package org.debugroom.mynavi.sample.spring.data.dynamodb.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan("org.debugroom.mynavi.sample.spring.data.dynamodb.domain")
@Configuration
public class DomainConfig {
}

続いて、DynamoDBへの接続で必須になる設定クラスがDynamoDBConfigクラスです。@EnableDynamoDBRepositoriesアノテーションを付与すると、DynamoDBの接続に必要なクラスがアプリケーション起動時に自動で構築されるようになります。@EnableDynamoDBRepositoriesには、同時にDynamoDBテーブルへアクセスするRepositoryクラスがあるベースパッケージを指定しなければなりません。

DynamoDB接続に必要なクラスはSpringBootが自動構築してくれますが、最低限、「接続先のDynamoDBのリージョン」「エンドポイント」「認証情報」の3つを設定する必要があります。

Spring Data DynamoDBも内部的には、DynamoDBとの接続にAWS SDKが提供しているcom.amazonaws.services.dynamodbv2.AmazonDynamoDBを利用しており、com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilderを使って、リージョンや認証情報を含めてインスタンスを作成するのが一般的です。そのため、設定クラス上でAmazonDynamoDBクラスの設定を行ってBean定義します。

なお、AmazonDynamoDBClientBuilderは、デフォルトで、事前に設定しておいた.aws内にあるCredentialの中にあるアクセスIDとシークレットキーを参照する実装になっています。古いWeb記事のサンプルなどでは、アプリケーション内で直接ClientBuilderにアクセスIDとシークレットキーを設定するコードをたまに見かけますが、AWSとしてこのやり方は推奨していないので、アプリケーションにキーを設定するコードを記述するのはやめておきましょう。

以下のコードでは、AwsClientBuilderのEndpointConfigrationに、東京リージョンとDynamoDBのサービスエンドポイントを設定ファイル「application.yml」から取得して、AmazonDynamoDBClientBuilderへ設定しています。

package org.debugroom.mynavi.sample.spring.data.dynamodb.config;

import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import org.socialsignin.spring.data.dynamodb.repository.config.EnableDynamoDBRepositories;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDynamoDBRepositories(basePackages = "org.debugroom.mynavi.sample.spring.data.dynamodb.domain.repository")
public class DynamoDBConfig {

    @Value("${amazon.dynamodb.region}")
    private String region;
    @Value("${amazon.dynamodb.endpoint}")
    private String endpoint;

    @Bean
    public AmazonDynamoDB amazonDynamoDB(){
        return AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(
             new AwsClientBuilder.EndpointConfiguration(endpoint, region)).build();
    }

}

application.ymlの中身は以下のようになっています。

amazon:
  dynamodb:
    region: ap-northeast-1
    endpoint: https://dynamodb.ap-northeast-1.amazonaws.com

●AmazonDynamoDBClientBuilderについて

DynamoDBオブジェクトの生成に利用したAWS SDKのAmazonDynamoDBClientBuilderは、正確に言えば以下の順序で認証情報を取得していく実装になっています。

  1. 環境変数AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEY
  2. システムプロパティaws.accessKeyIdとaws.secretKey
  3. ユーザーのAWS認証情報ファイル
  4. AWSインスタンスプロファイルの認証情報
これまでの設定では、ローカル環境ではユーザーのAWS認証情報ファイルで取得する状態になっていますが、この実装のまま本番環境へ持って行っても、最後のAWSインスタンスプロファイルから認証情報を取得できます。

この辺りの挙動の詳細を知りたい方は、DefaultAWSCredentialsProviderChainの実装を参照してみてください。最後のインスタンスプロファイル情報はアプリケーションを実行するEC2インスタンス、もしくはECSコンテナが持つ認証情報で、インスタンスやコンテナを実行する際、IAMロールから権限を割り当てるオプションがあります。

開発環境/本番環境が変わるからといって、環境依存をなくすためにアプリケーション内でアクセスキーIDやシークレットキーを割り当てる実装は必要ありません。今回説明したように、開発環境(AWS内ではないローカル環境の場合)は.aws配下の認証情報を取得し、本番環境では、インスタンスのプロファイル上から認証情報を取得する実装を心掛けてください。

アプリケーションの設定に関する実装はここまでです。次回は、アプリケーションのDBアクセス処理などの実装を進めていきます。

著者紹介


川畑 光平(KAWABATA Kohei) - NTTデータ 課長代理

金融機関システム業務アプリケーション開発・システム基盤担当を経て、現在はソフトウェア開発自動化関連の研究開発・推進に従事。

Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなどさまざまな開発プロジェクト支援にも携わる。2019 APN AWS Top Engineers & Ambassadors選出。