Slide 1

Slide 1 text

例外設計について考えて Kotlin(Spring Boot & Arrow)で実践する Kotlin Fest 2024 杉本 将来(@Msksgm)

Slide 2

Slide 2 text

• はじめに • 例外設計を考える背景 • 技術的例外・ビジネス例外と予期する例外・予期しない例外 • 例外設計とモデリング • モニタリング・オブザーバビリティと例外 • 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 • まとめ ⽬次 1

Slide 3

Slide 3 text

• はじめに ⽬次 2

Slide 4

Slide 4 text

はじめに ⾃⼰紹介 • 名前 • 杉本将来(HN: Msksgm) • SNS • X: @msksgm • GitHub: Msksgm • Zenn: msk • 「ハンズオンで学ぶサーバーサイド Kotlin」を作成(200 like 突破 🎉) • 業務 • Yahoo! オークション・Yahoo! フリマ の課⾦システムの開発保守 • Yahoo! オークション・Yahoo! フリマ の SRE 業務 3 SNS のアイコン ハンズオンで学ぶ サーバーサイド Kotlin

Slide 5

Slide 5 text

はじめに 「例外設計について考えて Kotlin で実践する」の⽬的 • 例外設計を考えることで、プロダクト開発へ好影響を与えられるようになりたい • プロダクト開発 ≒ 企画・開発・運⽤・フィードバック、DevOps、プロダクトマネジメント、 アジャイル(スクラム、XP)、etc... 4 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート ・アラート ・評価 フィードバック

Slide 6

Slide 6 text

はじめに 注意事項 • 取り扱わないこと • Spring Boot の初歩的な内容 • ドメイン駆動設計の詳細 • Arrow-kt の詳細な利⽤⽅法 • Kotlin・Java の Exception と Error 型の定義と異なる⾔葉の使い⽅ • 「例外」という⾔葉を多⽤しますが、Kotlin・Java の Exception(例外)・Error (エラー)ではないことに注意してください 5

Slide 7

Slide 7 text

• 例外設計を考える背景 ⽬次 6

Slide 8

Slide 8 text

例外設計を考える背景 「例外設計を考える背景」の⽬的 • 例外設計が、企画・開発・運⽤・フィードバックに影響を与えること学ぶ • なぜ例外を考えるか、下図に従って概要を把握してもらう。詳細は後述の節で解説 7 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート ・アラート ・評価 「例外設計を考える背景」の範囲 フィードバック

Slide 9

Slide 9 text

例外設計を考える背景 企画 例外設計を考えると企画(プロダクトマネジメント)に影響を与える • 例外(失敗パターン)は企画・開発・運⽤間で合意する • 例外が発⽣したときの、重要度を開発・運⽤が把握できる 8

Slide 10

Slide 10 text

例外設計を考える背景 開発 例外設計を考えると開発体験と設計の考え⽅に影響を与える • 例外時のモデリング • 単体テストのテスト容易性の向上と不要なテストの削除 • 分岐(if、when、switch)におけるそれ以外(else、default)の考え⽅ • ユースケース・クリティカルユーザージャーニーの推敲 9

Slide 11

Slide 11 text

例外設計を考える背景 運⽤ 例外設計を考えると運⽤の考え⽅に影響を与える • 影響度からアラートレベルの設定(Slack通知、オンコール、etc...) • 具体的な例外名を SLI に組み込むことで、次の改善の優先度を決めることが可能 10 ログ監視 アラート設計

Slide 12

Slide 12 text

例外設計を考える背景 フィードバック 例外設計を考えるとソフトウェアから得られるフィードバックに影響を与える • アラート対応 • 評価(ユーザー、社内、etc...) • etc... 11 評価 アラート対応

Slide 13

Slide 13 text

例外設計を考える背景 まとめ • 例外設計はプロダクト開発の⼀連に影響を与える • それぞれのフェーズに対して好影響を与えることができる 12 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート ・アラート ・評価 フィードバック

Slide 14

Slide 14 text

• 技術的例外・ビジネス例外と予期する例外・予期しない例外 ⽬次 13

Slide 15

Slide 15 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 「技術的例外・ビジネス例外と予期する例外・予期しない例外」の⽬的 • 例外の分類を考える⽬的を知る • 参考⽂献から例外の種類を把握し理解 • 例外の⽤途を知る • 「技術的例外・ビジネス例外」「予期する例外・予期しない例外」のユースケースの理解 14 「技術的例外・ビジネス例外と予期する例外・予期しない例外」 の範囲

Slide 16

Slide 16 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 例外設計の参考⽂献 • ⿊枠: 発表者が読了済のためなんとなくわかる • ⾚枠: 発表者が未読 or ⼀部読了のため、書いてあることを抜粋 15

Slide 17

Slide 17 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 参考⽂献の分類 16 例外の概念・思想について⾔及 ドメインモデルをコードとして表現

Slide 18

Slide 18 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 本項で扱う範囲 17 これらの書籍から例外を考える 例外の概念・思想について⾔及

Slide 19

Slide 19 text

「プログラマが知るべき 97 のこと」 • タイトル通りのプログラマが知るべきエッセイ集 • 通称「きのこ」(プログラマが知るべき 97 のこと) • 邦訳だと 97 エッセイ(78 ⼈) + 10 エッセイ(⽇本⼈エンジニア 8 ⼈) • 本発表では、「技術的例外とビジネス例外を明確に区別する」を⾔及 技術的例外・ビジネス例外と予期する例外・予期しない例外 18 Dan Bergh Johnsson (出典: https://ndc-security.com/speakers/dan-bergh-johnsson ) プログラマが知るべき 97 のこと

Slide 20

Slide 20 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 きのこ21「技術的例外とビジネス例外を明確に区別する」 の要約 「両者を同じ例外階層構造で扱ってはいけない」 19 技術的例外 ビジネス例外 発⽣起因 「契約」の外(契約外) 「契約」の⼀部(契約内) ビジネスロジック No Yes 具体例 DB が応答しない 引き落とそうとしたが、 残⾼が⾜りない try/catch の有無 しない(⼤域脱出) する ハンドリング フレームワークが対応する クライアントが対応する 表: 技術的例外とビジネス例外の⽐較

Slide 21

Slide 21 text

「セキュア・バイ・デザイン」について • 設計観点からセキュアなコードを作成する書籍 • セキュリティ本に⾒せかけた、ドメイン駆動設計の書籍 • 「技術的例外とビジネス例外を明確に区別する」と著者が同じ • この本からドメイン駆動設計も踏まえた例外を考える 技術的例外・ビジネス例外と予期する例外・予期しない例外 20 セキュア・バイ・デザイン Dan Bergh Johnsson (出典: https://ndc-security.com/speakers/dan-bergh-johnsson )

Slide 22

Slide 22 text

「セキュア・バイ・デザイン」第 9 章(⼀部)の要約 2 つを混合させることはアプリケーションに複雑さをもたらす 技術的例外・ビジネス例外と予期する例外・予期しない例外 21 技術的例外 ビジネス例外 発⽣起因 ドメインとは無関係 技術的 or フレームワークのエラー ドメインの観点から不正 ドメイン・ルール違反 具体例 DB が応答しない 引き落とそうとしたが、 残⾼が⾜りない 表: 技術的例外とビジネス例外の⽐較 p.332 引⽤ > 開発者の中には、 例外をスローする第⼀の⽬的は、技術的なのかどうなのかにかかわらず、不正なアクションを起こさせないことであると考えており、 そのため、ビジネス例外も技術的例外も同じよう にまとめて扱う設計を好む⼈もいます。 > もしかしたら、これら 2 種類の例外の違いは⼤したことではないように思っている開発者もいるかもしれません。 > しかしながら、異なる種類の例外を同じように扱うことは、アプリケーションに様々な複雑さとセキュリティの問題をもたらすことになります。

Slide 23

Slide 23 text

「セキュア・バイ・デザイン」第 9 章(⼀部)の具体例(1/4) 技術的例外・ビジネス例外と予期する例外・予期しない例外 22 リスト 9.2. 同じ型を使ってビジネス例外と技術例外の両⽅を表現している例 public class AccountRepository { private final AccountDatabase accountDatabase; public Account fetchAccountFor( final Customer customer, final AccountNumber accountNumber ) { try { return accountDatabase .selectAccountsFor(customer) // 指定した顧客の銀⾏⼝座の取得 .stream() .filter( // 指定した⼝座番号と⼀致する銀⾏⼝座のみを選択 account -> account.number().equals(accountNumber)) .findFirst() // 銀⾏⼝座の取得 .orElseThrow( // 該当する銀⾏⼝座がない場合は例外をスロー () -> new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)は⾒つかりませんでした")); ) } catch (SQLException e) { throw new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)が取得できませんでした"), e); } } }

Slide 24

Slide 24 text

「セキュア・バイ・デザイン」第 9 章(⼀部)の具体例(2/4) 技術的例外・ビジネス例外と予期する例外・予期しない例外 23 リスト 9.2. 同じ型を使ってビジネス例外と技術例外の両⽅を表現している例 ビジネス例外 技術的例外 public class AccountRepository { private final AccountDatabase accountDatabase; public Account fetchAccountFor( final Customer customer, final AccountNumber accountNumber ) { try { return accountDatabase .selectAccountsFor(customer) // 指定した顧客の銀⾏⼝座の取得 .stream() .filter( // 指定した⼝座番号と⼀致する銀⾏⼝座のみを選択 account -> account.number().equals(accountNumber)) .findFirst() // 銀⾏⼝座の取得 .orElseThrow( // 該当する銀⾏⼝座がない場合は例外をスロー () -> new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)は⾒つかりませんでした")); ) } catch (SQLException e) { throw new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)が取得できませんでした"), e); } } }

Slide 25

Slide 25 text

「セキュア・バイ・デザイン」第 9 章(⼀部)の具体例(3/4) 技術的例外・ビジネス例外と予期する例外・予期しない例外 24 public class AccountRepository { private final AccountDatabase accountDatabase; public Account fetchAccountFor( final Customer customer, final AccountNumber accountNumber ) { try { return accountDatabase .selectAccountsFor(customer) // 指定した顧客の銀⾏⼝座の取得 .stream() .filter( // 指定した⼝座番号と⼀致する銀⾏⼝座のみを選択 account -> account.number().equals(accountNumber)) .findFirst() // 銀⾏⼝座の取得 .orElseThrow( // 該当する銀⾏⼝座がない場合は例外をスロー () -> new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)は⾒つかりませんでした")); ) } catch (SQLException e) { throw new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)が取得できませんでした"), e); } } } リスト 9.2. 同じ型を使ってビジネス例外と技術例外の両⽅を表現している例 ビジネス例外 技術的例外 技術的例外 ビジネス例外 発⽣起因 ドメインとは無関係 技術的 or フレームワークのエラー ドメインの観点から不正 ドメイン・ルール違反 具体例 メモリ不⾜ 引き落とそうとしたが、 残⾼が⾜りない

Slide 26

Slide 26 text

「セキュア・バイ・デザイン」第 9 章(⼀部)の具体例(4/4) 技術的例外・ビジネス例外と予期する例外・予期しない例外 25 public class AccountRepository { private final AccountDatabase accountDatabase; public Account fetchAccountFor( final Customer customer, final AccountNumber accountNumber ) { try { return accountDatabase .selectAccountsFor(customer) // 指定した顧客の銀⾏⼝座の取得 .stream() .filter( // 指定した⼝座番号と⼀致する銀⾏⼝座のみを選択 account -> account.number().equals(accountNumber)) .findFirst() // 銀⾏⼝座の取得 .orElseThrow( // 該当する銀⾏⼝座がない場合は例外をスロー () -> new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)は⾒つかりませんでした")); ) } catch (SQLException e) { throw new IllegalStateException( String.format("〇〇様の銀⾏⼝座(〇〇)が取得できませんでした"), e); } } } リスト 9.2. 同じ型を使ってビジネス例外と技術例外の両⽅を表現している例 ビジネス例外 技術的例外 技術的例外 ビジネス例外 発⽣起因 ドメインとは無関係 技術的 or フレームワークのエラー ドメインの観点から不正 ドメイン・ルール違反 具体例 ⽀払い済みの注⽂に更に商品を追加 メモリ不⾜ 異なる種類の例外を同じように扱うことは、 アプリケーションに様々な複雑さとセキュリティの問題 をもたらすことになります。

Slide 27

Slide 27 text

「技術的例外とビジネス例外」についてまとめ 技術的例外とビジネス例外を定義することでやりたいこと • 両者の例外階層構造を区別する • アプリケーションから複雑さを取り除く 技術的例外・ビジネス例外と予期する例外・予期しない例外 26 技術的例外 ビジネス例外 発⽣起因: 契約 「契約」外 「契約」の⼀部 発⽣起因: ドメイン ドメインとは無関係 ドメインルール ビジネスロジック No Yes try/catch の有無 しない(⼤域脱出) する ハンドリング フレームワークが対応する クライアントが対応する 表: 「技術的例外とビジネス例外」まとめ

Slide 28

Slide 28 text

「技術的例外とビジネス例外」の経験的な限界 「技術的例外とビジネス例外」の分類で以下のようなパターンがあった • 技術的例外でクライアントがハンドリングしたいとき • ex: 排他処理でないアプリケーションにおける Insert 時の⼀意制約違反 • ビジネス例外でフレームワークがハンドリング(⼤域脱出)したいとき • ex: ビジネス的に存在しないマスターの組み合わせ 技術的例外・ビジネス例外と予期する例外・予期しない例外 27 「技術的例外とビジネス例外」を⾒直す必要性

Slide 29

Slide 29 text

技術的例外・ビジネス例外と予期する例外・予期しない例外 「技術的例外とビジネス例外」の拡張 「予期する例外・予期しない例外」という考え⽅(例外設計(kawasima)) 28 > (⾔語ごとの例外の分類を表にまとめた後の⽂章) > 各⾔語によって違いはあるが、ここでは、Expected but Unacceptedを「予期する例外」 > Unexpectedを「予期しない例外」と呼ぶ。ここで「予期する」かどうかはプログラム上で明⽰的に > 具象型の例外をキャッチしハンドリングするかどうかを指し、⼈間が予想しうるかどうかではない。 予期する 予期しない try/catch の有無 する しない(⼤域脱出) ハンドリング クライアントが対応する フレームワークが対応する 表:「予期する例外・予期しない例外」のハンドリングの分類 出典: scrapbox, kawashima, 例外設計, https://scrapbox.io/kawasima/%E4%BE%8B%E5%A4%96%E8%A8%AD%E8%A8%88

Slide 30

Slide 30 text

「技術的例外とビジネス例外」と「予期する例外・予期しない例外」 技術的例外・ビジネス例外と予期する例外・予期しない例外 29 予期する 技術的例外 予期しない 技術的例外 予期する ビジネス例外 予期しない ビジネス例外 発⽣起因: 契約 「契約」外 「契約」外 「契約」の⼀部 「契約」の⼀部 発⽣起因: ドメイン ドメインとは 無関係 ドメインとは無関係 ドメインルール ドメインルール ビジネスロジック No No Yes Yes try/catch の有 無 する しない (⼤域脱出) する しない (⼤域脱出) ハンドリング クライアント側に 組み込む フレームワークが対応する クライアント側に 組み込む フレームワークが 対応する 具体例 ⾮同期処理によって発 ⽣しうる⼀意制約違反 外部 API の想定していな いステータスコード バリデーションエラー マスターテーブルに存在 しない組み合わせ 表: 「技術的例外とビジネス例外」・「予期する例外と予期しない例外」の組み合わせ

Slide 31

Slide 31 text

「技術的例外とビジネス例外」と「予期する例外・予期しない例外」 技術的例外・ビジネス例外と予期する例外・予期しない例外 30 予期する 技術的例外 予期しない 技術的例外 予期する ビジネス例外 予期しない ビジネス例外 発⽣起因: 契約 「契約」外 「契約」外 「契約」の⼀部 「契約」の⼀部 発⽣起因: ドメイン ドメインとは 無関係 ドメインとは無関係 ドメインルール ドメインルール ビジネスロジック No No Yes Yes try/catch の有 無 する しない (⼤域脱出) する しない (⼤域脱出) ハンドリング クライアント側に 組み込む フレームワークが対応する クライアント側に 組み込む フレームワークが 対応する 具体例 ⾮同期処理によって発 ⽣しうる⼀意制約違反 外部 API の想定していな いステータスコード バリデーションエラー マスターテーブルに存在 しない組み合わせ 表: 「技術的例外とビジネス例外」・「予期する例外と予期しない例外」まとめ 拡張部分 • 技術的例外にもハンドリングする例外が存在する • ビジネス例外にもハンドリングしない例外が存在する • ハンドリングの有無によらず、技術的例外とビジネス例外は区別する

Slide 32

Slide 32 text

例外の分類まとめ 技術的例外・ビジネス例外と予期する例外・予期しない例外 31 技術的例外 ビジネス例外 予期する例外 予期しない例外 予期する 技術的例外 予期しない 技術的例外 予期しない ビジネス例外 予期する ビジネス例外

Slide 33

Slide 33 text

例外の分類まとめ 技術的例外・ビジネス例外と予期する例外・予期しない例外 32 技術的例外 ビジネス例外 予期する例外 予期しない例外 予期する 技術的例外 予期しない 技術的例外 予期しない ビジネス例外 予期する ビジネス例外 アプリケーションに複雑さをもたらさないために、技術的例外とビジネス例外を分類する 予期する例外・予期しない例外 によって、ハンドリング⽅法を分ける

Slide 34

Slide 34 text

「技術的例外・ビジネス例外と予期する例外・予期しない例外」のまとめ • 例外の分類を考える⽬的を知る • 例外構造を区別し、アプリケーションの複雑さを減らすことが⽬的 • 例外の⽤途を知る • 例外の発⽣パターンやハンドリングをもとに 4 つに分類 技術的例外・ビジネス例外と予期する例外・予期しない例外 33

Slide 35

Slide 35 text

• 例外設計とモデリング ⽬次 34

Slide 36

Slide 36 text

例外設計とモデリング 「例外設計とモデリング」の⽬的 • 例外を分類し、コードに表現する⽅法を考える • ドメイン駆動設計の考え⽅や⽂化からヒントを得る • アーキテクチャにコードとしての例を紹介 35 「例外設計とモデリング」の範囲

Slide 37

Slide 37 text

例外設計とモデリング 「例外設計とモデリング」の注意点 • ドメイン駆動設計について • ドメイン駆動設計については詳細に触れない • 個⼈的な調査と解釈が多い • ⽂化やアーキテクチャの呼称や考えが原著と異なる可能性 • 本項で扱わない範囲 • 本来は運⽤・フィードバックについても触れたいが、別の項で触れます 36

Slide 38

Slide 38 text

例外設計とモデリング 参考⽂献の分類(再掲) 37 例外の概念・思想について⾔及 ドメインモデルをコードとして表現

Slide 39

Slide 39 text

例外設計とモデリング 参考⽂献の分類 38 ドメインモデルをコードとして表現 これらの書籍からドメインモデル について考える

Slide 40

Slide 40 text

「エリック・エヴァンスのドメイン駆動設計」について • 通称エヴァンス本 • ドメイン駆動設計(以下、DDD)の考え⽅は後々の設計について⼤きな影響を与えた • DDD そのものを解説した書籍 • 「実践ドメイン駆動設計」(通称、IDDD 本) • 「ドメイン駆動設計 モデリング/実装ガイド」 • 「ドメイン駆動設計⼊⾨」 • etc... • DDD の概念を輸⼊したり独⾃拡張したりした本(「隠れ DDD 本」と個⼈的に呼んでいる) • 「セキュア・バイ・デザイン」 • 「単体テストの考え⽅/使い⽅」 • 「現場で役⽴つシステム設計の原則」 • etc.. • 「エリック・エヴァンスのドメイン駆動設計」を参考⽂献に加えている書籍 • 「ソフトウェアアーキテクチャの基礎」 • 「Design It!」 • etc... 例外設計とモデリング 39

Slide 41

Slide 41 text

DDD の⽂化(個⼈的な解釈) • ドメインの認識と技術の側⾯から相互フィードバックで⽬標を達成する • 認識=戦略的 DDD 、技術=戦術的 DDD と呼ばれたりする 例外設計とモデリング 40 戦略的 DDD 戦術的 DDD ユビキタス⾔語 ドメインモデリング アーキテクチャ デザインパターン 図: 戦略的 DDDと戦術的 DDD ドキュメンテーション

Slide 42

Slide 42 text

戦略的 DDD 戦術的 DDD ユビキタス⾔語 ドメインモデリング アーキテクチャ デザインパターン 図: 戦略的 DDD と戦術的 DDD DDD と例外設計 • 例外はドメインに従う • 先述した例外の分類はステークホルダーで定義する必要がある • 例外をドメインに従わせるには、戦略的と戦術的の両⽅の側⾯が必要 • 最初から分類を断定できないので、学びを反映しやすいコード・⽂化が必要 例外設計とモデリング 41 例外を • ユースケースとして理解する • ビジネス側と合意する • ドキュメントに反映する ドキュメンテーション 例外を • ドメインオブジェクトとして扱う • ハンドリングする

Slide 43

Slide 43 text

例外設計とモデリングをコードに反映 • ドメインを理解し、コードに反映するまでを簡単に説明 • 擬似的に⼀連のフィードバックを紹介 • 本当はスライドにまとまるものではないが、今回は⼀例として紹介 • 疑似コードによって、オニオンアーキテクチャと例外の設計について解説 • オニオンアーキテクチャに例外の視点を加える • ⻑くなるので疑似コードで説明 例外設計とモデリング 42

Slide 44

Slide 44 text

例外設計とモデリングをコードに反映 • ドメインを理解し、コードに反映するまでを簡単に説明 • 擬似的に⼀連のフィードバックを紹介 • 本当はスライドにまとまるものではないが、今回は⼀例として紹介 例外設計とモデリング 43

Slide 45

Slide 45 text

例外設計とモデリング(1/5) 例外設計とモデリング 44 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート フィードバック • アラート ビジネス ︓XXX が YYY のときには、ZZZ になります エンジニア︓AAA、BBB、CCC の時にはどうなりまか︖ ビジネス ︓AAA のときは、aaa、BBB のときは、bbb です。CCC のときには考えなくて良いです。 エンジニア︓承知しました

Slide 46

Slide 46 text

例外設計とモデリング(2/5) 例外設計とモデリング 45 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート フィードバック • アラート エンジニア︓ - YYY のとき ZZZ - AAA のときは、aaa - BBB のときは、bbb YYY、AAA、BBB 以外のときには、例外を throw してアラートを流す

Slide 47

Slide 47 text

例外設計とモデリング(3/5) 例外設計とモデリング 46 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート フィードバック • アラート 「アラートが発⽣︕︕︕」 - CCC のパターンが存在した - オンコールで対応した

Slide 48

Slide 48 text

例外設計とモデリング(4/5) 例外設計とモデリング 47 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート フィードバック • アラート ビジネス: 把握できていないパターンがあった エンジニア︓ CCCには、別パターン(CCCʻ、 CCCʼʼ)があった アラート設計は、リアルタイムで対応す るのは不要だから、warn にしておこう 今回のパターンはポストモーテムにして 社内で振り返ろう

Slide 49

Slide 49 text

例外設計とモデリング(5/5) 例外設計とモデリング 48 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート フィードバック • アラート 実際に綺麗に整理できる状況はなかなかない しかし、予期しない技術的・ビジネス例外を把握できない、例外として throw しない、 アラートが来ない、異常事態に気が付かないまま運⽤されるのは深刻な問題 例外を把握しフィードバックまで活かせる⽂化作りが⼤切

Slide 50

Slide 50 text

例外設計とモデリングをコードに反映 • 疑似コードによって、オニオンアーキテクチャと例外の設計について解説 • オニオンアーキテクチャに例外の視点を加える • ⻑くなるので疑似コードで説明 例外設計とモデリング 49

Slide 51

Slide 51 text

「オニオンアーキテクチャ」について • アーテキクチャ構成は参考書籍をもとに以下とする • 書籍によって呼称や役割の解釈に違いはあるが、役割がわかれていたら OK 例外設計とモデリング 50 Domain UseCase Presentation Infrastructure Domain 層 UseCase 層 Infrastructure 層 Presentation 層

Slide 52

Slide 52 text

「オニオンアーキテクチャ」と例外 • 各層の役割(Spring Boot による簡易版) 例外設計とモデリング 51 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり

Slide 53

Slide 53 text

「オニオンアーキテクチャ」と例外 • 各層の役割(Spring Boot による簡易版) 例外設計とモデリング 52 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり 例外はどこで書く︖

Slide 54

Slide 54 text

• Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • 予期する技術的例外 は Domain のインタ フェースから定義 • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 53 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出

Slide 55

Slide 55 text

• API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス • Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • 予期する技術的例外 は Domain のインタ フェースから定義 • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 54 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出 @RestController class XXXController(...) { @GetMapping(...) fun xxxMethod(...): ResponseEntity<...> { try { val xxx = xxxUseCase.execute(xxx) } catch(...) { throw XxxUseCaseErrorException(...) } return ResponseEntity(...) } data class XXXUseCaseErrorException(val error: XXXArticleUseCase.Error) : Exception() @ExceptionHandler(value = [XXXUseCaseErrorException::class]) fun onShowArticleUseCaseErrorException(e: XXXUseCaseErrorException): ResponseEntity<...> = { ... return ResponseEntity<...>( GenericErrorModel(...), ... ) } • UseCase が throw した例外 をハンドリング • API 仕様に反するリクエストをバリデー ションエラーとして throw Presentation 層

Slide 56

Slide 56 text

• API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス • Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • 予期する技術的例外 は Domain のインタ フェースから定義 • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 55 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出 Presentation 層 @RestControllerAdvice class GlobalExceptionHandleController { @ExceptionHandler(NoResourceUnexpectedBusinessException: :class) fun noResourceFoundExceptionHandler(e: NoResourcenexpectedBusinessException): ResponseEntity<...> { return ResponseEntity<...>( GenericErrorModel( errors = GenericErrorModelErrors( body = listOf(”...") ), ), HttpStatus.NOT_FOUND ) } ... } • ⼤域脱出した例外を catch してレスポンス • 予期しない技術的・ビジネス例外をハンドリングする • この例だと、予期しないビジネス例外を 404 としてハンドリ ングした例

Slide 57

Slide 57 text

• Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • 予期する技術的例外 は Domain のインタ フェースから定義 • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 56 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出 UseCase 層 interface XxxUseCase { fun execute(...): Zzz = throw NotImplementedError() } @Service class XxxUseCaseImpl(...) : XxxUseCase { override fun execute(...): Zzz { try { val a = domain.DomainObject(...) } catch (...) { throw ExpectedDomainObjectException } ... return z } } • Domain が throw した例外からビジネスロジックの継続と中断 を判断 • 予期する例外だったら catch する • 予期しない例外だったら catch しない • API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス

Slide 58

Slide 58 text

• API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 57 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出 • Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある Domain 層 class DomainObject private constructor(...) { companion object { fun newWithValidation(val a String, val b Int): DomainObject { // 予期するビジネス例外 throw DomainExpectedException(...) return DomainObject() } fun newYYY(...) { // 予期しないビジネス例外 throw UnexpectedException(...) return DomainObject() } } } • ドメインルールに違反するロジックを例外として throw • 予期する例外は呼び出し側が try/catch、予期しない例外 は⼤域脱出してフレームワークがハンドリング • ドメイン側がクライアント側をどうするかは検討不要

Slide 59

Slide 59 text

• API 仕様に反するリクエストを バリデーションエラー • UseCase が throw した例外 をハンドリングしてレスポンス • ⼤域脱出した例外を catch し てレスポンス 「オニオンアーキテクチャ」と例外の具体例 • Web API で例外についての⼀例 例外設計とモデリング 58 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • Domain が throw した例外 からビジネスロジックを継続と中 断を判断して例外を throw • ドメインルールに違反するロジッ クを例外として throw • 予期する例外は try/catch、 予期しない例外は⼤域脱出 • Domain のインタフェースを 実装するため Infra の独⾃ 定義はない(はず) • 予期する技術的例外 は Domain のインタ フェースから定義 • DB やネットワーク起因の予 期しない技術的例外はここで 発⽣する可能性がある Infrastructure 層 @Repository class XxxRepositoryImpl(...) : XxxRepository { override fun findBySlug(slug: Slug): YYY { val x = Hoge() // 予期する技術的例外 ... throw TechnicalExpectedError(...) return yyy } }

Slide 60

Slide 60 text

例外設計とモデリング 「例外設計とモデリング」のまとめ • 例外を分類し、コードに表現する⽅法を考えた • ドメイン駆動設計の考え⽅や⽂化からフィードバックの過程を紹介 • 具体的なコード例をオニオンアーキテクチャと⼀緒に紹介 59 「例外設計とモデリング」の範囲

Slide 61

Slide 61 text

• モニタリング・オブザーバビリティと例外 ⽬次 60

Slide 62

Slide 62 text

モニタリング・オブザーバビリティと例外 「モニタリング・オブザーバビリティと例外」⽬的 • ⼊⾨モニタリング・オブザーバビリティ • システムからのフィードバックを獲得するために、SLI/SLO、O11y、監視について知る • すべての例外を予想することはできない(予期する/しないの分類もそのため) • 想定外のことが発⽣したときに、すべての開発者が原因を追跡できるようになる必要がある • 例外から考えるモニタリング・オブザーバビリティとログ • 例外からログの出⼒箇所と内容について知る 61 「モニタリング・オブザーバビリティと例外」の範囲

Slide 63

Slide 63 text

モニタリング(監視) • 意味 • あるシステムやそのシステムのコンポーネントの振る舞いや出⼒を観察しチェックし続けること • 具体例 • メモリ・CPU 使⽤率 モニタリング・オブザーバビリティと例外 62 > 監視とは、あるシステムやそのシステムのコンポーネントの振る舞いや出⼒を観察しチェックし > 続ける⾏為である。 「⼊⾨ 監視」 p.35 より引⽤

Slide 64

Slide 64 text

オブザーバビリティ • 意味 • 「システムがどのような状態になったとしても、それがどんなに斬新で奇妙なものであっても、ど れだけ理解し説明できるかを⽰す尺度」 • o11y(Observability) と略されることも モニタリング・オブザーバビリティと例外 63 > 簡単に⾔うと、私たちが考えるソフトウェアシステムの「オブザーバビリティ」とは、 システムがどのよう > な状態になったとしても、それがどんなに斬新で奇妙なものであっても、どれだけ理解し説明できる > かを⽰す尺度です。 また、そのような斬新で奇妙な状態に対しても、事前にデバッグの必要性を > 定義したり予測したりすることなく、 システムの状態データのあらゆるディメンションやそれらの組み > 合わせについてアドホックに調査し、よりデバッグが可能であるようにする必要があります。 もし、 新 > しいコードをデプロイする必要がなく 、どんな斬新で奇妙な状態でも理解できるなら、オブザーバビ > リティがあると⾔えます。 「オブザーバビリティ・エンジニアリング」 p.6 より引⽤

Slide 65

Slide 65 text

SLO/SLI • 意味 • SLO(サービスレベル⽬標) • 障害を起こしても許容される頻度の⽬標値 • 正常に動作していなくてもユーザーが重⼤な混乱に陥らないと保証する⽬標値 • SLI(サービスレベル指標) • ユーザーの満⾜度に相関している指標 モニタリング・オブザーバビリティと例外 64

Slide 66

Slide 66 text

SLOの値が意味する可⽤性表 • SLO を具体的な時間に置き換えると下表のようになる モニタリング・オブザーバビリティと例外 65 可⽤性(%) 年あたりダウンタイム ⽉あたりダウンタイム 週あたりダウンタイム ⽇あたりダウンタイム 90%(ワンナイン) 36.5⽇ 72時間 16.8時間 2.4時間 95% 18.25⽇ 36時間 8.4時間 1.2時間 99%(ツーナイン) 3.65⽇ 7.20時間 1.68時間 14.4分 99.5% 1.83⽇ 3.60時間 50.4分 7.2分 99.9%(スリーナイン) 8.76時間 43.8分 10.1分 1.44分 99.95% 4.38時間 21.56分 5.04分 43.2秒 99.99%(フォーナイン) 52.56分 4.38分 1.01分 8.64秒 99.995% 26.28分 2.16分 30.24秒 4.32秒 99.999%(ファイブナイン) 5.26分 25.9秒 6.05秒 864.3ミリ秒 可⽤性表

Slide 67

Slide 67 text

SLO とオンコール対応 • SLO 99.9 % でオンコール対応について考える モニタリング・オブザーバビリティと例外 66 可⽤性(%) 年あたりダウンタイム ⽉あたりダウンタイム 週あたりダウンタイム ⽇あたりダウンタイム 90%(ワンナイン) 36.5⽇ 72時間 16.8時間 2.4時間 95% 18.25⽇ 36時間 8.4時間 1.2時間 99%(ツーナイン) 3.65⽇ 7.20時間 1.68時間 14.4分 99.5% 1.83⽇ 3.60時間 50.4分 7.2分 99.9%(スリーナイン) 8.76時間 43.8分 10.1分 1.44分 99.95% 4.38時間 21.56分 5.04分 43.2秒 99.99%(フォーナイン) 52.56分 4.38分 1.01分 8.64秒 99.995% 26.28分 2.16分 30.24秒 4.32秒 99.999%(ファイブナイン) 5.26分 25.9秒 6.05秒 864.3ミリ秒 可⽤性表 参考: https://speakerdeck.com/ymotongpoo/reliability-objective-and-system-architecture

Slide 68

Slide 68 text

SLO とオンコール対応 • オンコール対応について考えてみる モニタリング・オブザーバビリティと例外 67 可⽤性(%) 年あたりダウンタイム ⽉あたりダウンタイム 週あたりダウンタイム ⽇あたりダウンタイム 90%(ワンナイン) 36.5⽇ 72時間 16.8時間 2.4時間 95% 18.25⽇ 36時間 8.4時間 1.2時間 99%(ツーナイン) 3.65⽇ 7.20時間 1.68時間 14.4分 99.5% 1.83⽇ 3.60時間 50.4分 7.2分 99.9%(スリーナイン) 8.76時間 43.8分 10.1分 1.44分 99.95% 4.38時間 21.56分 5.04分 43.2秒 99.99%(フォーナイン) 52.56分 4.38分 1.01分 8.64秒 99.995% 26.28分 2.16分 30.24秒 4.32秒 99.999%(ファイブナイン) 5.26分 25.9秒 6.05秒 864.3ミリ秒 可⽤性表 1 ヶ⽉の SLO を「99.9 %」としていたとき、ダウンタイム「43.8 分」で以下の対応を終えられれば OK 0. 問題発⽣ 1. アラート発報 2a. 担当者(アラート輪番が)アラートに反応 2b. 担当者(アラート輪番が)が担当チームにエスカレーション 3. 調査開始 4. 原因特定 5a. 必要/可能ならロールバック 5b. 問題箇所を修正 6a. アプリケーションならアーティファクトをビルド 6b. ミドルウェア等なら設定ファイルを検証 7a. デプロイ承認依頼 7b. 許可後、デプロイ依頼 7c. 修正をデプロイ 8. 問題が解消されたことを確認 参考: https://speakerdeck.com/ymotongpoo/reliability-objective-and-system-architecture

Slide 69

Slide 69 text

SLO とオンコール対応 • SLO を守るためにできること モニタリング・オブザーバビリティと例外 68 可⽤性(%) 年あたりダウンタイム ⽉あたりダウンタイム 週あたりダウンタイム ⽇あたりダウンタイム 90%(ワンナイン) 36.5⽇ 72時間 16.8時間 2.4時間 95% 18.25⽇ 36時間 8.4時間 1.2時間 99%(ツーナイン) 3.65⽇ 7.20時間 1.68時間 14.4分 99.5% 1.83⽇ 3.60時間 50.4分 7.2分 99.9%(スリーナイン) 8.76時間 43.8分 10.1分 1.44分 99.95% 4.38時間 21.56分 5.04分 43.2秒 99.99%(フォーナイン) 52.56分 4.38分 1.01分 8.64秒 99.995% 26.28分 2.16分 30.24秒 4.32秒 99.999%(ファイブナイン) 5.26分 25.9秒 6.05秒 864.3ミリ秒 可⽤性表 1 ヶ⽉の SLO を「99.9 %」としていたとき、ダウンタイム「43.8 分」で以下の対応を終えられれば OK 0. 問題発⽣ 1. アラート発報 2a. 担当者(アラート輪番が)アラートに反応 2b. 担当者(アラート輪番が)が担当チームにエスカレーション 3. 調査開始 4. 原因特定 5a. 必要/可能ならロールバック 5b. 問題箇所を修正 6a. アプリケーションならアーティファクトをビルド 6b. ミドルウェア等なら設定ファイルを検証 7a. デプロイ承認依頼 7b. 許可後、デプロイ依頼 7c. 修正をデプロイ 8. 問題が解消されたことを確認 SLO を守るためにできること • 「完全に落ちること」だと定義するなら、冗⻑化する • 99.9 % の信頼性を冗⻑化して 1-(1-0.999)*(1-0.999)=99.99999 とか • ⾃動化できる部分を増やす • リリース後、バーンレートが⾼かったら、すぐにロールバックできる • 承認スピードを向上させる • 承認へのラインを増やす • SLI に誤りがあれば SLI を⾒直す • SLI がビジネスの振る舞いを反映できているのか考える • オブザーバビリティを向上させる施策をうつ • 計装の場所を増やす • ユースケースを⾒直す • (特に)技術的例外とビジネス例外を混合していないかなど 参考: https://speakerdeck.com/ymotongpoo/reliability-objective-and-system-architecture

Slide 70

Slide 70 text

SLO とオンコール対応 • SLO を守るためにできること モニタリング・オブザーバビリティと例外 69 可⽤性(%) 年あたりダウンタイム ⽉あたりダウンタイム 週あたりダウンタイム ⽇あたりダウンタイム 90%(ワンナイン) 36.5⽇ 72時間 16.8時間 2.4時間 95% 18.25⽇ 36時間 8.4時間 1.2時間 99%(ツーナイン) 3.65⽇ 7.20時間 1.68時間 14.4分 99.5% 1.83⽇ 3.60時間 50.4分 7.2分 99.9%(スリーナイン) 8.76時間 43.8分 10.1分 1.44分 99.95% 4.38時間 21.56分 5.04分 43.2秒 99.99%(フォーナイン) 52.56分 4.38分 1.01分 8.64秒 99.995% 26.28分 2.16分 30.24秒 4.32秒 99.999%(ファイブナイン) 5.26分 25.9秒 6.05秒 864.3ミリ秒 可⽤性表 1 ヶ⽉の SLO を「99.9 %」としていたとき、ダウンタイム「43.8 分」で以下の対応を終えられれば OK 0. 問題発⽣ 1. アラート発報 2a. 担当者(アラート輪番が)アラートに反応 2b. 担当者(アラート輪番が)が担当チームにエスカレーション 3. 調査開始 4. 原因特定 5a. 必要/可能ならロールバック 5b. 問題箇所を修正 6a. アプリケーションならアーティファクトをビルド 6b. ミドルウェア等なら設定ファイルを検証 7a. デプロイ承認依頼 7b. 許可後、デプロイ依頼 7c. 修正をデプロイ 8. 問題が解消されたことを確認 SLO を守るためにできること • 「完全に落ちること」だと定義するなら、冗⻑化する • 99.9 % の信頼性を冗⻑化して 1-(1-0.999)*(1-0.999)=99.99999 とか • ⾃動化できる部分を増やす • リリース後、バーンレートが⾼かったら、すぐにロールバックできる • 承認スピードを向上させる • 承認へのラインを増やす • SLI に誤りがあれば SLI を⾒直す • SLI がビジネスの振る舞いを反映できているのか考える • オブザーバビリティを向上させる施策をうつ • 計装の場所を増やす • ユースケースを⾒直す • (特に)技術的例外とビジネス例外を混合していないかなど 本発表では、例外設計から SLO を守るためにできることを考える 参考: https://speakerdeck.com/ymotongpoo/reliability-objective-and-system-architecture

Slide 71

Slide 71 text

企画・開発・運⽤の⼀連 モニタリング・オブザーバビリティと例外設計から考えるログ設計 70 企画 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ エラーログ 運⽤ ・ アラート設計 監視 ・ SLO ・ バーンレート アラート 評価 正しいユースケース理解と例外設計をしなければならない ・ログやステータスコードが適切でなければ、SLO は誤った値になる ・誤った SLO によるオンコールはアラート疲れを引き起こし、無視するようになる

Slide 72

Slide 72 text

企画・開発・運⽤の⼀連 モニタリング・オブザーバビリティと例外設計から考えるログ設計 71 企画 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ エラーログ 運⽤ ・ アラート設計 監視 ・ SLO ・ バーンレート アラート 評価 正しいユースケース理解と例外設計をしなければならない ・ログやステータスコードが適切でなければ、SLO は誤った値になる ・誤った SLO によるオンコールはアラート疲れを引き起こし、無視するようになる 今回は、例外設計の話なので、エラーログに考える

Slide 73

Slide 73 text

「オニオンアーキテクチャ」と例外の振り返り(各層の例外) • Web API で各層と例外についての⼀例 モニタリング・オブザーバビリティと例外設計から考えるログ設計 72 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり

Slide 74

Slide 74 text

「オニオンアーキテクチャ」と例外の振り返り(各層の例外) • Web API で各層と例外についての⼀例 モニタリング・オブザーバビリティと例外設計から考えるログ設計 73 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり ログ出⼒はどこに書く︖

Slide 75

Slide 75 text

「オニオンアーキテクチャ」と例外の振り返り(各層の例外) • Web API で各層と例外についての⼀例 モニタリング・オブザーバビリティと例外設計から考えるログ設計 74 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり Domain 層か UseCase 層が候補

Slide 76

Slide 76 text

例外のログを書く場所について考える モニタリング・オブザーバビリティと例外設計から考えるログ設計 75 候補 理由 UseCase 層 • 例外の原因の推測が容易 • ログを⾒ればコードを特定できる状態 • ユースケースの仕様と重要度の⼀致が容易 • info/warn/error/critical を記述しやすい Domain 層 • ドメイン知識から throw する例外が明確になる • 予期しない例外なら Error/Critical なエラーを出す • 予期しないドメインの例外のログを書く場所が他にない • @RestControllerAdvice にログを出すこともできるが、技術的例外 とビジネス例外のログが混合する可能性がある 表︓候補と理由

Slide 77

Slide 77 text

「オニオンアーキテクチャ」と例外の振り返り(各層の例外) • Web API で各層と例外についての⼀例 モニタリング・オブザーバビリティと例外設計から考えるログ設計 76 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり Domain 層か UseCase 層が候補 interface XxxUseCase { fun execute(...): Zzz = throw NotImplementedError() } @Service class XxxUseCaseImpl(...) : XxxUseCase { override fun execute(...): Zzz { try { val a = domain.DomainObject(...) } catch (...) { // 記述しない場合もある log.stdout(”info", "xxxがyyyでした") throw ExpectedDomainObjectException } ... return z } }

Slide 78

Slide 78 text

「オニオンアーキテクチャ」と例外の振り返り(各層の例外) • Web API で各層と例外についての⼀例 モニタリング・オブザーバビリティと例外設計から考えるログ設計 77 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 Presentation 層 - @RestController アノテーション - エンドポイントの定義 - リクエスト/レスポンス UseCase 層 - @Service アノテーション - ドメインオブジェクトを組み合わせ てユースケースを実現する Domain 層 - ビジネスロジックをコードに落とし込 む場所 Infrastructure 層 - @Repository アノテーション - DB・外部 API とのやりとり Domain 層か UseCase 層が候補 class DomainObject private constructor(...) { companion object { fun newWithValidation(val a String, val b Int): DomainObject { // 予期するビジネス例外(記述しない場合もある) log.stdout(“info”, “vvvでbbbが発⽣しました”) throw DomainExpectedException(...) return DomainObject() } fun newYYY(...) { // 予期しないビジネス例外(記述する) log.stderr(“critical”, “zzzでxxxが発⽣しました”) throw UnexpectedException(...) return DomainObject() } } }

Slide 79

Slide 79 text

モニタリング・オブザーバビリティと例外 「モニタリング・オブザーバビリティと例外」まとめ • ⼊⾨モニタリング・オブザーバビリティ • 監視、O11y、SLI/SLOについて知った • 例外から考えるモニタリング・オブザーバビリティとログ • アーキテクチャをもとに、例外のログ出⼒箇所と内容について知った 78 「モニタリング・オブザーバビリティと例外」の範囲

Slide 80

Slide 80 text

• 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 ⽬次 79

Slide 81

Slide 81 text

例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 • 「例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践」⽬的 • 例外設計を Arrow-kt の Either 型でより実践的に • Spring Boot まで活⽤したときの説明 80 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート アラート 評価 「例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践」の範囲

Slide 82

Slide 82 text

例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 • Arrow-kt について • Kotlin に関数型のパラダイムを持ち込むライブラリ • 「Arrow brings idiomatic functional programming to Kotlin」 • さまざまな機能があるが、今回の発表では Either 型だけ利⽤ 81 出典: https://arrow-kt.io/

Slide 83

Slide 83 text

例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 • Arrow-kt の Either 型を⽤いたときの変化(1/3) • Kotlin の Exception について • Kotlin には検査例外がないので、コンパイルエラーでハンドリング忘れを防⽌できない • try/catch の判断を実装の内部までみる必要がある(予期する・しない例外も同様) 82 fun divide(a: Int, b: Int): Int { if (b == 0) { throw IllegalArgumentException("除数を 0 にしてはい けません") } return a / b } try { val result = divide(a, b) println(result) } catch (e: Exception) { println("${e.message}") } Exception による try catch Exception がメソッドシグネチャに含まれていない -> 呼び出し側は、Exception の発⽣を予測できない 呼び出し側が実装の詳細を知る必要がある -> try/catch を忘れる可能性がある

Slide 84

Slide 84 text

• Arrow-kt の Either 型を⽤いたときの変化(2/3) • Arrow-kt の Either 型 • 成功/失敗ケースを型で表現できる • Either 型の評価をしなければ値を利⽤できない 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 83 Either 型による値の評価(⼀例) メソッドシグネチャに Either 型が含まれている -> 呼び出し側が成功/失敗ケースを把握できる 呼び出し側は、Either の評価をしなければ値を利⽤ できない(コンパイルエラーが発⽣する) -> ハンドリングを忘れない fun divide(a: Int, b: Int): Either { return when (b == 0) { true -> “除数を 0 にしてはいけません”.left() false -> (a / b).right() } } val result = divide(a, b) when (result) { is Either.Right -> { println(“除算結果 ${result.value}") } is Either.Left -> { println(result.value) } }

Slide 85

Slide 85 text

• Arrow-kt の Either 型を⽤いたときの変化(3/3) • Sealed Interface と Either 型を組み合わせた⾃作エラー型 • Left 型を Sealed Interface で記述すると失敗パターンの⾃作エラー型を作れる • 複数の失敗ケースのハンドリングを簡潔に記述しやすくなる 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 84 Either 型による値の評価 成功パターンの型 戻り値の Either.Left を Sealed Interface にする Either data class ApiCallSuccess(val message: String) sealed interface ApiCallError { data class NotFound(val message: String) : ApiCallError object ServerError : ApiCallError } fun apiCall(): Either { return when (...) { // 条件式 A -> ApiCallSuccess(...).right() B -> ApiCallError.NotFound(...).left() C -> ApiCallError.ServerError.left() } } val result = apiCall() when (result) { is Either.Left -> when (val it = result.value) { is ApiCallError.NotFound -> ... is ApiCallError.ServerError -> ... } is Either.Right -> { ... } } 失敗パターンの⾃作エラー型(Sealed Interface) Either を評価(Right or Left)した後、 失敗ケースをさらに評価する

Slide 86

Slide 86 text

Arrow-kt を例外の分類へ適⽤(1/2) • 「技術的例外・ビジネス例外」と「予期する例外・予期しない例外」(再掲) • 前節(Arrow-kt の紹介前)までは、どのように例外を catch するかまでだった • 予期する/予期しない例外を catch するかは、実装の詳細を知る必要があった 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 85 技術的例外 ビジネス例外 予期する例外 予期しない例外 予期する 技術的例外 予期しない 技術的例外 予期しない ビジネス例外 予期する ビジネス例外

Slide 87

Slide 87 text

Arrow-kt を例外の分類へ適⽤(2/2) • Arrow-kt の Either 型を利⽤した分類 • 予期する -> Either 型(⾃作エラー型)、予期しない -> Exception に分類する • ハンドリングの必要性を Either 型から判断、Exception は全て⼤域脱出 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 86 技術的例外 ビジネス例外 予期する例外 予期しない例外 予期する 技術的例外 予期しない 技術的例外 予期しない ビジネス例外 予期する ビジネス例外 予期する -> Either 型 予期しない -> Exception

Slide 88

Slide 88 text

Kotlin Conf 2024 にて • 「Unlocking the Power of Arrow 2.0: A Comprehensive Guide」 • Arrow のメンテナによる発表 • 主に Arrow 2.0 についての発表で、Either と Exception についても⾔及 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 87 Kotlin Conf 2024 Simon Vergauwen (出典: https://kotlinconf.com/speakers/ea32e9a2-b68e-44c5-b4b7-6d2aa0dc3141/ )

Slide 89

Slide 89 text

「Unlocking the Power of Arrow 2.0: A Comprehensive Guide」について • 発表において • “Exception” と “Typed Errors” と呼ばれていた • 例外の分類 • “Exception” • “Fatal exception”, “Unexpected exception, non-business related” • 本発表における「予期しない技術的例外」 • “Typed Error” • “Prevent forgetting about business errors” • 本発表における「予期するビジネス例外」、⾃作エラー型 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 88

Slide 90

Slide 90 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 89 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リングする • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw

Slide 91

Slide 91 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 90 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リング • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw @RestController class XXXController(...) { @GetMapping(...) fun getXxx(...): ResponseEntity { val xxx = xxxUseCase.execute(xxx).getOrElse { throw XXXUseCaseErrorException(it) } return ResponseEntity(...) } data class XxxUseCaseErrorException(val error: XXXArticleUseCase.Error) : Exception() @ExceptionHandler(value = [XxxUseCaseErrorException::class]) fun onXxxUseCaseErrorException(e: XxxUseCaseErrorException): ResponseEntity<...> = // ⾃作エラー型の Sealed Interface で分岐 when (val error = e.error) { is yyy -> ResponseEntity<...>( GenericErrorModel(...), HttpStatus.NOT_FOUND ) is zzz -> ResponseEntity<...>( GenericErrorModel(...), HttpStatus.BAD_REQUEST ) } } • UseCase の戻り値が Either.Left のとき Exception ハンドリング • UseCase の戻り値が • Either.Right だったとき -> 正常系の Response • Either.Left だったときに -> 異常系の Response • UseCase の Either.Left の Sealed Interface に合わせて 分岐 Presentation 層

Slide 92

Slide 92 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 91 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リング • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw @RestControllerAdvice class GlobalExceptionHandleController { @ExceptionHandler(NoResourceUnexpectedBusinessException: :class) fun noResourceFoundExceptionHandler(e: NoResourcenexpectedBusinessException): ResponseEntity<...> { return ResponseEntity<...>( GenericErrorModel( errors = GenericErrorModelErrors( body = listOf(”...") ), ), HttpStatus.NOT_FOUND ) } ... } • ⼤域脱出した例外を catch してレスポンス(修正前と同じ) • 予期しない技術的・ビジネス例外をハンドリングする • Exception はすべてここでハンドリングされるのが明確になる Presentation 層

Slide 93

Slide 93 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 92 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リング • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw UseCase 層 interface XxxUseCase { fun execute(...): Either = throw NotImplementedError() // ⾃作エラー型 sealed interface Error { data class Yyy(...) : Error data class Zzz(...) : Error } } @Service class XxxUseCaseImpl(...) : XxxUseCase { override fun execute(...): Either { // 失敗ケースの場合は、UseCase の⾃作エラー型に詰め替える val aaa = DomainObject.new(...).getOrElse { return XxxUseCase.Error.Yyy(...).left() } // ... return domainObejct.right() } } • UseCase の⾃作エラー型 • ユースケースの失敗ケース に合わせて作成 • ドメインオブジェクトで予期する例 外(⾃作エラー型)が発⽣した 場合、UseCase の⾃作エラー 型に詰め替え • 成功した場合は Either.Right を return

Slide 94

Slide 94 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 93 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リング • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw Domain 層 class DomainObject private constructor(...) { companion object { fun newWithValidation(val a String, val b Int): Either { // 予期するビジネス例外は⾃作エラーで詰め替え return XxxError.Yyy(...).left() // ... return DomainObject().right() } fun newYYY(...): Either<...>{ // 予期しないビジネス例外は throw throw UnexpectedException(...) // ... return DomainObject().right } } } • 予期する例外の場合は、 Domain の⾃作エラー型で詰 め替え • このコード例では Sealed Interface を省略 • 予期しない例外の場合は、 Exception を throw

Slide 95

Slide 95 text

Spring Boot × Arrow-kt(Either 型) • Web API で組み込んだ時の⼀例 例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 94 Domain 層 UseCase 層 Infrastructure 層 Presentation 層 • UseCase の戻り値である Either 型を評価してレスポンス • 予期する例外(⾃作エ ラー型)をハンドリング • ⼤域脱出した例外を catch し てレスポンス • 予期しない例外をハンド リング • Domain の戻り値の Either 型を評価して処理の継続と中 断を評価 • Either.Right -> 継続 • Either.Left -> 中断 • ドメインルールから、予期する例 外を判断 • 予期する例外 -> Either 型(⾃作エラー 型)を return • 予期しない例外 -> Exception を throw • Domain のインタフェースを 実装する • 予期する例外は Domain の Sealed インタフェースから定義 • 予期しない例外は Exception を throw Infrastructure 層 @Repository class XxxRepositoryImpl(...): XxxRepository { override fun findBySlug(slug: Slug): Either { val x = Hoge() ... // 予期する例外(⾃作エラー型) return XxxRepository.Error.Nnn(...).left() return yyy.right } }

Slide 96

Slide 96 text

例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践 • 「例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践」まとめ • 例外設計を Arrow-kt の Either 型でより実践的になることを説明 • 予期する/予期しないが Either と Exception で役割を分けた • Spring Boot まで活⽤したとき、簡潔な実装にできる 95 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート アラート 評価 「例外設計を Kotlin(× Arrow-kt)の⾃作エラー型で実践」の範囲

Slide 97

Slide 97 text

• まとめ ⽬次 96

Slide 98

Slide 98 text

まとめ 「例外設計について考えて Kotlin で実践する」のまとめ • 例外設計を考えることで、プロダクト開発へ好影響を考えた • 例外設計について、Kotlin で実践した 97 企画 ・ 要件定義 ・ ユースケースの定義 ・ SLO の合意 開発 ・ 例外を実装 ・ ログ 運⽤ ・ アラート設計 ・ SLO ・ バーンアラート ・アラート ・評価 フィードバック

Slide 99

Slide 99 text

まとめ 「例外設計について考えて Kotlin で実践する」のまとめ • 例外設計を考える背景 • 例外設計は、⼀連のプロダクト開発の影響を与える • 技術的例外・ビジネス例外と予期する例外・予期しない例外 • 例外を分類する⽬的を把握し、例外を 4 種類に分類 • 例外設計とモデリング • DDD の⽂化から、例外を把握し、コードに落とし込む • 監視・オブザーバビリティと例外 • SLO 観点からログについて考えた • Kotlin(SpringBoot×Arrow-kt)で実践 • 例外の種別を Arrow-kt で表現し実装を拡張させた 98

Slide 100

Slide 100 text

まとめ 宣伝 • 内容が重厚になったため、説明不⾜になった部分が多くあります • コードも断⽚的で、⽂化的な部分はスライドでは表現しきれませんでした • 「ハンズオンで学ぶサーバーサイド Kotlin」は、発表内容を超初歩的に実践し ています • 発表をもとに、例外設計に興味を持ち、Kotlin(Spring Boot × Arrow-kt)で 実践したい⽅は是⾮読んでみてください。Like をいただけると嬉しいです🙏 99 「ハンズオンで学ぶサーバーサイド Kotlin」

Slide 101

Slide 101 text

参考⽂献 書籍(1/2) • 和⽥ 卓⼈ (監修), Kevlin Henney (編集), 夏⽬ ⼤ (翻訳)、『プログラマが知るべき97のこ と』, オライリージャパン, 2010/12/18, 276ページ • バートランド メイヤー (著), 酒匂 寛 (翻訳), 『オブジェクト指向⼊⾨ 第2版: 原則・コンセプト』, 翔泳社, 2007/1/1, 904ページ • Andrew Hunt (著), David Thomas (著), 村上雅章 (翻訳), 『達⼈プログラマー(第2版): 熟達に向けたあなたの旅』, オーム社, 2020/11/21, 422ページ • Dan Bergh Johnsson (著), Daniel Deogun (著), Daniel Sawano (著), 須⽥智之 (翻訳), 『セキュア・バイ・デザイン 安全なソフトウェア設計』, マイナビ出版, 2021/9/24, 560 ページ • Tom Long (著), 秋勇紀 (翻訳), ⾼⽥新⼭ (翻訳), ⼭本⼤祐 (監修) , 『 Good Code, Bad Code 〜持続可能な開発のためのソフトウェアエンジニア的思考』, 秀和システム, 2023/1/28, 432ページ • 松岡幸⼀郎(著), 『ドメイン駆動設計 モデリング/実装ガイド』 • 松岡幸⼀郎(著), 『ドメイン駆動設計 サンプルコード&FAQ』 100

Slide 102

Slide 102 text

参考⽂献 書籍(2/2) • ヴォーン・ヴァーノン (著), 髙⽊ 正弘 (翻訳), 『実践ドメイン駆動設』, 翔泳社, 2015/3/16, 616ページ • エリック エヴァンス (著), 和智 右桂 (翻訳), 牧野 祐⼦ (翻訳), 『エリック・エヴァンスのドメイン駆動設計: ソフトウェアの核⼼にある複雑さに⽴ち向かう』,翔泳社, 2011/4/1, 538ページ • Vladimir Khorikov (著), 須⽥智之 (翻訳), 『単体テストの考え⽅/使い⽅』, 2022/12/28, 416 ページ • Scott Wlaschin, 『Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#』, Pragmatic Bookshelf, 2018/2/13, 312ページ • Mike Julian (著), 松浦 隼⼈ (翻訳), 『⼊⾨ 監視 ―モダンなモニタリングのためのデザインパター ン』,Pragmatic Bookshelf , オライリー・ジャパン, 2019/1/17, 228ページ • Charity Majors (著), Liz Fong-Jones (著), George Miranda (著), ⼤⾕ 和紀 (翻訳), ⼭⼝ 能迪 (翻訳), 『オブザーバビリティ・エンジニアリング』, オライリー・ジャパン, 2023/1/27, 336ページ • Alex Hidalgo (著), ⼭⼝ 能迪 (監修, 翻訳), 成⽥ 昇司 (翻訳), 『SLO サービスレベル⽬標 ―SLI、 SLO、エラーバジェット導⼊の実践ガイド』, オライリー・ジャパン, 2023/7/11, 432ページ 101

Slide 103

Slide 103 text

参考⽂献 ウェブサイト • scrapbox, 例外設計, https://scrapbox.io/kawasima/%E4%BE%8B%E5%A4%96%E8%A8%AD%E8%A8% 88, 6⽉14⽇(⾦) • Zenn, ハンズオンで学ぶサーバーサイド Kotlin(Spring Boot&Arrow)v2.0.1, https://zenn.dev/msksgm/books/implementing-server-side-kotlin-development, 6⽉ 14⽇(⾦) • NDC { Security }, Dan Bergh Johnsson, https://ndc-security.com/speakers/dan- bergh-johnsson, 6⽉14⽇(⾦) • Kotlin Conf 2024, SIMON VERGAUWEN, https://kotlinconf.com/speakers/ea32e9a2- b68e-44c5-b4b7-6d2aa0dc3141/, 6⽉14⽇(⾦) • Speaker Deck,信頼性⽬標とシステムアーキテクチャー / Reliability Objective and System Architecture, https://speakerdeck.com/ymotongpoo/reliability-objective-and-system- architecture 6⽉14⽇(⾦) • Arrow, Arrow, https://arrow-kt.io/, 6⽉16⽇(⽇) • Kotlin Conf 2024, Unlocking the Power of Arrow 2.0: A Comprehensive Guide, https://kotlinconf.com/talks/586193/, 6⽉19⽇(⽔) 102