Slide 1

Slide 1 text

データの整合性を保つ
 非同期処理アーキテクチャパターン 
 2025.2.14


Slide 2

Slide 2 text

自己紹介
 2

Slide 3

Slide 3 text

3 SmartBank, Inc. 
 Engineer 
 木田 悠一郎
 @mokuo_
 @mokuo
 SIer : 基幹業務システム → Rails 受託開発
 → Sansan株式会社 : データを扱う部署で複数システム
 → 株式会社スマートバンク : カード決済 / あとばらい領域


Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

6 6

Slide 7

Slide 7 text

7 7

Slide 8

Slide 8 text

本日お話しすること
 8

Slide 9

Slide 9 text

本日お話しすること
 一連のイベント から構成される業務
 9 例) B/43 カード発行処理のイメージ(実際のフローとは異なります)
 非同期処理 を含むこともある


Slide 10

Slide 10 text

ç ç ç 10 本日お話しすること
 処理の流れ が
 把握し辛い
 変更を行うのが
 難しい
 データ整合性の
 担保が困難
 直面する課題


Slide 11

Slide 11 text

11 本日お話しすること
 適切に設計を行うことで、
 これらの課題を回避することができます
 本日は、上記の課題を解消する
 アーキテクチャのパターン を紹介します
 ❏ 処理の流れが把握し辛い
 ❏ 変更を行うのが難しい
 ❏ データ整合性の担保が困難


Slide 12

Slide 12 text

12 アジェンダ
 1. イベントを正しく保存する
 2. イベント間の整合性を保つ
 3. データベースとメッセージキューの整合性を保つ
 4. メッセージのスキーマを定義する
 ポイントは4つ!


Slide 13

Slide 13 text

イベントを正しく保存する
 13

Slide 14

Slide 14 text

14 非同期処理の前後では何らかのデータを
 保存することが多い
 モノリスでもマイクロサービスでも、
 まずはしっかりモデリング を行うことが大切
 イベントを正しく保存する


Slide 15

Slide 15 text

イベントを正しく保存する
 ● イベント
 ○ 日付を生成条件としているモノ 
 ● リソース
 ○ イベント以外のモノ
 モノを2種類に分ける
 15 出典: 事業分析・データ設計のためのモデル作成技術入門:書籍案内|技術評論社 https://gihyo.jp/book/2022/978-4-297-12946-0


Slide 16

Slide 16 text

16 イベントを正しく保存する
 ● 出来事の結果として生起した、持続するモノに帰属している日付
 ○ ❌ 従業員入社日、部門設立日など
 ● データ管理のための将来日
 ○ ❌ 有効日、適用日など
 ● データ管理のための過去日
 ○ ❌ 登録日、更新日など
 イベントに帰属する日付は事象が生起した過去日
 ※ 以下はイベントの日付ではない
 = 受注日、発送日、請求日、入金日 など


Slide 17

Slide 17 text

イベントを正しく保存する
 ※ 便宜上、実際のカード発行フローを簡略化したものになっていますので、ご注意ください
 17

Slide 18

Slide 18 text

イベント毎にテーブルを作成する
 イベントを正しく保存する
 関連イベントID の持ち方にはいくつかのパターン があります
 18

Slide 19

Slide 19 text

● 👍 メリット
 ○ イベントの順序が明確になり、外部キー制約などを活用できる
 ● 👎 デメリット 
 ○ イベントの関連を1つずつ辿っていく必要がある
 パターン① : 1つ前のイベントのIDを持つ
 19 イベントを正しく保存する


Slide 20

Slide 20 text

※ 便宜上、実際のカード発行フローを簡略化したものになっていますので、ご注意ください
 フローが分岐する場合
 20 イベントを正しく保存する


Slide 21

Slide 21 text

パターン② : 最初のイベントのIDを持つ
 21 ● 👍 メリット
 ○ イベント分岐に対応できる、関連イベントを一気に引ける
 ● 👎 デメリット 
 ○ イベントの順序をテーブルだけで表現できない
 イベントを正しく保存する


Slide 22

Slide 22 text

※ 便宜上、実際のカード発行フローを簡略化したものになっていますので、ご注意ください
 複雑なフローの場合 
 
 22 イベントを正しく保存する


Slide 23

Slide 23 text

パターン③ : ロングタームイベント
 23 最新のステータス 
 各イベントのスーパータイプ 
 ● 👍 メリット
 ○ 最新のステータスを管理できる、複雑なイベント分岐に対応可能
 ● 👎 デメリット 
 ○ テーブルだけでイベント順序を表現できない
 イベントを正しく保存する


Slide 24

Slide 24 text

『WEB+DB PRESS Vol.130』の特集1
 第4章で紹介されているパターン
 パターン③ : 
 ロングタームイベント
 出典: WEB+DB PRESS Vol.130|技術評論社 https://gihyo.jp/magazine/wdpress/archive/2022/vol130
 24 イベントを正しく保存する


Slide 25

Slide 25 text

25 パターン④ : ツリー構造で持つ
 イベントを正しく保存する
 辿ってきたイベントのIDをスラッシュ区切りでカラムに持たせる
   例) 入金待ち   : カード発行申請ID/入金待ちID
   例) カード発行審査: カード発行申請ID/入金待ちID/カード発行審査ID


Slide 26

Slide 26 text

● 👍 メリット
 ○ 関連するイベントを柔軟に引くことができる
 ● 👎 デメリット 
 ○ ライブラリがない言語では実装にコストがかかる
 Ruby では ancestry という gem があり
 簡単に導入することができる
 出典: stefankroes/ancestry: Organise ActiveRecord model into a tree structure https://github.com/stefankroes/ancestry
 26 パターン④ : 
 ツリー構造で持つ
 イベントを正しく保存する


Slide 27

Slide 27 text

書籍『SQL アンチパターン』で 経路列挙モデル として紹介されている手法
 パターン④ : 
 ツリー構造で持つ
 27 出典: SQLアンチパターン - O'Reilly Japan https://www.oreilly.co.jp/books/9784873115894/
 イベントを正しく保存する


Slide 28

Slide 28 text

   関連するイベントの数や複雑さに応じて、
 
 
 
   の順に検討してみる!
 パターンまとめ
 28 パターン① : 1つ前のイベントのIDを持たせる
 パターン② : 最初のイベントのIDを持たせる
 パターン③ : ロングタームイベント
 要件によって 
 ”パターン④:ツリー構造で持たせる”パターンも検討すると良い
 イベントを正しく保存する


Slide 29

Slide 29 text

複数の責務、概念が混ざり合っている
 
 null カラムが生じる
 イベントを正しく保存する
 NGパターン: 全部盛りモデル
 29 カード発行
 PK
 ID
 ユーザーID
 カードID
 ステータス
 カード名義
 配送先
 申請日時
 発行日時
 発送日時
 利用開始日時


Slide 30

Slide 30 text

イベントを正しく保存する
 ● 仕様変更に対応しやすくなる
 ○ 例) 複数カードまとめて発行したい
 Q. テーブルが増えると
   複雑になるのでは?
 30

Slide 31

Slide 31 text

イベントを正しく保存する
 ● 複雑さを分けて考える
 ○ 一つのモデルに含まれる複雑さは分割す ることで回避できる 
 ○ 複数のモデルが持つ複雑さは業務本来 が持つ複雑さなので回避できない 
 Q. テーブルが増えると
   複雑になるのでは?
 31

Slide 32

Slide 32 text

イベントを正しく保存する
 ● テーブルを更新する際はロックで排他 制御を行う必要がある
 ○ 更新が必要なテーブルと不要な テーブルを分けることでロックの開 放待ち発生しにくくなる
 Q. JOIN が増えてクエリが  遅くならない?
 32

Slide 33

Slide 33 text

モデリングについては、こちらも合わせてご参照ください
 33 https://speakerdeck.com/mokuo/shui-gazuo-cheng-sitemo1tunogou-zao-ninarumoderinguzuo-cheng-ji-shu-theory-of-models-nimeng-wojian-ru


Slide 34

Slide 34 text

イベント間の整合性を保つ
 34

Slide 35

Slide 35 text

35 非同期メッセージングを導入する目的
 可用性
 を上げる
 パフォーマンス 
 を上げる
 イベント間の整合性を保つ


Slide 36

Slide 36 text

「可用性」 の定義
 
 ” システムがどれくらいの期間利用できるか 
 (24 時間 365 日稼働する場合には、障害が発 生した場合に迅速にシステムを稼働できるよう にするための措置が必要となる) ”
 
 出典:ソフトウェアアーキテクチャの基礎 - O'Reilly Japan https://www.oreilly.co.jp//books/9784873119823/
 36 イベント間の整合性を保つ


Slide 37

Slide 37 text

37 例) B/43 システムアーキテクチャ
 イベント間の整合性を保つ


Slide 38

Slide 38 text

例) カード発行処理を簡略化して説明
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 38 イベント間の整合性を保つ


Slide 39

Slide 39 text

ユーザーに待ち時間が発生
 39 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 同期通信の場合
 イベント間の整合性を保つ


Slide 40

Slide 40 text

カード管理システムで障害が発生
 した場合、依存する機能が停止 する
 40 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 同期通信の場合
 イベント間の整合性を保つ


Slide 41

Slide 41 text

41 裏側で非同期通信を行うことで、
 パフォーマンス と可用性が向上する
 
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 非同期通信の場合
 非同期
 イベント間の整合性を保つ


Slide 42

Slide 42 text

42 裏側で非同期通信を行うことで、
 パフォーマンス と可用性が向上する
 
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 非同期通信の場合
 非同期
 ⚠ ただし、デメリット もある
 イベント間の整合性を保つ


Slide 43

Slide 43 text

43 DBトランザクションの
 ACID 特性
 ● 原子性(Atomicity)
 ○ 全て成功 or 失敗
 ● 一貫性(Consistency)
 ○ 全ての制約が満たされた状態
 ● 独立性(Isolation)
 ○ 直列に実行した場合と結果が同じ
 ● 耐久性(Durability)
 ○ コミット完了したデータが消失しない
 出典:理論から学ぶデータベース実践入門 ―― リレーショナルモデルによる効率的な SQL|技術評論社 https://gihyo.jp/book/2015/978-4-7741-7197-5
 
 イベント間の整合性を保つ


Slide 44

Slide 44 text

44 どちらか一方のみ失敗することがある
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 非同期通信の場合
 非同期
 イベント間の整合性を保つ


Slide 45

Slide 45 text

ç ç ç 45 リトライ
 最初から
 実行し直す 
 ロールバック 
 (もとに戻す)
 非同期処理に失敗した時、以下のいずれかの対応を行う必要がある
 イベント間の整合性を保つ


Slide 46

Slide 46 text

46 イベント間の整合性を保つ
 出典:Saga パターン - Azure Design Patterns | Microsoft Learn https://learn.microsoft.com/ja-jp/azure/architecture/patterns/saga
 ● コレオグラフィ
 ● オーケストレーション
 整合性担保のための2つのアプローチ
 マイクロサービス における概念だが、モノリスでも参考にできる


Slide 47

Slide 47 text

47 ● 👍 メリット
 ○ 調整ロジックが不要
 ○ 単純なフローに適している
 ● 👎 デメリット 
 ○ ステップが増えると複雑になる
 コレオグラフィ
 イベント間の整合性を保つ
 出典:Saga パターン - Azure Design Patterns | Microsoft Learn https://learn.microsoft.com/ja-jp/azure/architecture/patterns/saga


Slide 48

Slide 48 text

48 オーケストレーション
 イベント間の整合性を保つ
 出典:Saga パターン - Azure Design Patterns | Microsoft Learn https://learn.microsoft.com/ja-jp/azure/architecture/patterns/saga
 ● 👍 メリット
 ○ 全体を管理しやすい
 ○ 複雑なフローに適している
 ● 👎 デメリット 
 ○ オーケストレーターにロジックが集中する


Slide 49

Slide 49 text

49 オーケストレーション
 イベント間の整合性を保つ
 出典:Saga パターン - Azure Design Patterns | Microsoft Learn https://learn.microsoft.com/ja-jp/azure/architecture/patterns/saga
 オーケストレーターとして
 マネージドサービスを導入する
 ● AWS
 ○ AWS Step Functions
 ● GCP
 ○ Workflows
 ● Azure
 ○ Azure Logic Apps


Slide 50

Slide 50 text

50 オーケストレーション
 イベント間の整合性を保つ
 出典 : chaps-io/gush: Fast and distributed workflow runner using ActiveJob and Redis https://github.com/chaps-io/gush
 ちなみに・・・
 Ruby には gush という gem があり、
 Redis か ActiveJob を使ってワークフローを組 むことができる


Slide 51

Slide 51 text

オーケストレーターとして使えるマネージドサービス
  ※ 右図はイメージです
   実際の B/43 カード発行フローとは異なるためご注意ください
 51 例) AWS Step Functions
 ECS タスク実行や SQS メッセージ送信なども 可能


Slide 52

Slide 52 text

オーケストレーターとして使えるマネージドサービス
 AWS マネジメントコンソールで実行状況が分か りやすく表示される
 52 例) AWS Step Functions
  ※ 右図はイメージです
   実際の B/43 カード発行フローとは異なるためご注意ください


Slide 53

Slide 53 text

オーケストレーターとして使えるマネージドサービス
 53 例) AWS Step Functions
 ● どこでエラーになったか分かる
 ● リトライやエラーハンドリング 可能
  ※ 右図はイメージです
   実際の B/43 カード発行フローとは異なるためご注意ください


Slide 54

Slide 54 text

データベースとメッセージキューの
 整合性を保つ
 54

Slide 55

Slide 55 text

55 DBトランザクションの
 ACID 特性
 ● 原子性(Atomicity)
 ○ 全て成功 or 失敗
 ● 一貫性(Consistency)
 ○ 全ての制約が満たされた状態
 ● 独立性(Isolation)
 ○ 直列に実行した場合と結果が同じ
 ● 耐久性(Durability)
 ○ コミット完了したデータが消失しない
 出典:理論から学ぶデータベース実践入門 ―― リレーショナルモデルによる効率的な SQL|技術評論社 https://gihyo.jp/book/2015/978-4-7741-7197-5
 
 データベースとメッセージキューの整合性を保つ


Slide 56

Slide 56 text

56 DB にデータを保存しつつ
 メッセージを送信したいことがある
 
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 非同期通信の場合
 非同期
 データベースとメッセージキューの整合性を保つ


Slide 57

Slide 57 text

DB トランザクションとメッセージ送信はどちら か一方だけ失敗する可能性がある
 57 データベースとメッセージキューの整合性を保つ


Slide 58

Slide 58 text

DB トランザクションとメッセージ送信はどちら か一方だけ失敗する可能性がある
 58 データベースとメッセージキューの整合性を保つ


Slide 59

Slide 59 text

DB トランザクションとメッセージ送信はどちら か一方だけ失敗する可能性がある
 59 データベースとメッセージキューの整合性を保つ


Slide 60

Slide 60 text

60 システム間で整合性が 取れない状態になる
 カード管理
 システム
 アプリ
 バックエンド
 家計簿
 アプリ
 どちらか一方のみ失敗
 非同期
 データベースとメッセージキューの整合性を保つ


Slide 61

Slide 61 text

データベースとメッセージキューの整合性を保つ
 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063
 61 “ データベーストランザクションの一部として データベースの OUTBOX テーブルにイベン トやメッセージを保存することによってメッ セージをパブリッシュする “ (https://microservices.io/patterns/data/transactional- outbox.html 参照)
 Transactional outbox
 パターン


Slide 62

Slide 62 text

62 OUTBOX テーブルを読み、メッセージ をパブリッシュする方法として、さらに2 つのパターンがある Transactional outbox
 パターン
 データベースとメッセージキューの整合性を保つ
 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063


Slide 63

Slide 63 text

ワーカー常時起動、バッチ定期実行など
 63 “ データベースの OUTBOX テーブルを ポーリングしてメッセージをパブリッシュす る ” (https://microservices.io/patterns/data/polling-pub lisher.html 参照)
 パターン① : 
 Polling publisher
 OUTBOX テーブルからメッセージをパブリッシュする方法
 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063


Slide 64

Slide 64 text

64 “ トランザクションのログをテーリングして データベースに加えられた変更をパブリッ シュする ” (https://microservices.io/patterns/data/transaction -log-tailing.html 参照)
 パターン② : 
 Transaction log tailing
 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063
 例) MySQL の binlog、
   PostgresQL の WAL など
 OUTBOX テーブルからメッセージをパブリッシュする方法


Slide 65

Slide 65 text

65 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063
 いくつかの例が書籍で紹介されている
 ● Debezium / Eventuate Tram
 ○ DB への変更を Apache Kafka にパブ リッシュ
 ● LinkedIn Databus
 ○ Oracle トランザクションログ
 ● DynamoDB streams
 OUTBOX テーブルからメッセージをパブリッシュする方法
 パターン② : 
 Transaction log tailing


Slide 66

Slide 66 text

66 補足: 重複するメッセージの処理
 例) Amazon SQS
 出典 : サーバーレスにおけるべき等性の実装 (メッセージ編)| AWS 
 https://aws.amazon.com/jp/builders-flash/202106/serverless-idempotency-implementation/
 ● 標準キュー 
 ○ At-Least-Once Delivery
 ● FIFOキュー
 ○ Exactly-Once-Processing
 どちらのタイプでもメッセージ削除に
 失敗すると複数回処理されることがある


Slide 67

Slide 67 text

67 補足: 重複するメッセージの処理
 対処方法は2つ
 1. べき等なメッセージハンドラを作る
 2. 重複するメッセージを捨てる
 出典: マイクロサービスパターン 実践的システムデザインのためのコード解説 - インプレスブックス
 https://book.impress.co.jp/books/1118101063


Slide 68

Slide 68 text

メッセージのスキーマを定義する
 68

Slide 69

Slide 69 text

メッセージのスキーマを定義する
 非同期メッセージングを行う場合
 JSON 形式を使うことが多い
 69

Slide 70

Slide 70 text

メッセージのスキーマを定義する
 JSON Schema でスキーマを定義し、バ リデーション を行うことで堅牢な非同期 処理を実現できる
 (https://json-schema.org/ 参照)
 70

Slide 71

Slide 71 text

メッセージのスキーマを定義する
 https://json-schema.org/tools から 探すことができる
 例えば Ruby には json-schema とい う gem ライブラリがある
 (https://github.com/voxpupuli/json-schema 参照)
 71 JSON Schema バリデーター


Slide 72

Slide 72 text

おわりに
 72

Slide 73

Slide 73 text

おわりに
 一連のイベント から構成される業務
 73 例) B/43 カード発行処理のイメージ(実際のフローとは異なります)
 非同期処理 を含むこともある


Slide 74

Slide 74 text

ç ç ç 74 おわりに
 処理の流れ が
 把握し辛い
 変更を行うのが
 難しい
 データ整合性の
 担保が困難
 直面する課題


Slide 75

Slide 75 text

75 おわりに
 1. イベントを正しく保存する
 2. イベント間の整合性を保つ
 3. データベースとメッセージキューの整合性を保つ
 4. メッセージのスキーマを定義する
 ポイントは4つ!


Slide 76

Slide 76 text

ç ç ç 76 おわりに
 開発生産性 
 保守運用コスト 
 事業成長スピード 
 これらの改善にもつながる


Slide 77

Slide 77 text

77 ご清聴ありがとうございました!