Slide 1

Slide 1 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Developer Zone ヘキサゴナルアーキテクチャを利⽤した Lambda 関数のドメインモデルの実装 Live Atsushi Fukui S E S S I O N I D : D E V - 0 9 Senior Solutions Architect, Serverless Specialist Amazon Web Services Japan

Slide 2

Slide 2 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ⾃⼰紹介 v名前 v福井 厚(ふくい あつし)Twitter: afukui@ v所属 vアマゾン ウェブ サービス ジャパン合同会社 vシニアソリューションアーキテクト サーバーレス スペシャリスト v関⼼領域 vソフトウェア アーキテクチャ、オブジェクト指向設計、アジャイル開発 v好きなAWSサービス vサーバーレステクノロジー全般、 AWS Code シリーズ、AWS Amplify

Slide 3

Slide 3 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 下川 賢介 シニア サーバーレス スペシャリスト ソリューションアーキテクト 技術統括本部 アマゾン ウェブ サービス ジャパン合同会社 Kensuke Shimokawa

Slide 4

Slide 4 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ⾃⼰紹介 Amazon Elastic Container Service AWS Step Functions AWS Fault Injection Simulator ⾦森 政雄 Ø 所属/役職 : DevAx(Developer Acceleration) チーム ソリューションアーキテクト Ø 好きなサービス

Slide 5

Slide 5 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 本セッションのきっかけと⽬的 • 以下のような質問を頂くことが多かった § AWS Lambdaでドメインモデルをどのように実装すれば良いのか – マイクロサービスをLambdaで実装したいがDDDも活⽤したい § Lambda関数のユニットテストはどのようにすれば良いのか – AWS環境にデプロイする前にLambda関数をテストしたい § AWSの他のサービスと連携する必要があるが、ドメインモデルはクリーンに 保ちたい – ドメインモデルとAWSサービスの呼び出しが混在するとコードの可読性が低下する § そこでドメインモデルをクリーンに保ち、単体テストを容易にする サンプル実装を提供。本⽇はこれをライブで解説します︕

Slide 6

Slide 6 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Lambda でユニットテストは難しい︖ ならばユニットテストが容易な構造にする

Slide 7

Slide 7 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Agenda • ドメイン駆動設計とは • ヘキサゴナルアーキテクチャとは • サンプルプログラムの紹介 • Lambda関数のユニットテスト • まとめ

Slide 8

Slide 8 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ドメイン駆動設計とは

Slide 9

Slide 9 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ドメイン駆動設計とは • Eric Evansが2003年に「ドメイン駆動設計(DDD)」を発表 § ドメインエキスパートが⽤いるドメイン内で共通の⾔語(ユビキタス⾔語)で モデリングを⾏いモデルと実装が常に連動しているスタイルで開発を⾏う⼿法 § ドメインへの理解が進むに従ってモデルは洗練され、それに伴ってコードも進化 していく § ドメインモデルはレイヤ化アーキテクチャなどによって、インフラストラクチャ コードとは分離されている

Slide 10

Slide 10 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ドメイン駆動設計 (Eric Evans - 2003) • 設計における意思決定とドメイン 設計の議論における技術的な⽤語の 広義のフレームワークを提供 • ユビキタス⾔語 ー ビジネスドメイン エキスパートと開発者の間の意思疎通 として利⽤される⽤語によって モデリングと設計を⾏う • 戦略的な設計のためのガイドライン 境界づけられたコンテキスト、 蒸留、⼤規模な構造の考察 • マイクロサービスによって再度注⽬ される

Slide 11

Slide 11 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. モデル駆動設計を構成する⾔語のナビゲーションマップ モデル駆動設計 サービス エンティティ 値オブジェクト ファクトリ 集約 リポジトリ 利⼝なUI レイヤ化 アーキテクチャ

Slide 12

Slide 12 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 境界づけられたコンテキスト • 境界づけられたコンテキストは特定の モデルを適⽤できる限定された範囲 • コンテキストの境界を定めることで、 チームメンバーは何を⼀致させるべきで 何を独⽴して開発できるのかについての 理解を明確化し、共有できる https://www.martinfowler.com/bliki/BoundedContext.html Customer Ticket Product Product version Customer Product Territory Opportunity Pipeline Salesperson Defect Sales context Support context

Slide 13

Slide 13 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ヘキサゴナルアーキテクチャとは

Slide 14

Slide 14 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ヘキサゴナルアーキテクチャとは • ヘキサゴナルアーキテクチャは、別名 ポートとアダプタ アーキテクチャとも⾔われる • ポートとアダプタによってアプリケーションのコンポーネントが容易 に環境との間で疎結合に結ばれるソフトウェア設計のアーキテクチャ パターン https://alistair.cockburn.us/hexagonal-architecture/ • オリジナルは Dr. Alistair Cockburn が提唱 https://alistair.cockburn.us/coming-soon/

Slide 15

Slide 15 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Hexagonal Architecture Domain Model Ports Adapters Primary Actor Secondary Actor HTTP Request Event Message Queue … File Storage Database Queue …

Slide 16

Slide 16 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Hexagonal Architecture Domain Model Ports Adapters Primary Actor Secondary Actor HTTP Request Event Message Queue … File Storage Database Queue … アプリケーションはポートによって接続される アダプタは外界との糊の役⽬を果たす アプリケーションの外側にあってアプリケーションを駆動する側を プライマリアクター、駆動される側をセカンダリーアクターと呼ぶ

Slide 17

Slide 17 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. サンプルプログラムの紹介

Slide 18

Slide 18 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. aws-samples/aws-lambda-domain-sample GitHubでサンプルプロジェクトを公開 https://github.com/aws-samples/aws-lambda-domain-model- sample 本⽇はこちらのコードを解説 します︕

Slide 19

Slide 19 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. サンプルドメインモデル • できるだけシンプルで理解しやすいようにするため、⼀部の 機能のみを実装 • ワクチン予約システム – ワクチン予約者は空いている枠を検索してワクチン接種の予約を⾏うことが できる – ワクチン予約者は空き枠がない場合は予約ができない – ワクチン予約者は2つまでの予約を⾏うことができる – ワクチン予約者は同⼀⽇時に2つの予約を取ることはできない

Slide 20

Slide 20 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. サンプルドメインモデル(ワクチン予約システム) Recipient recipient_id: str email: str first_name: str last_name: str age: int add_reserve_slot(slot): void Slot slot_id: str reservation_date: datetime location: str vacant: bool is_vacant(): bool use_slot(): void

Slide 21

Slide 21 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. サンプルアーキテクチャ AWS Cloud Amazon API Gateway AWS Lambda Amazon DynamoDB User Internet

Slide 22

Slide 22 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Demo: コード参照

Slide 23

Slide 23 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ドメインモデルクラス class Recipient: def __init__(self, recipient_id:str, email:str, first_name:str, last_name:str, age:int): self.__recipient_id = recipient_id self.__email = email self.__first_name = first_name self.__last_name = last_name self.__age = age self.__slots = [] @property def recipient_id(self): return self.__recipient_id ..... def are_slots_same_date(self, slot:Slot) -> bool: for selfslot in self.__slots: if selfslot.reservation_date == slot.reservation_date: return True return False def is_slot_counts_equal_or_over_two(self) -> bool: ..... ドメインモデルで定義されたピュアなビジネス ロジックのみが実装される。 外部の世界に対する知識を持たない。

Slide 24

Slide 24 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ポートクラス(from Primary) class RecipientInputPort(IRecipientInputPort): def __init__(self, recipient_output_port: IRecipientOutputPort, slot_output_port: ISlotOutputPort): self.__recipient_output_port = recipient_output_port self.__slot_output_port = slot_output_port def make_reservation(self, recipient_id:str, slot_id:str) -> Status: status = None recipient = self.__recipient_output_port.get_recipient_by_id(recipient_id) slot = self.__slot_output_port.get_slot_by_id(slot_id) ..... # --------------------------------------------------- # execute domain logic # --------------------------------------------------- ret = recipient.add_reserve_slot(slot) ..... if ret == True: status = Status(200, "The recipient's reservation is added.") else: status = Status(200, "The recipient's reservation is NOT added!") return status

Slide 25

Slide 25 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ポートクラス(from Primary) class RecipientInputPort(IRecipientInputPort): def __init__(self, recipient_output_port: IRecipientOutputPort, slot_output_port: ISlotOutputPort): self.__recipient_output_port = recipient_output_port self.__slot_output_port = slot_output_port def make_reservation(self, recipient_id:str, slot_id:str) -> Status: status = None recipient = self.__recipient_output_port.get_recipient_by_id(recipient_id) slot = self.__slot_output_port.get_slot_by_id(slot_id) ..... # --------------------------------------------------- # execute domain logic # --------------------------------------------------- ret = recipient.add_reserve_slot(slot) ..... if ret == True: status = Status(200, "The recipient's reservation is added.") else: status = Status(200, "The recipient's reservation is NOT added!") return status IRecipientInputPort(抽象クラス)を継承 コンストラクタで関連するポートクラスのインスタンスを 受け取る

Slide 26

Slide 26 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. インターフェイスクラス( from Primary) from abc import ABCMeta, abstractmethod from status import Status class IRecipientInputPort(metaclass=ABCMeta): @abstractmethod def make_reservation(self, recipient_id:str, slot_id:str) -> Status: raise NotImplementedError()

Slide 27

Slide 27 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. プライマリのアダプタはLambda • アダプタの役割は外部からのリクエストを内部アプリケーションに 伝えるための変換を⾏うこと • API Gatewayからの呼び出し(HTTPS)をポートクラスの呼び出しに 変換する役割はLambdaランタイムが⾏なっている

Slide 28

Slide 28 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ポートクラス(To Secondary) class RecipientOutputPort(IRecipientOutputPort): def __init__(self, adapter:IRecipientAdapter): self.__adapter = adapter def get_recipient_by_id(self, recipient_id:str) -> Recipient: return self.__adapter.load(recipient_id) def add_reservation(self, recipient:Recipient) -> bool: return self.__adapter.save(recipient) IRecipientOutputPort(抽象クラス)を継承

Slide 29

Slide 29 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. アダプタクラス(To Secondary) class DDBRecipientAdapter(IRecipientAdapter): def __init__(self): ddb = boto3.resource('dynamodb') self.__table = ddb.Table(table_name) def load(self, recipient_id:str) -> Recipient: try: response = self.__table.get_item( Key={'pk': pk_prefix + recipient_id}) ... def save(self, recipient:Recipient) -> bool: try: item = { "pk": pk_prefix + recipient.recipient_id, "email": recipient.email, "first_name": recipient.first_name, "last_name": recipient.last_name, "age": recipient.age, "slots": [] } ... IRecipientAdapter(抽象クラス)を継承 このクラスはAmazon DynamoDBへのアクセス を実装するアダプター

Slide 30

Slide 30 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. app.py def get_recipient_input_port(): return RecipientInputPort( RecipientOutputPort(DDBRecipientAdapter()), SlotOutputPort(DDBSlotAdapter())) def lambda_handler(event, context): body = json.loads(event['body']) recipient_id = body['recipient_id'] slot_id = body['slot_id'] # get an input port instance recipient_input_port = get_recipient_input_port() status = recipient_input_port.make_reservation(recipient_id, slot_id) return { "statusCode": status.status_code, "body": json.dumps({ "message": status.message }), } コンクリートクラスのインスタンスをコンストラクタ に渡して、RecipientInputPortクラスのインスタンスを ⽣成するファクトリ アダプター(Lambda)からポートへの呼び出し

Slide 31

Slide 31 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 依存関係の注⼊ RecipientInputPort RecipientOutputPort IRecipientOutputPort SlotOutputPort ISlotOutputPort DDBRecipientAdapter IRecipientAdapter DDBSlotAdapter ISlotAdapter

Slide 32

Slide 32 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. クラス図

Slide 33

Slide 33 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Demo: 実際の動作確認

Slide 34

Slide 34 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Lambda 関数のユニットテスト

Slide 35

Slide 35 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Testing Pyramid 35

Slide 36

Slide 36 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Testing Pyramid 36 Pure

Slide 37

Slide 37 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. サーバーレスのUnit Testは純粋なロジックに対して実施

Slide 38

Slide 38 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Unit Test対象の関数をTestableにする • 関数をTestable(テスト可能)にするとは︖ • y = f(x) の形にする • xに特定の値を与えるとyの値が決まる # add関数 def add(a, b): return a + b def test_add(): #unit test expected = 10 #期待する値 assert expected == add(5, 5)

Slide 39

Slide 39 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Pure Logicに対するUnite Test • 純粋な要件に対してテストケースを書く 他のAWSサービスとの結合⽅法に対する知識を含まない • テストが軽くなるため、開発やデリバリーを⾼速化できる • 外部のアーキテクチャが変更された時にも、テストケースが影響を 受けにくい テストの陳腐化が防げる テストのメンテナンスコストの軽減 • Pure Logicなので意図が伝わりやすい、理解しやすい テストケースから仕様を理解出来る

Slide 40

Slide 40 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Demo: ユニットテスト

Slide 41

Slide 41 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ドメインモデルクラスに対するユニットテストは容易 def test_add_slot_one(fixture_recipient, fixture_slot): slot = fixture_slot target = fixture_recipient target.add_reserve_slot(slot) assert slot != None assert target != None assert 1 == len(target.slots) assert slot.slot_id == target.slots[0].slot_id assert slot.reservation_date == target.slots[0].reservation_date assert slot.location == target.slots[0].location assert False == target.slots[0].is_vacant def test_add_slot_two(fixture_recipient, fixture_slot, fixture_slot_2): ..... def test_cannot_append_slot_more_than_two(fixture_recipient, fixture_slot, fixture_slot_2, fixture_slot_3): ..... def test_cannot_append_same_date_slot(fixture_recipient, fixture_slot): ..... ドメインモデルのピュアなビジネスロジックに対する ユニットテストは容易

Slide 42

Slide 42 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ポートクラスに対してはダミークラスの注⼊ class DummyRecipientAdapter(IRecipientAdapter): def load(self, recipient_id:str) -> Recipient: return Recipient(recipient_id, email, first_name, last_name, age) def save(self, recipient:Recipient) -> bool: return True @pytest.fixture() def fixture_recipient_output_port(): #SetUp recipient_output_port = RecipientOutputPort(DummyRecipientAdapter()) #execute testing yield recipient_output_port #TearDown recipient_output_port = None def test_recipient_port_recipient_by_id(fixture_recipient_output_port): target = fixture_recipient_output_port recipient_id = "dummy_number" recipient = target.get_recipient_by_id(recipient_id) assert recipient != None .....

Slide 43

Slide 43 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ポートクラスに対してはダミークラスの注⼊ class DummyRecipientAdapter(IRecipientAdapter): def load(self, recipient_id:str) -> Recipient: return Recipient(recipient_id, email, first_name, last_name, age) def save(self, recipient:Recipient) -> bool: return True @pytest.fixture() def fixture_recipient_output_port(): #SetUp recipient_output_port = RecipientOutputPort(DummyRecipientAdapter()) #execute testing yield recipient_output_port #TearDown recipient_output_port = None def test_recipient_port_recipient_by_id(fixture_recipient_output_port): target = fixture_recipient_output_port recipient_id = "dummy_number" recipient = target.get_recipient_by_id(recipient_id) assert recipient != None ..... 抽象クラスを継承したダミークラス Fixtureでターゲットクラスにダミークラスを注⼊して インスタンス化 ターゲットクラスのロジックをユニットテスト で確認

Slide 44

Slide 44 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ローカルで実⾏確認するためのmain.py def get_recipient_input_port(): return RecipientInputPort( RecipientOutputPort(DDBRecipientAdapter()), SlotOutputPort(DDBSlotAdapter())) def main(): recipient_input_port = get_recipient_input_port() status = recipient_input_port.make_reservation("1", "1") print(f"status_code: {status.status_code}, message: {status.message}") if __name__ == "__main__": main() app.pyと同じファクトリー。ダミークラスに置き換える ことも可能 以下のコマンドでローカルマシンから実⾏し、動作確認を⾏う ことが可能 $ python main.py

Slide 45

Slide 45 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. まとめ

Slide 46

Slide 46 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. まとめ § Lambdaでドメインモデルをどのように実装すれば良いのか → DDDのモデルをクラスに実装することは同じ。外部の知識を持たせない § Lambda関数のユニットテストはどのようにすれば良いのか → ドメインモデルクラスはピュアなビジネスロジックのテストが容易。 ポートクラスにはダミークラスを注⼊してテストを容易に § AWSの他のサービスと連携する必要があるが、ドメインモデルはクリーンに 保ちたい → Hexagonal Architectureを利⽤してドメインロジックと外部サービスの間を 疎結合に保つ

Slide 47

Slide 47 text

Thank you! © 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved.

Slide 48

Slide 48 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Appendix

Slide 49

Slide 49 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Advanced Topic

Slide 50

Slide 50 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Lambda関数をどの単位で分割するのか • 1つのサブドメイン全体を1つの関数にする︖ • 個別にスケールしたいビジネスロジック単位に関数にする︖ • それともAPIごとにLambda関数にする︖

Slide 51

Slide 51 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コンテキストマップ Sales context Support context Marketing context 境界つけられたコンテキストだけでは、ドメインの 全体像を⽰すことはできない コンテキストマップは、境界づけられたコンテキスト を統合することにより、異なるが関連するユビキタス ⾔語のマッピングを処理する DDDでは境界づけられたコンテキストを統合するため の7つのパターンを説明 • 共有カーネル (Shared Kernel) • 顧客/供給者の開発チーム (Customer/Supplier Development Teams) • 順応者 (Conformist) • 腐敗防⽌層 (Anticorruption layer) • 別々の道 (Separate ways) • 公開ホストサービス (Open/Host service) • 公表された⾔語 (Published language)

Slide 52

Slide 52 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. パブリッシュ/サブスクライブ • 特定ドメイン イベントのための論理的な発⾏者︓ ⼀貫した境界を適⽤する • 複数種類のカップリングに対応 • 個々のサブスクライバは個別の 境界づけられたコンテキストで ドメイン イベントに反応 • ドメインモデルのシンプルで効果的な ドメインイベントの発⾏⽅法︓軽量なオブザーバーパターン

Slide 53

Slide 53 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. event [i-ʼvent] 名詞 状態が変更されたことを⽰す シグナル

Slide 54

Slide 54 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベントとは • イベントは、状態の変化に対してサービス間で情報を共有するための 主要なメカニズムになる • イミュータブル – 過去は変更できない。コピーすることで容易に スケール • イベントは意味論的な意図を持ち過去時制の動詞として表現される 例︓ “customer_created” • 軽量で、”customer_id”のような境界コンテキストをまたがる共通の プロパティによって関連付けられる

Slide 55

Slide 55 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベントは観察可能であり、指⽰ではない コマンドの指⽰ 請求書を 発⾏して ください。 承知 しました イベントの観察 Xさんが今 ⼩物を注⽂ しました 請求書を 送ります セールスレポート に追加します。

Slide 56

Slide 56 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベントルーターによるサービスの疎結合化 プロデューサとコンシューマ を抽象化 イベントの選択とフィルタ

Slide 57

Slide 57 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. ⾮同期化による応答性の改善と依存性の削減 同期コマンド Client Service A Service B ⾮同期イベント Client Service A Service B

Slide 58

Slide 58 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベントストアによる弾⼒性の改善と スケーラビリティ サービスが処理するまでメッセージ をバッファリング イベントの発⾏ イベントストア イベントの購読 Business logic

Slide 59

Slide 59 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 信頼性、弾⼒性、独⽴したスケール性 注⽂ サービス 請求 サービス 倉庫管理 サービス 販売予測 サービス プロデューサ コンシューマ イベント ルーター ルーティング/ フィルタ/ ルール キュー キュー キュー

Slide 60

Slide 60 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベント駆動デザインパターン

Slide 61

Slide 61 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベント駆動デザインパターン • コレオグラフィ • 全体の作業を制御する指揮者は存在せず、個々のサービスに予め与えられた 動作条件に従ってサービスを実⾏ • コマンドクエリ責務分離 • データ ストアの読み取り操作と更新操作を分離

Slide 62

Slide 62 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コレオグラフィーパターンとは • 必要なすべての情報を含んだ最初のイベントを 1 つのメッセージに 保存して、最初のトランザクションを完了 • 他のサービスがそのメッセージを⾮同期的に取得し、それぞれの タスクを完了させる • サービスが疎結合になり、直接互いに影響を与えない • メッセージの保存と取得が⾮同期の関係になり、スケーラビリティと 信頼性が向上

Slide 63

Slide 63 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コレオグラフィーパターン ユーザー リクエストサービス リクエストキュー サービスA サービスB サービスC

Slide 64

Slide 64 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 実装例1: SNS、 SQS、 Lambdaの利⽤ ユーザー リクエストサービス Amazon SNS Amazon SQS Amazon SQS Amazon SQS AWS Lambda AWS Lambda AWS Lambda ファンアウト

Slide 65

Slide 65 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 実装例2: Amazon EventBridgeの利⽤ 配送 ポイント プレミアム会員 カート ⽀払 ⽀払い⽅法 認証 注⽂ 1分毎に実⾏ Events 会員 ステータス ⽀払認証 カートに ⼊れる 注⽂完了 Amazon EventBridge リクエストサービス イベントバス

Slide 66

Slide 66 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コマンドクエリ責務分離パターンとは • コマンドクエリ責務分離(Command-Query Responsibility Segregation︓CQRS)パターンはデータを更新するコマンドと参照 するクエリを分離することで、ユースケースに応じて個別にスケール することを可能とするパターン • データソースを分離することで異なるデータ構造を取ることも可能と なり、クエリ側はクエリで返すデータ転送オブジェクト(Data Transfer Object : DTO)に合わせた形のスキーマとすることで オブジェクト関係マッピング(Object Relational Mapping : ORM) のオーバーヘッドを軽減することも可能になる • コマンド側とクエリ側が結果整合性を許容する必要がある

Slide 67

Slide 67 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コマンドクエリ責務共有パターン ユーザー サービス ドメインモデル ドメインモデル ORM ORM データ転送 オブジェクト クエリ 更新

Slide 68

Slide 68 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. コマンドクエリ責務分離(CQRS)パターン ユーザー コマンドサービス ドメインモデル データ転送 オブジェクト クエリ 更新 クエリサービス データ転送 オブジェクト ⾮同期 結果整合

Slide 69

Slide 69 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 実装例1︓ Kinesis Data StreamsとLambdaを 利⽤ フロントサービス Amazon Kinesis Data Streams AWS Lambda AWS Lambda Amazon Aurora 正規化テーブル ユーザー Amazon DynamoDB ⾮正規化テーブル

Slide 70

Slide 70 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. 実装例2︓ DynamoDB StreamsとLambdaを 利⽤ ユーザー Amazon DynamoDB Streams ⾮正規化テーブル AWS Lambda Amazon Aurora 正規化テーブル フロントサービス

Slide 71

Slide 71 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. イベントドリブンアーキテクチャ 選択における観点 Design Considerations

Slide 72

Slide 72 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. AWSのメッセージングとイベントサービス イベントストア イベントルーター キュー ストリーム トピック イベントバス AWSネイティブ マネージド オープンソース Amazon SQS Amazon MQ Amazon SNS Amazon MQ Amazon Kinesis Amazon MSK Amazon EventBridge

Slide 73

Slide 73 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. Amazon SQSによるハイボリュームなビッグデータ処理 AWS Cloud Amazon S3 AWS Lambda Amazon SQS AWS Lambda Amazon SNS Amazon SQS AWS Lambda Amazon SQS AWS Lambda Amazon SQS AWS Lambda Dead Letter Queue File Create/Upload Trigger Sending Message Receive Message Send Topic Subscribe Topic イベント情報をキュー に保存する責務 キューの情報とトピック として送信する責務 個別のドメインロジック を実⾏する責務

Slide 74

Slide 74 text

© 2022, Amazon Web Services, Inc. or its affiliates. All rights reserved. その他の参考情報 • https://speakerdeck.com/_kensh/serverless-testing-2021 • https://speakerdeck.com/_kensh/serverless-testing