$30 off During Our Annual Pro Sale. View Details »

ヘキサゴナルアーキテクチャを利用したLambda 関数のドメインモデルの実装 Live

ヘキサゴナルアーキテクチャを利用したLambda 関数のドメインモデルの実装 Live

ヘキサゴナルアーキテクチャを利用したLambda 関数のドメインモデルの実装 Live
AWS Summit 2022 Developer Zone (dev-09) セッション資料です。

Atsushi Fukui

May 27, 2022
Tweet

More Decks by Atsushi Fukui

Other Decks in Technology

Transcript

  1. © 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

    View Slide

  2. © 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

    View Slide

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

    View Slide

  4. © 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) チーム
    ソリューションアーキテクト
    Ø 好きなサービス

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. © 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

    View Slide

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

    View Slide

  14. © 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/

    View Slide

  15. © 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

    View Slide

  16. © 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

    アプリケーションはポートによって接続される
    アダプタは外界との糊の役⽬を果たす
    アプリケーションの外側にあってアプリケーションを駆動する側を
    プライマリアクター、駆動される側をセカンダリーアクターと呼ぶ

    View Slide

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

    View Slide

  18. © 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
    本⽇はこちらのコードを解説
    します︕

    View Slide

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

    View Slide

  20. © 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

    View Slide

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

    View Slide

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

    View Slide

  23. © 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:
    .....
    ドメインモデルで定義されたピュアなビジネス
    ロジックのみが実装される。
    外部の世界に対する知識を持たない。

    View Slide

  24. © 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

    View Slide

  25. © 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(抽象クラス)を継承
    コンストラクタで関連するポートクラスのインスタンスを
    受け取る

    View Slide

  26. © 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()

    View Slide

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

    View Slide

  28. © 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(抽象クラス)を継承

    View Slide

  29. © 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へのアクセス
    を実装するアダプター

    View Slide

  30. © 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)からポートへの呼び出し

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. © 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)

    View Slide

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

    View Slide

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

    View Slide

  41. © 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):
    .....
    ドメインモデルのピュアなビジネスロジックに対する
    ユニットテストは容易

    View Slide

  42. © 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
    .....

    View Slide

  43. © 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でターゲットクラスにダミークラスを注⼊して
    インスタンス化
    ターゲットクラスのロジックをユニットテスト
    で確認

    View Slide

  44. © 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. © 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. © 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
    ファンアウト

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. © 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
    ⾮正規化テーブル

    View Slide

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

    View Slide

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

    View Slide

  72. © 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

    View Slide

  73. © 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
    イベント情報をキュー
    に保存する責務
    キューの情報とトピック
    として送信する責務
    個別のドメインロジック
    を実⾏する責務

    View Slide

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

    View Slide