【レポート】ヘキサゴナルアーキテクチャを利用したLambda関数のドメインモデルの実装Live (DEV-09) #AWSSummit

2022.05.31

2022 年 5 月 25 - 26 日の 2 日間AWS Summit Onlineが開催されました。本エントリは 26日に配信された「ヘキサゴナルアーキテクチャを利用したLambda 関数のドメインモデルの実装 Live」というセッションのレポートです。現在オンデマンド配信もされていますので、そちらもぜひチェックしてください。

セッション概要

マイクロサービスアーキテクチャが注目されるに従って再度ドメイン駆動設計への関心が高まっています。本セッションでは、ドメイン駆動設計、ヘキサゴナルアーキテクチャの概要を紹介し、AWS Lambdaでヘキサゴナルアーキテクチャを利用したドメインモデルの実装をライブコーディングを交えて解説します。ビジネスアプリケーションにとって重要なドメインモデルをサーバーレスを利用して実装するサンプルコードとしてもご利用頂けます。対象は、ドメイン駆動設計をサーバーレスで実装することに関心がある全てのアーキテクト、デベロッパーの方々です。

スピーカー

  • アマゾン ウェブ サービス ジャパン合同会社 シニアソリューションアーキテクト サーバーレス スペシャリスト 福井 厚
  • アマゾン ウェブ サービス ジャパン合同会社 技術統括本部 シニア サーバーレス スペシャリスト ソリューションアーキテクト 下川 賢介
  • アマゾン ウェブ サービス ジャパン合同会社 DevAx(Developer Acceleration) チーム ソリューションアーキテクト 金森 政雄

オンデマンド配信

事前にAWS Summitのポータルサイトへのユーザー登録とサインインが必要です。完了すると以下からセッションを聴講できます。(ライブ配信の際とURLが異なっているのでご注意を!)

セッション資料

サンプルプログラム

レポート

福井さんが進行されて、そこに下川さんと金森さんが質問を入れる、(それに対して福井さんが「素晴らしい質問ですねー」と返す)という流れで進みました。

セッションの目的

  • Lambdaでドメインモデルをどのように実装すればよいのか
  • Lambda関数のユニットテストの方法
  • Lambdaはほかサービスと連携する必要があるが、ドメインモデルはクリーンに保ちたい

このような質問を受けることが多かったので、回答となるサンプルコードを公開した。これをライブで紹介する。

ドメイン駆動設計とは

  • ドメインエキスパート(=業務をよく知っている人)と開発者が一緒になってモデルを作る。さらにモデルと実装が常につながっているように開発し続ける手法
  • ビジネスへの理解が深まるごとにモデル、コードも洗練、進化していく
  • ドメインモデルはレイヤ化アーキテクチャなどにより、インフラストラクチャコード(DBにアクセスする、メールを送るなど)とは分離され疎結合に保たれているべきである
  • →マイクロサービスアーキテクチャが注目されている昨今、サービスの分割方法を考える際にこのドメイン駆動設計の考え方が使えるのではないかと再び注目されている

ヘキサゴナルアーキテクチャとは

  • 別名ポートとアダプタアーキテクチャ
  • ソフトウェア設計のアーキテクチャパターン。ポートとアダプタを使いアプリケーションコンポーネントと環境とを疎結合に結ぶ

サンプルプログラムの紹介

  • ワクチン予約システムを題材にドメインモデルを実装している
  • AWSアーキテクチャはAPI Gateway→Lambda→DynamoDB

ドメインモデル

  • 非常にシンプル、外界の世界に関するロジック(AWS SDKを使ったり)は入っていない
  • メンバ変数に対してコンストラクタで値を与えてインスタンス化するだけ
  • 残りはそのプロパティ(メンバ変数)を取得するのとビジネスモデルの関数

インプットポート

  • IRecipientInputPortインターフェイス(抽象クラス)を継承している。これによってクラスの差し替えがしやすくなる

インプットアダプタ

  • Lambda自体がやってくれている。Lambdaを使うメリット
  • = handlerにイベントオブジェクトを渡してくれる。Lambda以外だとこの部分の変換を自前実装しないといけない

アウトプットポート

  • コンストラクタの引数にアダプターが必要
    • このアダプターもインターフェイスを継承している
  • add_reservation が呼ばれたときに、アダプターのsaveメソッドを呼ぶだけ
    • アダプターを切り替えれば、保存先を簡単に切り替えることができる

handler

  • JSONから必要なパラメーターを取り出すところは同じ
  • インプットポートインスタンスを取得するファクトリーメソッドのところで、アウトプットポート2つとアウトプットアダプター2つもインスタンス化している。一括して関連が組み立てられている(依存関係の注入)

デモ

SAM CLI を使ってデプロイして、作ったAPI Gatewayに対してリクエストを送り、ワクチン予約が成功したところまでを実演されました。 READMEに手順が記載されているので試してみてください。

Lambda関数のユニットテスト

Testing Pyramid

  • マニュアルテストを減らして、自動化したユニットテストを増やしていきましょう
  • つまり如何に自動化ユニットテストを書きやすいコードにできるかがカギ

ユニットテストの対象をTestableにする

  • y = f(x) の形にする
  • つまりxさえ決まればyの値が決まる形にする
  • 純粋な要件に対してテストケースを書く。AWSサービスとの結合方法に関する知識を含まない

ドメインモデルに対するテスト

  • 非常にシンプル
  • テスト内容から仕様も理解しやすい
  • 高速にテストできる

ポートのテスト

  • アダプタはダミーに切り替えて、アダプタの実装に拠らずにポートのテストをする

ローカルで実行確認するためのmain.py

  • ローカルコンソールからロジックを確認するために実行する
  • Lambdaデプロイ前の確認に使う
  • やっていることはapp.pyのlambda_handler関数と同じ

デモ

  • 高速にユニットテストを回せるところがダミーを使うメリット

まとめ

  • Lambdaでドメインモデルをどのように実装すれば良いのか
    → DDDのモデルをクラスに実装することは同じ。外部の知識を持たせない
  • Lambda関数のユニットテストはどのようにすれば良いのか
    → ドメインモデルクラスはピュアなビジネスロジックのテストが容易。 ポートクラスにはダミークラスを注入してテストを容易に
  • AWSの他のサービスと連携する必要があるが、ドメインモデルはクリーンに 保ちたい
    → Hexagonal Architectureを利用してドメインロジックと外部サービスの間を 疎結合に保つ

感想

私がドメイン駆動設計やヘキサゴナルアーキテクチャについて知見が無かったため少し難しかったです。とはいえ、サンプルコードと図解と口頭での説明と補足(下川さんと金森さんの質問)がある丁寧な構成だったので、きっと他の方法で学ぶよりは格段にわかりやすいのだと思います。実際コードを書くとなるとまだなかなか大変そうなので、さらにサンプルコードを読み込んだり、ドメイン駆動設計やヘキサゴナルアーキテクチャについて書籍を読んだりして理解を深めていきたいと思いました。