学習履歴一覧

183件中の 26-50件 を表示

Design It! 5章 アーキテクチャ上重要な要求を掘り下げる 読了

やったこと Design It! 5章 アーキテクチャ上重要な要求を掘り下げる 読了 学んだこと ポイント アーキテクチャに影響を与える要求とは アーキテクチャの制約をうまく利用する 品質特性シナリオとは ASRワークブックとは 学び アーキテクチャに多大な影響を与える要求に注視し、これを発見するために手を尽くす 適切に選択された制約は、問題を単純化し、アーキテクチャ設計を容易にする 一方で、非常に厳しい制約は、アーキテクチャ設計を必要以上に複雑化する 設計判断(特に開発初期)は、後々制約と化す しかし、制約と化した設計判断は変更できる 与えられた制約か、制約と化した設計判断なのか、区別できるようにする 品質特性の説明するために、品質特性シナリオを書く テスト可能で、測定可能であると良い 特定した「アーキテクチャに多大な影響を与える要求」は、ASRワークブックとして文書に記録する ## 所感 要求の内、アーキテクチャに大きな影響を与えるものに注視するのは、非常に納得感のある話だった。 アーキテクチャを判断する際、優先的に考慮するべき事項として、貴重な情報元になりそう。 与えられた制約か、制約と化した設計判断なのか区別するというのは、あまり考えたことが無かった。 制約とかした設計判断を、勝手に「変えられないもの」と思い込み、「変更する」という選択肢が見えなくなってしまう状態は非常に怖い。 そうならないためにも、与えられた制約か、制約と化した設計判断なのか区別することは重要だと考えるようになった。 初期の設計判断が、後々に制約と化す様はイメージしやすかった。 制約を活用することで選択肢を絞る話、バックログを作成する際にスコープを絞る話に似ていると感じた。 メモ ステークホルダーの要求の内、アーキテクチャに多大な影響を与える要求を アーキテクチャ上重要な要求、(ASR) と呼称する。 アーキテクトは、ASRを明らかにする責務を負う。 ステークホルダーも気付いていない、隠れたASRは無いか ステークホルダーの要求の内、ASRに該当するのか ASRだと判定するには、以下の要求分類について考える。 制約 変更できない設計判断 通常は与えられるものだが、選択することもある 品質特性 影響を与える機能 アーキテクチャにおいて特別な注意を必要とするフィーチャーや機能 その他、影響を及ぼすもの 例) 時間、知識、経験、スキル、社内政治など 制約を活用して設計の選択肢を絞る ここで言う制約とは、 変更できない設計判断を指す。 適切に選択された制約は、問題を単純化し、アーキテクチャ設計を容易にする。 一方で、非常に厳しい制約は、アーキテクチャ設計を必要以上に複雑化する。 制約によって、人、プロセス、予算、スケジュールに関する判断を制限する。 制約の例) 技術的な制約 プログラミング言語の選択(JVM上で動くか、など) OS・プラットフォーム(特定のOSで動くか) ビジネス上の制約 チーム編成 予算・スケジュール 法的制約 制約について、決定事項とその発生源を簡潔にまとめておく。 まとめた制約の例) 制約 発生源 タイプ コンテキスト OSSとして開発しなければならない。 市長 ビジネス 市はオープンデータポリシーを持ち、市民はソースコードにアクセスできなければならない。 ... ... ... ... 制約は一度決定すると、 100%交渉不可能となる。 そのため、受け入れる制約は慎重に決定する。 ソフトウェア開発が進むにつれて、設計判断は(変更が困難という意味で)制約のようになる。 特に、開発初期の設計判断は、開発終盤で大きな制約となる可能性がある ステークホルダーから与えられた制約とは異なり、設計判断は(困難を伴うかもしれないが)変更することはできる。 そのため、「与えられた制約」なのか、「制約と化した設計判断」なのかは区別するようにする。 品質特性を定義する 品質特性とは、ソフトウェアシステムの外部から見える特性とそのシステムの運用に対する期待を表す。 つまり、アーキテクチャ特性。 ASRを考える際、品質特性は常について回る。 品質特性は、システム運用の文脈においては非機能要件と同義と言える。 一方で、品質要求の文脈においては、品質特性は機能的な意味合いもあるため、非機能要件とは呼べない。 また、ソフトウェアアーキテクチャ設計の際は、機能、制約、品質特性を区別した方がよい。 品質特性シナリオとして記録する 品質特性を明確に説明するために、品質特性シナリオを使用する。 品質特性シナリオは、ソフトウェアシステムが特定の環境のコンテキスト内でどのように動作するかを説明したもの。 ◆品質特性シナリオの例 品質特性 シナリオ 優先度 可用性 RFPDBが応答しない場合、システムは障害を記録し、3秒以内に古いデータで応答する必要がある 高 可用性 ユーザーは、RFP検索機能を、年間を通して平均99%の確率で行うことが出来る 高 信頼性 RFPの更新は、変更後24時間以内にアプリケーションに反映される 低 ... ... ... 良い品質特性シナリオは、以下を満たす。 誤解の余地なく、誰でも理解できる文で要求の意図を伝えることが出来る 正確 測定可能 具体的かつ測定可能な応答測定とするように努める ステークホルダーと会話を始める前に、応答測定のたたき台を用意する。 たたき台をもとに、ステークホルダーと議論を重ね、ステークホルダーの共感を呼ぶ応答測定を発見する。 よい応答測定は、テストしやすい。 言い換えると、シナリオを使ったテストを作成できないのであれば、そのシナリオは特定の計測可能な応答測定を持っておらず、詳細化が不十分だと言える。 機能要求の分類を探す アーキテクチャ設計に影響を与える機能要求を、影響力のある機能要求と呼ぶ。 影響力のある機能要求を発見するのはセンスを伴う。 書籍では、以下の手順を紹介している。 アーキテクチャについての現在の考えを要約した概念的なアーキテクチャをスケッチする 大量の機能要求リストから、同じものをグループ化し、代表的な機能要求のみ抽出する 同じアーキテクチャ要素内に実装されている可能性のある機能要求を探す 実装が難しそうな機能要求を探す 価値が高く、優先度の高い機能要求を探す 特定した問題の種類ごとに、概念的なアーキテクチャを辿りながらそれぞれの要求グループをどう達成背売るか示す 実装方法がすぐに示せない場合、ASRである可能性がある それ以外のアーキテクチャに影響するものを見つけ出す 例 知見のある技術 RoRしか経験が無ければ、他のフレームワークの採用は見送られやすい => 設計判断 コスト(金) システムにかける予算が十分になく、貧弱なマシンしか使えないのであれば、それに合った機能や言語・フレームワークを使用せざるを得ない など 必要な情報を掘り下げる ASRは、プロダクトバックログから発見しやすい。 ユーザーストーリーから、品質特性シナリオを作成し、詳細を明らかにしていく。 そのためにも、ステークホルダーと話をする。 ASRの発見に役立つ手法の例 GQMワークショップ ステークホルダーインタビュー 前提リスト ミニ品質特性ワークショップ インセプションデッキ ステークホルダーの目標を理解するためにも、積極的傾聴能力を磨く ASRワークブックを組み立てる 特定したASRは、 ASRワークブックとして文書に記録する。 開発初期は頻繁に更新される。 アーキテクチャがまとまっていくと、更新頻度が減る。代わりに、参照頻度が高くなり、重要な文書に昇華する。 まれに、実行可能なテストやソースコードが、ASRワークブックにとって代わる

ソフトウェアアーキテクチャ

2023年12月28日(木)

1.5時間

書籍 「Design It!」 2章 読了

やったこと 書籍 「Design It!」 2章 読了 学んだこと ポイント デザイン思考とは デザイン思考 4つの原則 4つのデザインマインドセット 学び デザイン思考とは、人間に焦点を当てた、問題解決のアプローチ 設計判断に影響を与える人々を特定し、彼らに焦点を当てることで、解決すべき本質的な問題を発見する デザイン思考には、「人間性」「曖昧性」「再デザイン」「触感性」4つの原則が存在する デザインに当たっては、4つの異なるデザインマインドセットが存在する 各マインドセットごとに、プラクティスが存在する プラクティスを適用することで、新たな学びを得ることが出来、次のフィールドループに活用できる 気づき 「アーキテクチャは他の人と共有できなければ、それを形にすることはできない」の辺り アーキテクチャに限らず、アイデア全般にも同じことが言えそう 思い描いたアイデアを第三者に(伝わるように)説明できなければ、理解をえられず、支援も期待できない デザインマインドセットの「評価」は、ユースケースごとに実施すると良いかも? ## メモ アーキテクチャを設計するために、解決すべき問題を明らかにし、解決策を探求する。 これらに役立つ手法が デザイン思考 と呼ばれる手法。 デザイン思考は、人間に焦点を当てた、問題解決のためのアプローチ。 設計判断に影響を与える人々を特定することで、解決すべき本質的な問題が見えてくる。 2.1 デザイン思考の4つの原則 デザイン思考は、影響を受ける人々の視点から問題や解決策を考える手法。 デザイン思考は、以下4つの普遍的な原則を持つ。 人間性の規則(The Human Rule) すべてのデザイン活動は究極的には社会的な性質を持つ 曖昧性の規則(The Ambiguity Rule) デザイン思考者は曖昧性を保全せねばならない 再デザインの規則(The Re-design Rule) すべてのデザインは再デザインである 触感性の規則(The Tangibility Rule) 手で触れられるアイデアを作ることは、常にコミュニケーションを促進する 上記4原則は、プログラム設計、UI設計をはじめ、ソフトウェアアーキテクチャなど、幅広い分野に応用できる。 人間のための設計 人のためにソフトウェアを設計する そして、人と共に設計する エンドユーザーと同じく、ステークホルダー全員を大事にする アーキテクトは、チームと協働してアーキテクチャを設計する 象牙の塔のアーキテクトになってはならない 全ての設計判断は、他の人々に理解され、共有できなければならない 曖昧さを保つ アーキテクトは設計しすぎないこと(必要最小限のアーキテクチャ)。 優先すべき品質特性をどうやって満たすか、品質特性を促進sヌルためにリスクをどう減らすかだけを示す それ以外の設計判断はすべて、後続の設計者にゆだねる つまり、責任を持てる限りにおいて設計判断を遅らせる こうした曖昧さは、後々に選択の余地を残すことにつながる。 設計とは再設計である アーキテクトは、過去の知見やパターンを最大限活用する。 過去の知見やパターンを活用(改良)して、現在の問題の解決に役立てる。 アーキテクチャをタンジブルにする アーキテクチャを誰かと共有する際は、コード以外の方法で形にすること。 設計の根拠、設計判断が及ぼす効果への理解を促進し、よりよい議論につなげるため コード以外の方法には、図、プロトタイプ、モデル作成、制御フローの図示など、様々な手法が検討できる。 アーキテクチャを他の人と共有できなければ、それを形にすることはできない。 デザインマインドセットを身に着ける デザインに当たっては、異なる4つのマインドセット(視点?)がある。 理解~問題を理解する ステークホルダーから情報を収集し、問題の理解に努める 言い換えると、要求を理解するよう努める 同時に、ビジネス目標と品質特性を調査し、優先順位とトレードオフを把握しなければならない 探求~アイデアを探求する 最適な品質特性と構造の組み合わせをひたすら探求する 作成 ~ 形にする アイデアを共有する、試すために、アイデアを形にする 評価~妥当かを評価する ユースケースや実験、リスク調査などを通して、設計判断を評価する 評価を行い、現在の理解を基準に設計判断か妥当かどうか判断する 各マインドセットには、それぞれプラクティスが存在する。 プラクティスを適用することによって、新たな学びを得ることが出来、次にフィードバックループ デザインマインドセットは、強力なフィードバックループを持つプロセスを前提としている。 Think、Do、Check 日々の変化をいち早く察知し、状況に対応するために、素早いフィードバックループを持つ設計アプローチが必要となる。 本書では、素早いフィードバックループとして、Think-Do-Checkサイクルを紹介している。 Think-Do-Check サイクルでは、以下を繰り返す。 Think ... 考えるステップ。ビジネス目標、品質特性、トレードオフ、リスクなどを考える Do ... 実行ステップ。モデルやプロトタイプ作成、計画実行など Check ... Do ステップで達成したことを批評的に検証し、次のアクションを決定する Check で得られた洞察から、次に何をするべきかを得て、Thinkステップに戻る 一つのマインドセットに焦点を当てて、このサイクルを回す。 マインドセットを適用する デザインマインドセットは、任意の順で実行できる。 そして、マインドセットは頻繁に移り変わる。 一度の会話で、複数回マインドセットを切り替えることもある 四つのマインドセットを区別し、認識できるようになること。 すると、意図的にマインドセットを切り替えることが出来るようになる。

ソフトウェアアーキテクチャ

2023年12月17日(日)

1.0時間

書籍 「Design It!」 1章 読了

やったこと 学んだこと ポイント ソフトウェアアーキテクトとは どんな人? 何をする人? アーキテクチャの基本構造3つ 品質特性とは 学び ソフトウェアアーキテクトは、広い視野を持って、技術以上のことを扱う 構造にもいくつか種類がある 「構造の種類が異なる」ことを認識できると、様々な性質を考える際に役立つ ## メモ 1.1 ソフトウェアアーキテクトが行うこと エンジニアリングの観点から問題を定義 ステークホルダー(プロダクトマネージャーやプロジェクトマネージャーなど)と協力し、ソフトウェアのビジネス目標と要求を定義する。 ステークホルダーと協力し、ソフトウェアのビジネス目標と要求を定義する システムに対する品質特性を定義する アーキテクチャに影響を与える設計上の制約やフィーチャーに注視する システムを分割し、責務を割り当てる 以下の効果を得るため、ソフトウェアシステムを、責務別に分解する。 品質特性をはじめとするシステム要求を満たす戦略を立てられるようになる より開発しやすくなる 小さいものほど、設計、開発、テストなどが容易になる 広い視野を持って全体に目を向ける システム全体について考える場合、理想と現実のバランスを見極める(≒トレードオフについて考える)必要がある。 設計判断が、ソフトウェアに関わる要素(ユーザー、開発・運用チーム、ハードウェア、開発目的)に与える影響を広くとらえ、うまくバランスを取ることが求められる。 品質特性間のトレードオフを決定する 全ての品質特性を満たすことは非現実的。 そのため、何かをあきらめる必要がある(≒トレードオフを考える)。 例)可用性のためにハードウェアを複数購入すると、コストをあきらめることになる コストを引き換えに、可用性をとった形 こういったトレードオフを把握し、最適なトレードオフを、ステークホルダーと共に選択する。 技術的負債を管理する 技術的負債 ... 現在の設計と、継続的に価値を届けるために必要な設計とのギャップ 開発チームは、必要に応じて技術的負債を負う(リリースを早めるため、など)。 そして、定期的に負債を返済する。 定期的に負債を返済するために、アーキテクトは、技術的負債を適切に管理する。 また、アーキテクトは、技術的負債を可視化する。 これによって、ステークホルダーは、技術的負債に対してアクションの必要性を判断できる。 チームのアーキテクチャスキルを高める アーキテクチャの専門家として、チームのアーキテクチャスキル向上に努める。 適切なタイミングで、 ペア作業や共有ドキュメント作成を通して、 建設的な批評を行う これにより、チームはアーキテクチャを理解できるようになり、優れたソフトウェアシステム開発に貢献する。 1.2 ソフトウェアアーキテクチャとは何か システムのソフトウェアアーキテクチャとは 望まれる品質特性やその他の性質を促進するためにソフトウェアをどう構成するかということに対する、重要な設計判断が集まったもの 「品質特性を促進する」とは、それがソフトウェアシステムの中に現れるよう働きかけることを指す。 品質特性の一つが「コスト」であれば、「納期に間に合い、予算内で、残業に頼り過ぎない」というのが正しいアーキテクチャと思われる 基本構造を定義する 「要素」 ... ソフトウェアの基本構成単位 「関係」 ... 「要素」が役割を全うする際にどのように連携するかを示したもの 「構造」 ... 「要素」と「要素」を、「関係で繋いだもの」 ![[Drawing 2023-12-17 12.20.50.excalidraw]] 要素と関係の分類には、以下の例がある。 概要 要素の例 関係の例 モジュール 設計時に現れる構造 クラス、パッケージ、レイヤ、モジュール、構成ファイル、DBなど ~を使用する、~の使用を許可する、~に依存する コンポーネント&コネクター (C&C) 実行時に現れる構造 オブジェクト、コネクション、スレッド、プロセス、層など ~を呼び出す、~を購読する、~をパイプする、~を戻す 割り当て (対応構造) 上記構造群の対応を示す構造 サーバー、センサーラップトップ、LB、コンテナ、チーム、人 ~上で実行する、~内で実行する、~の責務を負う、~を開発する、~を格納する 異なる種類の構造を知っていると、システム内で必要となる様々な性質を考える際に役立つ。 モジュール構造を使って、テスト容易性や保守性について考えることが出来る C&C構造は、可用性やパフォーマンスなどの実行時の問題を考えるのに役立つ 構造は、システムの形状を決定する。 システムの形状は、利用者が体験する部分であり、重要である。 品質特性を見通す 品質特性とは ステークホルダーがソフトウェアシステムの良さを判断するための、外部から見える特徴。 例) 拡張容易性、可用性、保守性、テスト容易性 など(≒アーキテクチャ特性) ステークホルダーが望む、ソフトウェアシステムが満たすべき品質特性を、設計に組み込んでいくことになる。 そこには、当然トレードオフが存在する。 1.3 チームのアーキテクトになる アーキテクトは、チームの役割や名刺上の肩書ではなく、心の持ち方。 ソフトウェアシステムの構造に影響を与える決定を行う人であれば、誰しもアーキテクト代行である。 プログラマーからソフトウェアアーキテクトへの道 アーキテクトは、様々な開発経験を通じて、技術的責任の範囲を広げる。 アーキテクチャに対する責任が増えると、徐々にコードに触れる機会が減少する。 しかし、決して「コードを書かない」状態になってはならない。 今まで携わってきたプロジェクトに対して、以下の問いに答え、振り返る機会を設ける。 ステークホルダーは誰だったか、主要なビジネス目標は何だったか ビジネス目標を満たすために取られた全体のソリューションはどのようなものだったか どんな技術に取り組んだか 最大のリスクは何だったか、それらをどのように克服したか 今もう一度やり直すとしたら、どのようにするか 振り返り、学びをまとめよう。 1.4 素晴らしいソフトウェアを作り上げる ソフトウェアアーキテクチャの手助け 大きな問題をより小さく、より管理しやすい問題へと変える 人々の協働のやり方を示す 上手なコミュニケーション方法の提案 複雑な問題について会話するための語彙を提供する 話していることを互いに理解するために、基本思想や語彙を提供する フィーチャーや機能以外のモノ(特に品質特性)にも目を向ける コストのかかる間違いを避けるのに役立つ 後々大きな問題を引き起こす可能性のある、難しい部分を発見する 変化への柔軟な対応を可能にする

ソフトウェアアーキテクチャ

2023年12月17日(日)

1.0時間

書籍 「Design It!」 3章 読了

やったこと 書籍 「Design It!」 3章 読了 学んだこと ポイント デザイン思考を使用した、解決策の模索方法 デザインスイートスポットとは リスクを活用する 学び 適切なデザインマインドセットを選択し、リスク軽減策発見に役立てる デザインスイートスポットとは、アーキテクチャ設計に費やすべき、最も費用対効果の高い時間 リスクを以下のように活用する リスクを明らかにする 頭をよぎった「嫌な予感」を、「条件」と「結果」に落とし込む デザインマインドセットを使用して、リスク軽減策を発見する リスクを許容範囲内まで軽減出来たら、アーキテクチャ設計に専念する メモ デザイン思考を使用することで、実験・検証を繰り返しつつ、複雑な問題の解決策を模索できる。 ソフトウェアシステムの持つリスクを考えることで、より幅広いデザイン戦略の一環としてデザインマインドセットを選ぶ方法について学んでゆく。 3.1. 満足のいく設計を見つける アーキテクチャを設計する前に、ソフトウェアシステムで解決したい問題を定義する。 その問題を完璧に定義することは不可能に近い。 そのため、最適で合理的な設計を追求する代わりに、必要最低限のアーキテクチャを発見することを目指す。 必要最低限のアーキテクチャは、以下の活動を通して発見していく。 解決策を実験とみなす 問題の解決策を検証する実験と考える リスクを減らすことに専念する アーキテクトは、失敗する可能性を常に考慮して設計する アーキテクチャの失敗は、全ての失敗につながるため 問題を単純化する 問題を単純化すると、解決策も単純化する 定型問題(既知の解決策を利用できる問題)に当てはめることで、先人の知見を活用できるようになる 素早く繰り返し、素早く学ぶ 問題と解決策を同時に考える 問題は、常に解決策を念頭に置いて定義される ... とのこと 問題を理解するためには、解決策を探る 解決策をうまく探るためには、問題に対する理解を深めなければならない => 問題と解決策を同時に考える必要がある 3.2 どのくらい前払いの設計を行うかを決める 十分なアーキテクチャへの投資は、将来の修正コスト削減につながる。 一方で、アーキテクチャ設計に時間を割きすぎることは、実装時間を遅らせ、結果的に価値提供も遅れることになる。 ここで、「アーキテクチャ設計に費やす最適な時間いくらか?」という問題が発生する。 最適な時間は、ソフトウェアシステムの大きさと要求の多様性に応じて変化する。 この時間のことを。デザインスイートスポットと呼ぶ。 デザインスイートスポットを見つける スイートスポットは、アーキテクチャ設計と修正作業時間の損益分岐点。 研究によって、プロジェクトスケジュールの約20%未満辺りが最適と言われている。 以下 は、100 日間の初期開発スケジュールを想定した数値 アーキテクチャに費やす日数 修正に費やす日数 合計のスケジュール日数 - 5 38 143 17 21 138 ★ 33 7 140 また、以下のことが分かっている。 ソフトウェアシステムが大きくなるほど、アーキテクチャ設計への投資効果が大きくなる 小規模のソフトウェアシステムでは、アーキテクチャ設計への投資効果がほとんどない これは、コードを書きなおすほうが早いことを意味する アーキテクチャ設計への投資を怠ると、後半の修正コストが膨大となる アーキテクチャ設計へ投資するほど、必要な手直しは少なくなる スイートスポットは、ソフトウェアシステムの大きさ、複雑さに加え、要求の変動制(要求が、将来的に変更する可能性)にも依存する。 変更される可能性が高い場合、判断を遅らせる。 3.3 リスクに道案内させる リスクは、成功を妨げるかもしれないものを測るための優れた指標。 アーキテクチャに専念すべきタイミングを決定する指標として、リスクを使用できる。 つまり、十分にリスクを軽減できたタイミングで、アーキテクチャに専念する まとめると、以下の通り リスクを明らかにし、大きな問題を起こすリスクを特定する 特定したリスクを軽減するために、デザインマインドセットを選択し、対応方法を探す うまくリスクを軽減出来たら、パッシブデザインへと移行し、アーキテクチャに専念する 条件と結果を明らかにする リスクを、「条件」と「結果」という二つの要素で説明する。 条件 : 事実である「何か」 結果 : 発生する「よくないこと」 明らかとなった「条件」と「結果」は、リスクに対して次のアクションを決定する良い情報源となる。 リスクを使用してデザインマインドセットを選択する 設計を通じて、リスクを軽減する。 「嫌な予感」をうまく「条件」と「結果」に落とし込むことが出来れば、設計の助けとなる。 デザインマインドセットは、リスクを軽減する戦略立てに役立つ。 以下の問いかけを参考に、試すべき適切なマインドセットを判断する。 問い どこにリスクがあるか 試すべきマインドセット ステークホルダーや他のアクターをもっと深く理解するべきか 問題 理解 十分な代替案を検討したか 解決策 探求 ステークホルダーは、設計コンセプトを十分理解しているか、また、アーキテクチャを確認できているか コミュニケーション 作成 設計判断を行う必要があるか 設計判断や設計の適合性 評価 リスクが軽減されたら、パッシブデザインへと移行する リスクを十分軽減できたら、アクティブデザインからパッシブデザインへと移行する。 アクティブデザイン ... リスクを減らすべく、フィードバックループを積極的に回す パッシブデザイン ... 稼働しているアーキテクチャを観察し、必要に応じて措置を講じる パッシブデザインにて、アーキテクチャから重大なリスクを確認した場合、設計における仮定が誤っていたことを示す。 この場合、再びアクティブデザインに切り替え、新しい仮定に基づいてアーキテクチャを調整する。 3.4 設計計画を立てる アーキテクチャに費やす時間全般について計画を立てる。 分析は前もって行うか? 後々の変化を予期出来ているか? いつコードを書き始めるべきか? 良い設計計画は、期待を設定し、詳細を説明する者になっている。 また、設計計画は正式なスケジュールである必要はない。 インセプションデッキのような、さっと作成できるドキュメントを使用して、計画を形にする。 設計を止める条件 必要な設計成果物 アーキテクチャを文書化する方法を決定し、全員に伝達する タイムライン プロジェクトスケジュール内の重要なマイルストーンを記述する 少なくとも、アーキテクチャ上重要な要求のレビュー、設計案のレビュー、評価実施のマイルストーンは含めること 上位リスク リスク上位を設計計画に組み入れる 様々なタイミングで、リスク上位を見直す 概念的なアーキテクチャ設計 潜在的な解決策から始める

ソフトウェアアーキテクチャ

2023年12月17日(日)

1.0時間

API デザインパターン 24章 バージョン管理と互換性 読了

やったこと API デザインパターン 24章 バージョン管理と互換性 読了 学んだこと ポイント 互換性の意味 後方互換性の意味 バージョン管理の手段 バージョン管理のトレードオフ 学び 「互換性」は、二つの異なるコンポーネントが、互いに正常にやり取りできるかどうか 新バージョンのAPIが、旧バージョンAPIを使用したコードでも問題なく動作する場合、後方互換が有るという 後方互換のある変更を、既存のバージョンに加える 後方互換のある変更を、新バージョンとしてリリースする 後方互換の無い変更を、既存のバージョンに加える(基本的に選択しない) 後方互換の無い変更を、新バージョンとしてリリースする コードを変更するときに、後方互換を維持するか議論する必要が生まれる 機能追加、バグ修正など バージョン管理の手段 永続的な安定性 アジャイルインスタビリティ セマンティックバージョン管理方式 バージョン管理のトレードオフ 細かさと単純さ 安定性と新機能 満足度と偏在度 メモ WebAPIの更新は難しい。 API利用者に損害を与えることなくWeb APIを変更するにはどうしたらよいか。 「利用者に対応してもらう」は受け入れてもらえない。 「そもそも変更しない」も不可能に近い。 セキュリティや法律の問題で改修を余儀なくされる、など ではどうするか。 概要 チェックポイントやバージョンを導入し、バージョン別にデプロイできる。 利用者に問題を引き起こす可能性のある変更は、別バージョンとしてデプロイする。 バージョン管理の一番の目的は、API利用者に多くの機能を提供しつつ、不便さを最小限に抑えること。 互換性 二つの異なるコンポーネントが、互いに正常にやり取りできるかどうかを示す。 Web API の場合、クライアント、とサーバー間の通信を指す。 APIサーバーに変更を加えても、クライアント側で期待通りの動作をするのであれば、変更前と変更後のバージョンには互換性がある、と言える。 では、互換性(ここでは後方互換性)を持つかどうか、言い換えると、クライアントコードに影響を与えるかは、どう判断するか。 後方互換性を定義する 「後方互換性を有する」を一意に定義することはできない。 言い換えると、API利用者の期待によって変わりえる。 そのため、API提供者側で、APIの「後方互換性」を定義する必要がある。 後方互換性を考える必要がある、一般的な問題を見ていく。 機能追加 機能追加の際、既存のバージョンを拡張するか、新バージョンを発行するか。 新機能を、どう実装するか。 既存のリソースのフィールドを増やす形で実装する メモリがシビアなIoTなどでは、ちょっとしたデータの追加がクラッシュに直結する可能性がある 新しいリソース、またはメソッドとして実装 既存のリソースに影響はない API利用者が、全リソースに対して何らかの処理(バックアップなど)を行っている場合、新規リソースの追加は処理対象の漏れにつながる 既存のリソースに影響を与えるような、リソース、メソッドの新規追加 利用者に新機能の学習を強いることになる いずれの方法でも、問題は残る。 バグ修正 バグ修正によるリリースも、場合によっては後方互換性を考慮する必要がある。 明確なエラー(500系エラーの修正)は、後方互換性がある。 一方で、400エラーを想定したところ、無意味な値 ({} など)を返していたようなエラーは難しい。 API利用者が、 {} が返ってくることを想定したコードを書いている可能性は非常に高い もし 400 エラーを返すようにすると、 {} を期待した既存のコードは動かなくなる つまり、400エラーを返すような修正は、後方互換性が無い 修正方法に正解はなく、「場合による」としか言えない。 強制的な変更の取り扱い 外的要因により、APIの変更を余儀なくされることがある。 例)GDPR導入によるGDPR対応 この時、既存のバージョンを修正するのか、新バージョンとしてデプロイするのか選択する余地がある。 この判断にも正解はなく、「場合による」としか言えない。 新バージョンをデプロイし、徐々に新バージョンへ移行するよう促す 既存のバージョンを修正し、「GDPR施行後、未対応の旧バージョンが使用される」というリスクをシャットアウトする 大事なのは、API利用者の負担を極力減らしつつ、義務付けられた変更を行うこと。 可能な限り事前通知を行い、利用者の作業を最小限に抑え、利用者への影響を少なくすること。 表からは見えない変更 性能の最適化、提供するサービスの精度向上など。 後方互換性があると見なされることが多いが、無いという方向でも成り立つ。 非同期処理を行っていたAPIのレスポンスが、突如 0.1秒未満になると、同期処理が現実味を帯びる 機械学習モデルの変更によるレスポンスの大幅な変化 利用者次第で、後方互換性有とも無しともとられる。 セマンティックスの変化 ここで言う「セマンティックス」は、「動作の変更」や「APIの概念の意味」を指す。 導入する事柄で評価が大幅に変化する。 大きな影響の例 : 新しい権限モデル導入による動作の変化 小さな影響の例 : 配列の順序 既存の利用者と、変更が利用者のコードに与える影響を調べ、利用者に受け入れられるか検討すること。 検討によって、変更を既存のバージョンに組み込むか、独立した新バージョンとしてデプロイするか決定する。 実装 新バージョンの導入方法について学ぶ バージョンの名前は? 旧バージョンは、非推奨となるまで、何日ほど存続させるか? 永続的な安定性 後方互換性のない必要が生じたときに初めて、バージョン管理方式について考えるやり方。 つまり、後方互換性のある変更は、常にバージョンNにマージされる。 バージョン管理の仕組みや方式を全く考慮しない場合に発生しやすい。 無意識に思いつきがちな方式であり、安定する傾向にある。 一方で、細かな変更が重要となるAPI(IoT用など)では受け入れられない。 IoTは、ちょっとしたデータの追加でメモリオーバーフローが発生しかねないため、特定のAPIバージョンでフリーズする機能を必要とする また、後方互換のない変更が増えると、たくさんのバージョンが発生する。 アジャイルインスタビリティ それぞれのAPIバージョンに、ライフサイクルを示すステータスを付与する方式。 Preview => Current => Deprecated => Deleted Preview ステータスの API バージョンは、開発者が自由に変更できる。 API利用者は、明日にも振る舞いが変わること理解する必要がある。 完成されたものになると、Current ステータスへ移行する。 必須となるパッチ当てやバグ修正だけを行い、互換性のない変更は実装しない。 互換性のない変更は、次のバージョンに追加していく。 古いバージョンのAPIを削除したい場合は、まず Deprecated へ移行し、削除時期を周知する。 時期が来たらAPIを削除し、ステータスを Deleted へ移行する。 Currentステータスのバージョンと、 Previewステータスのバージョンは、常に一つだけ。 これにより、有効なバージョンの数を最小限に抑えつつ、継続的な改良を進めるための強制力として機能させることが出来る。 どのバージョンも、将来的には Deprecated から Deleted へ移行する点が欠点。 つまり、現行バージョンがずっと機能し続ける保証は全くない。 新機能が待ち望まれ、安定している期間が短くても問題ないケースで有効。 反対に、長期的な安定性が求められ、新機能もあまり重要ではないようなケースでは使いにくく感じる。 セマンティックバージョン管理方式 X.Y.Z X : メジャーバージョン。後方互換性のない変更を加える時にインクリメントされる Y : マイナーバージョン。後方互換性のある変更を加える時にインクリメントされる Z : パッチバージョン。後方互換性のある、主にバグ修正などに伴う変更を加える時にインクリメントされる バージョンが膨大に増え、利用者を混乱させる可能性がある点が欠点。 利用者はできるだけ長い間、特定の非常に細かいバージョンを使い続けたがる傾向にある。 また、全バージョンが永遠にサポートされるわけではない。 非推奨ポリシーを設け、アジャイルインスタビリティのように Deprecated => Deleted となることもある。 利用者は最新バージョンを追求することも、特定のバージョンを使い続けることもできる。 トレードオフ 細かさと単純さ きめ細かなバージョンを提供するか、後方互換性を維持した少数のバージョンを提供するか、のトレードオフ。 細かさ : きめ細かなバージョンを提供する 特定の安定バージョンをずっと使い続けたい利用者には刺さる 一方で、新規API利用者には、どのバージョンを使えば良いのか惑わせることになる 単純さ : 後方互換性を維持した少数のバージョンを提供する バージョンが少ないため、最適な選択肢を選びやすい 一方で、特定の安定バージョンを求める利用者には刺さらない 安定性と新機能 APIに変更を加える機会を減らして長期間安定させるか、APIにどんどん機能を追加して、一定の期間だけ安定させるか、のトレードオフ。 満足度と偏在度 利用者は多岐にわたるため、API提供側のポリシーが、全利用者に受け入れられる可能性は非常に低い。 一部の利用者の満足度を最大化する方向で意思決定を行うのか、全利用者の使い勝手を最大化する方向で意思決定を行うのか、のトレードオフ。 利用者を「使えない」「不満足」「大丈夫」「満足」の4つに分類すると、以下の通り。 一部の利用者の満足度を最大化する方向 : 「満足」利用者を最大化する事に重きを置く。代わりに、「使えない」「不満足」が増える 全利用者の使い勝手を最大化する : 「大丈夫」利用者を最大化することに重きを置く。代わりに、「満足」が減る 扱うこと バージョン管理とは 互換性とは 後方互換性をどう定義するか バージョン管理手法に伴うトレードオフ Web API のための一般的なバージョン管理手法の概要 まとめ バージョン管理は、API設計者が時間の経過とともにAPIを変更・進化させながら、できる限り利用者に不利益を与えず、多くの改良を提供するためのツール 互換性とは、あるバージョンでのAPIに対して書かれたコードが、別のバージョンに対しても引き続き機能する事を意味する 新しいバージョンは、前のバージョンに対して書かれたコードが期待通りに動作する場合、「後方互換である」という API設計者が利用者の期待をどのように定義するかは、解釈によって変わる 一見無害に見えること(バグ修正など)が後方互換性のない変更に繋がったりする APIの利用者が利用者の期待をどのように定義するかは、解釈によって変わる

WebAPI

2023年12月10日(日)

2.0時間

API デザインパターン 28章 リソースリビジョン 読了

やったこと API デザインパターン 28章 リソースリビジョン 読了 学んだこと ポイント リビジョン機能とは何か どうやって過去の状態に戻るか リビジョンを作成する方法 リビジョンを操作する方法 子リソースをもつリソース(つまり親リソース)のリビジョンの扱い 学び リビジョン機能とは、リソースを過去の状態に戻す機能 リソースに、リビジョンを一意に識別できる 識別子を付与する リビジョンの作成方法には、暗黙的な作成と、明示的な作成の二つがある 暗黙的な作成では、何らかのイベントを条件に、API側で自動的にリビジョンを作成する 明示的な作成では、リビジョンを作成するカスタムメソッドを提供し、利用者の判断でリビジョンを作成してもらう リビジョンの操作 リビジョン取得機能は、標準の GET メソッドを拡張する形で提供する リビジョンの復元、削除機能は、カスタムメソッドにて提供する リビジョンの復元は、復元したいリビジョンのリソースを、最新のリビジョンとして新規作成することで実現する 復元の際、リソースの履歴を削除したり変更したりしてはならない ビジネス要求次第では、子リソースもリビジョン管理対象に含める ただし、APIの複雑化や、より多くのストレージ容量が必要になることを覚悟する 親リソースには、リビジョン機能を提供しないという選択肢もあり 言い換えると、子リソースを持たない独立しているリソースのみリビジョン機能を提供する メモ 基本的にリソースは、最新の状態のみ提供する。 しかし、状況によっては、過去の状態のリソースが必要になることもある。 本章では、リソースを、以前のリビジョンにロールバック可能にする手段を見ていく。 これまでのAPIでは、リソースの過去の状態は考慮しなかった。 過去のリソースの状態を扱うと、リソースの履歴を時系列で見ることが出来る。 これは、利用者にとってメリットになり得る。 概要 リソースの過去の状態を表現するために、「リソースリビジョン」という概念を利用する。 リソースリビジョン : 一意の識別子と作成時のタイムスタンプでラベル付けられた、リソースのスナップショット リソースリビジョンは、リソースに以下二つのフィールドを追加することで実現可能。 リビジョン識別子 スナップショットが取得されたときのタイムスタンプ 実装 リビジョン識別子 リビジョン識別子は、一般的な識別子と同じくランダムな文字列を使用する。 以下の選択肢はあまり考えられない。 連番 リビジョンを削除すると、1,2,4 のように歯抜けとなり、利用者に混乱を招く タイムスタンプ 稀に、タイムスタンプ(≒識別子)が衝突する可能性がある リビジョンを作成する リビジョンは、暗黙的に作成するか、明示的に作成するか、選択することが出来る。 リビジョンの生成方法は、API全体で一貫していることが重要。 暗黙的なリビジョン API側で、特定のイベントに合わせて、自動的にリビジョンを作成する戦略。 リソースのデータが更新されたとき cron のような定期実行 特定のマイルストーン 特定のフィールドが更新されたとき 特定のカスタムメソッドが実行されたとき など ビジネスの要求に応じて、リビジョンを作成する最適なタイミングを模索すること。 リビジョンは、減らす方向よりも増やす方向をお勧めする。 明示的なリビジョン リビジョンを作成できるカスタムメソッドを用意し、利用者が明示的に作成できるようにする戦略。 リビジョンの作成は、API利用者が制御できる(責務も負う)。 特定のリビジョンを取得する 標準の getメソッドを拡張して、過去のリビジョンを指定してリソースを取得する手段を提供する。 Messageリソースのリビジョン 1234 を指定する例 GET chatRooms/1/messages/2@1234 リビジョンを指定しない場合、レスポンスのIDフィールドにはリビジョン情報を含めない( revisionIdフィールドに、リビジョン情報を含める)。 リビジョンを指定した場合、レスポンスのIDフィールドにはリビジョン情報を含める( revisionIdフィールドに、リビジョン情報を含める)。 方針としては、リクエスト時に指定するIDと、レスポンスのIDを一致させること。 リビジョンのリストを取得する リソースのリビジョン一覧を返す、カスタムメソッドを提供する。 以前のリビジョンを復元する restoreRevision カスタムメソッドを使用して、リソースを、ある過去の状態に戻す。 指定されたリビジョンのリソースをコピーし、最新のリビジョンとして新規作成することで実現する。 この機能では、リソースの履歴を削除したり変更してはならない。 リビジョンを削除する 特定のリビジョンに、誤って機密データや機微な情報を含めてしまった場合に使用する。 リソースのリビジョンを削除する、カスタムメソッドを提供する。 この時、標準の delete メソッドを拡張する形で実装してはならない。 標準の delete メソッドはリソースの削除を目的としており、 リビジョンを削除する機能とは区別する必要がある 区別しないと、利用者が混乱する 削除は危険な操作のため、混乱をできるだけ避けたい 現在(≒最新)のリビジョンを削除する場合、412 エラー、またはそれに相当するエラーを返す。 これにより、一つしか存在しないリビジョンを削除するケースも同時に解決できる また、リビジョンに論理削除の仕組みを提供するべきではない。 子リソースを処理する 子リソースをもつリソース(つまり親リソース)のリビジョンを作成する場合、子リソースをどう扱うか。 正解はなく、ビジネス要件次第。 「子リソースごとまとめてリビジョンに含める」というビジネス要件があれば、子リソースごと扱うよう拡張することになる 処理が複雑化し、より多くのストレージが必要になるのは間違いない 親リソースはリビジョン機能の対象外として定義するのも有り トレードオフ リビジョン機能の提供と、APIの複雑化のトレードオフ。 特に、階層を考慮したストレージは、より複雑で、多くのストレージ容量を要する。 扱うこと リソースのリビジョンを、時間の経過とともに変更しながら安全に保存する方法 個々のリビジョンを識別する方法 暗黙的または明示的にリビジョンを作成する方法 利用可能なリビジョンをリストアップし、特定のリビジョンを取得する方法 前のリビジョンに戻す方法 リビジョン指定可能なリソースの子リソースをどうすべきか まとめ リソースのリビジョンは、リソースのスナップショットとして機能する リビジョンの識別子は、他のリソース同様ランダムな値を用いる タイムスタンプや数字が増えるだけの値を用いてはならない リビジョンは、暗黙的、あるいは明示的に作成できる 暗黙的の例 : リソースが変更されるたび 明示的な例 : 利用者からの要求に応じて リソースのリビジョンを指定する場合、@ の後ろにリビジョン識別子を指定するとよい  例) GET /chatRooms/1234@5678 ただし、リビジョンのリスト機能と削除機能は、カスタムメソッドで提供する リソースは、カスタムメソッドを使用して、以下の手順で前のリビジョンに復元できる 前のリビジョンと同じリビジョンを作成 作成したリビジョンをアクティブリビジョンとしてマークする

WebAPI

2023年12月10日(日)

1.0時間

API デザインパターン 29章 リクエストの再試行 読了

やったこと API デザインパターン 29章 リクエストの再試行 読了 学んだこと ポイント どんな時に再試行するべきか クライアント側で再試行する際の有力なアルゴリズムは何か API側で、クライアントに、再試行タイミングを伝える方法 学び 一過性、またはタイミングに起因するエラーは、再試行によってエラーを解消できる可能性がある クライアント側で再試行する場合は、指数バックオフを実装すると良い 実装の際は、以下の制限を加えること 最大試行回数 最大遅延時間 また、以下を実装すると良い ランダムジッター Retry-After HTTP ヘッダーを使用して、クライアント側に、再試行タイミングを伝える 値は、「遅延させる秒数」が有力 メモ Web APIで発生するエラーの内、クライアントではコントロールできない何かに起因するエラーは、リクエストの再試行にて解決することがある。 しかし、むやみに再試行すると、別の問題を引き起こす可能性がある。 本章では、クライアントが、いつ、どのように再試行するか、よりよいメカニズムを探る。 クライアントではコントロールできない何かに起因するエラーの例 サーバー側の過負荷 サービス提供に必要なコンポーネントが、(予定されていた)一時的なダウンタイムに入っている 概要 「多くのリクエストにレスポンスを返しつつ、できるだけ再試行を少なくする」ことを目標とする。 そのためには、以下の基本方針に従う。 クライアント側で指数バックオフを実装し、再試行してもらう API側で最適な再試行タイミングを把握できるなら、再試行タイミングをクライアントに指示する 例 API が一分に1回だけしか処理を実行できないなら、API側は次回実行できるタイミングを把握できるはず 実装 再試行可能か 一般的に、エラーレスポンスには、以下の3つのカテゴリーがある。 絶対に再試行できないもの 403,405,412,501 辺り 再試行できるかもしれないが慎重に検討すべきもの 500, 502, 504 辺り 何の問題もなく再試行できるもの 408, 421, 425, 429 503 辺り エラーコードから、再試行するべきか判断すること。 指数バックオフ 指数バックオフには、以下の制限を追加すると良い。 最大試行回数 最大遅延時間 最大遅延時間を超えて待機しないようにする また、以下を実装することが推奨される。 ランダムジッターの導入 指数バックオフに従った時間に、ランダムな値を加えた時間だけ待機するようにする 複数のリクエストが、同じタイミングで指数バックオフを実行し、同一時間でリクエストが集中する状況を回避する これにより、指数バックオフが最終的に失敗するという保証を得ることが出来る。 これは、「何度か再試行しましたが、成功する応答を得られませんでした」という意味になる。 Retry-After API側で把握している最適な再試行タイミングをクライアントに伝える場合、 Retry-After HTTPヘッダーを使用する。 書式は、以下の二つのいずれか。 タイムスタンプ 遅延させる秒数 秒数指定の方が、利用者に受け入れられやすい。 トレードオフ 指数バックオフの実装は、クライアント側にゆだねられることになる。 言い換えると、API提供側では制御できない 指数バックオフの導入によって失うものはないとされている。 つまり、現時点で欠点は発見されていない。 扱うこと 失敗したAPIリクエストを再試行しても安全かどうかを判断する方法 再試行のタイミングを決めるための高度な指数バックオフ方式 リクエストの集中を回避する方法 APIがクライアントに再試行のタイミングを指示できる方法 まとめ 一過性、あるいはタイミングに起因するエラーは再試行する価値がある 一方、恒久的な状態に依存するエラー(403など)は、再試行する価値は薄い 自動的にリクエストを再試行する場合、指数バックオフを利用すること リクエストが同じ時間に到着することに起因する問題(殺到する群れ問題)を避けることが出来る APIが、再試行するべき(≒成功する可能性のある)タイミングを知っている場合、Retry-After HTTPヘッダを使用して、クライアントに伝える

WebAPI

2023年12月10日(日)

1.0時間

API デザインパターン 27章 リクエストの検証 読了

やったこと API デザインパターン 27章 リクエストの検証 読了 学んだこと ポイント 検証リクエストとは何か どうやって実装するか 検証リクエストの振る舞いで気を付けるべきことは何か 外部リソースへ依存する部分はどう検証するか 学び 検証リクエストは、取り返しのつかないような、危険なAPIリクエストの動作を検証するための機能 検証用であることを示すプロパティ ( validateOnly など)を、リクエストボディに追加することで実装する 検証リクエスト 完全に安全かつ冪等性を持つメソッドになること いかなるデータ変更も、副作用も引き起こしてはならなず、 同じ条件で実行した検証メソッドの結果は、常に同じであること 検証リクエストは、外部依存も含め、可能な限り多くの面で検証し、通常(≒検証ではない)リクエストと同じ値を返す 外部依存が検証機能を提供している場合、それを使用する 提供していない場合、該当する外部依存の検証はスキップする ## 気づき 検証リクエストは、validateOnly に true を設定する必要がある点 検証したいような慎重なエンジニアは、恐らくドキュメントを読み込み、検証手段がないか調べると思われる そう考えると、明示的に true の場合のみ検証する ... という仕組みは納得できた メモ 対象とする問題 安全なAPIリクエストは、実行することで試すことが出来る。 しかし、取り返しがつかないような、危険なAPIリクエストは、とりあえず実行...という手段はとれない。 そのため、APIリクエストが意図したものか検証できる機能が必要になる。 概要 API利用者が、APIリクエストのプレビューを取得できる機能を提供する。 この機能は、検証用リクエストと示すフラグを一つ設けることで十分達成できる。 validateOnly などの boolean型の変数を一つ設けるイメージ。 これにより、本番リクエストと同等の処理を検証(権限、一意性制約に反しないか、参照整合性に問題はないかなど)を実施できる。 実装 検証リクエストは、結果として、完全に安全かつ冪等性を持つメソッドになること。 いかなるデータ変更も、副作用も引き起こしてはならなず、 同じ条件で実行した検証メソッドの結果は、常に同じであること。 validateOnly のデフォルト値は false にする。 言い換えると、利用者が validateOnly を明示的に true と設定した場合のみ、検証処理を実施する。 デフォルトを true とすると、常にフラグを設定しないと、通常(≒検証ではない)のリクエストを実行できない 明示的な指定を強制すると、大体間違いにつながる 検証リクエストでは、外部依存も含め、可能な限り多くの面で検証することが求められる。 レスポンスも、通常(≒検証ではない)リクエストと同じ値を返し、エラーコードも同じにすることが求められる。 外部依存関係 検証したい処理の中に、外部依存が存在する場合、どうやって安全性と冪等性を担保するか。 外部依存が検証機能を提供しているのであれば、これを利用する。 提供していないのであれば、その部分の検証はスキップする。 検証リクエストに求められる、以下の点は必ず守ること。 検証リクエストは、結果として、完全に安全かつ冪等性を持つメソッドになること。 いかなるデータ変更も、副作用も引き起こしてはならなず、 同じ条件で実行した検証メソッドの結果は、常に同じであること。 扱うこと なぜいくつかのAPIメソッドは、リクエスト前に検証する必要があるのか 検証リクエストにおける外部サービスとのやり取りをどのように管理するか 検証リクエストにおけるメソッドの特殊な副作用に対処する まとめ APIリクエストの中には、非常な危険なものもある そういったリクエストは、安全のため、事前に検証できる方法を提供するべきである 検証機能は、 validateOnly のような、単純な boolean 型の値で提供する システムには何の影響も与えない 検証リクエストをサポートするAPIメソッドが外部サービスとやりとりすることは十分あり得る 可能な限り外部依存を検証するべき ただし、全外部依存の検証は不可能であることは明示する

WebAPI

2023年12月10日(日)

0.5時間

API デザインパターン 26章 リクエストの重複実行回避 読了

やったこと API デザインパターン 26章 リクエストの重複実行回避 読了 学んだこと ポイント どのようなときに、リクエストの重複実行回避が必要になるか どうやって重複回避を実現するか リクエスト識別子は自動生成するべきか、ユーザーに設定させるべきか キャッシュは最新化するべきか 学び リクエストの重複実行回避機能は、冪等性を持たず、重複させたくない処理に対して必要となる 以下二つをAPI側でキャッシュすることで、リクエストの重複を検証できる リクエスト識別子 リクエストボディのフィンガープリント リクエスト識別子は、ユーザーに設定させるべき 利用者が、常に再試行を意図しているとは限らず、意図を知る方法がない 「重複実行を回避したい」という利用者の明確な意図を知るためにも、識別子は利用者の手で設定してもらう キャッシュは最新化するべきではない キャッシュ最新化により、再試行処理をリクエストしたクライアントにとって、意図しない結果が生成される可能性がある これは再試行処理の目的から逸脱している ## メモ ネットワーク経由のAPI リクエストは、失敗する可能性を常にはらんでいる。 場合によっては、APIリクエスト失敗を再試行処理が必要になる。 冪等性を持つリクエストなら問題ないが、冪等性のないリクエストの場合、処理を重複させない、安全な再試行方法が必要となる。 Web API で APIの実行に失敗した時に、安全にリクエストを再試行する仕組みを見ていく。 対象とする問題 ネットワーク経由のAPI リクエストは、失敗する可能性を常にはらんでいる。 本章では、APIレスポンスが正常に届かない状況を取り扱う。 レスポンスが届かなければ、基本的に再試行することになる。 冪等性を持つリクエストは再試行すればよい。 冪等性を持たないリクエストを何も考えずに再試行すると、リソースが重複する恐れがある。 リソースの重複を回避するためには、重複リクエストを防ぐ仕組みが必要となる。 概要 重複リクエストを避けるため、一度だけ実行されることを保証したいリクエストに、一意な識別子(以降、リクエスト識別子と呼称する)を付与する方法を考える。 識別子を比較することで、リクエストが既に処理されたか検証する。 重複を発見した場合、エラーを返すのではなく、正常に終了した時のレスポンスを返す。 実装 リクエスト識別子 リクエスト識別子は、クライアント側のリクエストプロパティに含める。 そのため、クライアント側でリクエスト識別子を決定する。 任意の識別子を許してしまうと、あまりよくない識別子を使用される恐れがあるため、API側である程度誘導すると良い。 フォーマット指定や、ランダムな値の入力を推奨(または強制)する API サーバー側は、無効なリクエスト識別子が入力されたリクエストに対して、 400 エラーを返す。 導出した識別子は使わない 導出した識別子とは、リクエストから算出するような識別子のこと。 クライアントに識別子を設定させず、処理の裏側でリクエストから識別子を算出し、設定する方法。 つまり、デフォルトで重複実行を回避する戦略。 しかし、利用者の意図を確実に知る方法はないため、デフォルトでは重複実行を回避するべきではない。 「重複実行を回避したい」という利用者の明確な意図を知るためにも、識別子は利用者の手で設定してもらう。 レスポンスをキャッシュする API側は、以下をキャッシュする。 「処理に成功して生成したレスポンス 上記レスポンスに対応するリクエストのリクエスト識別子 新規リクエストが届いたら、届いたリクエストのリクエスト識別子がキャッシュされていないかチェックする。 キャッシュされていれば、対応するレスポンスを返却する。 キャッシュされていなければ、処理を行い、結果をキャッシュした後にレスポンスを返す。 キャッシュの容量は有限のため、一定期間で削除することになる。 基本的に、失敗したらすぐに再試行されると考えるべき。 最初は5分程度に設定し、利用者の行動パターンから、キャッシュ期間を調整すると良い。 一貫性 キャッシュの導入は、最新ではないデータを扱う可能性が生まれることを意味する。 キャッシュを最新化して処理を行うと、再試行を試みたクライアントの意図しないレスポンスが生成される可能性がある。 リクエストの再試行という目的を考えると、一貫性を犠牲にしてでも、キャッシュを最新化しない方が良い。 リクエスト識別子の衝突 クライアント側でリクエスト識別子を指定してもらう以上、リクエスト識別子の衝突は十分発生し得る。 この問題は、リクエストボディのフィンガープリントを比較することで解決できる。 レスポンスキャッシュ時、リクエストボディのハッシュ値をフィンガープリントとして一緒にキャッシュする APIは、再試行リクエストを受け取ると、以下を比較する リクエスト識別子 リクエストボディのフィンガープリント 両方一致している場合、同一のリクエストとみなし、キャッシュを返却する 片方でも一致しない場合、 409 エラーを返す トレードオフ 重複実行回避機能の提供と、リクエストの重複実行回避ロジック実装によるAPIの複雑さがトレードオフ。 重複実行回避機能は、常に必要というわけではない。 必要に応じて追加するのでも十分。 扱うこと リクエスト識別子を使用して重複したリクエストの実行を防ぐ方法 リクエスト識別子が衝突しないようにし、結果を混乱させないようにする方法 リクエストとレスポンスの一貫性とのキャッシュとのバランス まとめ リクエストはいつでも失敗する可能性がある 従って、APIから、確認されたレスポンスが送られてこない限り、リクエストが処理されたかどうかを確認する方法はない 「リクエスト識別子」 APIを使うクライアントによって生成される API リクエストの識別子であり、リクエストの重複実行を識別できる リクエスト識別子と、そのリクエストのレスポンスをキャッシュすることで、重複リクエストを排除する キャッシュは永続保管することはなく、有効期限や無効化パラメーターを用いてキャッシュを制御する 有効期限と無効化パラメーターを慎重に決める必要がある リクエスト識別子と一緒にリクエストの内容のフィンガープリントもチェックする必要がある リクエスト識別子の衝突による異常な動作を避けるため

WebAPI

2023年12月10日(日)

1.0時間

API デザインパターン 25章 論理削除 読了

やったこと API デザインパターン 25章 論理削除 読了 学んだこと ポイント 論理削除とは 論理削除の実装方法 論理削除実装の際に考えるべきこと 学び 論理削除とは、後から復元可能な削除方法 以下の方法で実装する 論理削除フラグを使って、削除状態を制御 リソースのステータスに 削除ステータスを追加する 論理削除は標準の delete メソッドで実装。ほかにも、以下を実装する 復元用の、undelete カスタムメソッド 物理削除用の expunge カスタムメソッド 論理削除実装の際は、期限切れや参照整合性など、いくつか追加で考えることがある 標準の delete メソッドに適用される参照整合性ガイドラインは、論理削除されたリソースにも同様に適用する 気づき メモ 標準の delete メソッドの目的は、リソースを削除する事。 この時、 物理削除だけではなく、ゴミ箱のように復元できるような削除方法も選択できる。 復元できるような削除方法である「論理削除」について見ていく。 対象とする問題 ユーザーは、何らかの理由で削除を取り消したい場合がある。 フィルタリングのミス (APIの削除リクエストを叩くような)スクリプトのミス こういった状況を想定し、復元できる手段を残す。 概要 論理削除を実装する主な手段は以下の通り。 論理削除を示す deleted フラグ 既存のリソース状態を示すパラメータに、 deleted を追加する 実装 論理削除は、標準の delete メソッドに実装することになる。 論理削除実装に伴い、以下も必要となる。 物理削除用の expunge カスタムメソッド 復元用の undelete メソッド 他の標準メソッドの動作を変更 例 標準の list メソッドにて、 論理削除されたリソースを除外する 削除済みの指定方法 boolean型のフラグを使用する方法 デフォルトは false 状態フィールド リソースの状態を示すプロパティに、論理削除ステータスを追加する方法 論理削除を取り消すとき、どの状態へ遷移させるかが問題となる 標準メソッド修正する 論理削除の実装に合わせて、必要に応じて、標準メソッドを変更する。 GET 論理削除情報も含め、通常通りリソースを返却する。 つまり、論理削除されたリソースへの GET で 404 を返してはならない 「IDを指定して論理削除されたリソースの GET をリクエストする」のは、リソースの削除状態を確認するという目的と考えられるため LIST デフォルトでは、論理削除されたリソースは除外して、リソース群を返却する。 論理削除されたリソースも含めたリソース群を要求できるようにしたい場合、リクエストインターフェースを拡張することになる。 includeDeleted フラグなど。 フィルタリングで実装しないこと。 全ての LIST がフィルタリングをサポートしているわけではない フィルタリングは、結果として、常にデータを減らすことになる includeDeletedフラグは、反対にデータを増やすことになる API の目的に一貫性と明確さを保つ意味合いもある。 DELETE 論理削除に合わせて、 フラグを変更する処理に変更する。 論理削除済みのリソースに対して DELETE を要求された場合は、命令型と宣言型(7章)のどちらを取るかという、APIの一貫性に従う。 一般的には、期待通りにリクエストを実行できなかったことを示す、412エラーとする 復元する undelete カスタムメソッドを用意し、論理削除フラグを変更する。 未削除のリソースに対して undelete を要求された場合は、命令型と宣言型(7章)のどちらを取るかという、APIの一貫性に従う。 一般的には、期待通りにリクエストを実行できなかったことを示す、412 エラーとする 物理削除する expurge カスタムメソッドを用意し、物理削除を実装する。 未削除のリソースに対して expurge を要求された場合、 削除フラグの如何に関わらず削除してしまう。 期限切れ 定期的にゴミ箱を空にしたい場合などに使用する。 論理削除をサポートするリソースに、有効期限を表すフィールドを設ける。 論理削除状態以外の状態のとき、有効期限を表すフィールドはNullになる。 参照整合性 論理削除されたリソースは、他のリソースから参照可能かどうか、決めておく必要がある。 正解はなく、他のAPIと同じルールを適用することが重要。 参照整合性を保つために削除を禁止しているのであれば、同じ制約を論理削除にも課す ... など バージョンを介した論理削除の追加 後から、論理削除機能を追加する場合、どうやって安全に機能を追加するか。 結論は、正解はなく、状況による。 後方互換性が無いと判断される可能性を考慮し、論理削除実装の際はメジャーバージョンのアップを考慮しておくと良い。 扱うこと 倫理削除とは?それがいつ有効か? リソースに削除マークを付け、実際には削除されていないことを示す方法 論理削除をサポートするリソースの標準メソッドに必要な修正 論理削除されたリソースの削除を取り消す方法 削除されたリソースを永久に削除(物理削除)する方法 参照整合性の管理 まとめ 論理削除とは、リソースを削除済みとしてマークする機能 deleted フラグで表現することが多い 既にリソースの状態を表すパラメータを導入しているのなら、そこに deleted を追加するのも有り 論理削除をサポートする標準メソッドは、いくつかの小さな変更が必要 論理削除されたリソースに対して get をリクエストした場合、404を返してはならない 論理削除されたリソースには、復元用の undelete カスタムメソッド、 物理削除用の expunge カスタムメソッドを提供する 標準の delete メソッドに適用される参照整合性ガイドラインは、論理削除されたリソースにも同様に適用しなくてはならない 参照されるリソースのカスケード削除など 一貫性を維持するため

WebAPI

2023年12月10日(日)

1.0時間

API デザインパターン 30章 リクエストの認証 読了

やったこと API デザインパターン 30章 リクエストの認証 読了 学んだこと ポイント APIリクエストを認証するための3要件 上記3要件を満たす認証メカニズム 署名に入力するペイロード トレードオフ 学び APIリクエストを認証する3要件は「発信元の証明」「整合性」「否認防止」 デジタル署名は、上記3要件を満たす デジタル署名に入力するペイロードは、リクエスト・フィンガープリントを入力する 「HTTPメソッド」「パス」「ホスト」「HTTPボディのハッシュ値」 デジタル署名は、ユースケース次第では過剰な仕組みと判断される 特に「否認防止」機能 否認防止機能が不要な場合、軽量な代替手段を選択できる メモ 公開鍵の交換とデジタル署名を使用した、全APIリクエストを認証する方法と、その理由を追う。 対象とする問題 APIリクエストが、正規の利用者からのものだとどうやって判断するか。 以下の3つの要件を考慮することで判断する。 発信元の証明 あるユーザーAと称するリクエストが、本当にユーザーAから発信されたリクエストなのか確認する あるユーザーAのみが提供できる情報を確認することで実現する 整合性 受け取ったリクエスト情報が、(データの破損や改ざんなどで)送信時から変更されていないことを保証する 否認防止 リクエスト送信元が、リクエストを送信した事実を否認できないこと 言い換えると、リクエスト送信元が、確実にリクエストを送信した事実を証明すること 概要 デジタル署名技術は、上記3つの要件をすべて満たしており、本件に最適。 デジタル署名をWeb APIに組み込むには、以下の3ステップが必要。 クライアント側にて、公開鍵・秘密鍵のペアを作成する クライアントは、生成した公開鍵を、APIサーバー側に登録する 秘密鍵を使用して、デジタル署名の生成を始める 実装 認証情報の生成 ssh-keygen コマンドなどで、クライアント側にて公開鍵・秘密鍵のペアを作成する。 秘密鍵は、絶対に公開してはならない。 登録と認証情報の交換 生成した公開鍵を、APIサーバー側に登録する。 クライアントは、秘密鍵を使用してデジタル署名を作成し、APIリクエストに添付する。 APIサーバー側は、公開鍵を使用して、デジタル署名を検証できる。 署名の生成と検証 暗号系の標準ライブラリを使用して、デジタル署名を生成できる。 署名の検証も同様に、暗号系の標準ライブラリで検証可能。 リクエスト・フィンガープリント 署名は、リクエスト・フィンガープリントを使用して作成する。 リクエスト・フィンガープリントには、以下の情報を含める。 HTTPメソッド URL(ホストとパス) リクエストボディ データサイズが過度に大きくなることを考慮し、ハッシュ値を計算する 計算したハッシュ値を「Digest」と呼ばれる HTTPヘッダー(Base64形式)に格納する 存在するなら、リクエスト送信時間も含めると良い。 古すぎるリクエストを拒否できるようになる HTTPリクエストに署名するために使用するフィンガープリントの例 (request-target): get /sample/1 host: example.com date: Tue, 26 Oct 2022 12:00:00 GMT digest: SHA-256=xxxxxxxx 上記フィンガープリントをペイロードに、デジタル署名を作成する。 署名を持たせる APIサーバー側で署名を検証できるよう、リクエストに以下のメタデータを含める必要がある。 headers フィールド : フィンガープリントに含まれるヘッダーの順序付きリスト ヘッダーの順序によってフィンガープリントは変化するため、順序は大事 keyId フィールド : 公開鍵を特定できるID ユーザーIDを使用できる signature フィールド : デジタル署名本体 algorithm フィールド: 署名の生成に使用したアルゴリズム リクエストを検証する APIサーバー側は、送られてきたメタデータを使用して、以下を検証する。 ダイジェストヘッダーを確認し、リクエストが変更されていないことを検証する 公開鍵とフィンガープリントを使用して、署名が正しい事を検証する 事前に、リクエストのフィンガープリントを再計算する必要がある トレードオフ デジタル署名は非常に安全な選択肢だが、ユースケース次第では過剰な場合もある。 特に否認防止機能 否認防止が不要な場合、デジタル署名の代わりに、以下代替手段が検討できる。 秘密鍵 & HMAC 短命のアクセストークンを使用した認証メカニズム デジタル署名は計算量が多い。 この計算量を気にする場合、より軽量な代替手段を選ぶことになる。 扱うこと リクエスト認証システムの要件 デジタル署名の概要 認証情報の生成、登録、署名 HTTPリクエストのフィンガープリント 署名の詳細を伝える 署名の検証とHTTPリクエストの認証 まとめ リクエストの認証を成功させるには、以下3つの要件がある 発信元の証明 : リクエストが既知のソースから来たことの証明であり、不確実性はない 整合性 : リクエストが、既知の発信元から送信された時点から改変されていないことの証明 否認防止 : リクエスト送信元が、確実にリクエストを送信した事実を証明すること 言い換えると、リクエスト送信元が、リクエストを送信した事実を否認できないこと 認証は、IDが無記名債権のような秘密鍵の所有として定義されることを利用している リクエストに署名するとき、署名されるコンテンツは、以下を含むリクエストのフィンガープリントである必要がある 「HTTPメソッド」「パス」「ホスト」「HTTPボディ内の一連コンテンツ」

WebAPI

2023年12月10日(日)

1.0時間

API デザインパターン 23章 インポートとエクスポート 読了

やったこと API デザインパターン 23章 インポートとエクスポート 読了 学んだこと ポイント import と export カスタムメソッドの責務 APIと外部ストレージシステムを直接やり取りさせる利点 リソースとバイト列の変換をどう制御するか import と exportの一貫性 import 時、識別子が衝突する可能性をどうやって回避するか import で複数のリソースタイプを扱うにはどうするか import と export でフィルタリングをサポートするべきか 学び import と export カスタムメソッドは、データ転送にのみ責務を持つ 細かな変換、フィルタリングなど、ビジネスロジックレベルの処理に責務は負わない バックアップ & リストアを意図して設計されていない APIと外部ストレージシステムを直接やり取りすることで、以下の利点がある ETL処理を作成する必要がない データ転送量が減る リソースとバイト列の変換では、専用の設定インターフェースを用いて、処理を制御する import と exportは一貫性の責務を負わない 特に export では、最新のデータを転送できるとは限らない import 処理する時、 リソースを新規作成扱いで作成し、識別子も新しい値を割り当てる import 処理失敗時の再試行で重複する恐れがある これは、リクエスト重複回避(26章)にて回避できる import 処理では単一のリソースタイプだけサポートする 複数のリソースタイプを扱う複合リソースはサポートしない(それはデータ転送の範疇を超える、重要なビジネスロジックの領域) フィルタリング exportではサポートするべき import ではサポートするべきではない 利用者の責務で、インポート前に、 インポート対象のリソースをフィルタリングしてもらう 気づき 外部ストレージシステムとのやり取りをAPIサーバー側でサポートするかは、恐らくトレードオフ 利用者側に責務を転嫁して、APIサーバー側はバイト列だけ受け取る形でも良いと思う 利用者の利便性と、運用負荷のトレードオフ メモ import / export カスタムメソッドを用いて、リソースを柔軟かつ安全にAPI内外へ移動する手段を提供する。 更に、外部ストレージシステムと直接(≒仲介クライアントを介さない)通信する手段を提供する。 API と外部ストレージシステムが直接やり取りする利点 データ転送回数が少なくなる 「データ取得・整形・APIへ送信」というカスタムコードを記述する必要がなくなる ![[Drawing 2023-12-09 13.46.26.excalidraw]] 概要 APIにリソースを入出力するために、 import と export というカスタムメソッドを利用する。 API側で外部ストレージシステムとやり取りする以上、データのやりとりはAPI側で責任を持つ。 外部ストレージシステムやシリアライズフォーマットは無数に存在する。 import と export メソッドには、 将来登場するモノも含め、対応できる柔軟性が要求される。 この柔軟性を満たすために、包括的な設定インターフェースを設計する必要がある。 import には InputConfig を、 export には OutputConfig を設けるイメージ 設定インターフェースでは、以下を分離すると良い。 外部ストレージシステムにアクセスするための設定 API リソースのシリアライズ/デシリアライズ設定 インターフェースが決まると、処理の制御を考えていく。 実装 import / export メソッド 大きく、実装のポイントは以下の2つ import と export メソッドは、単一のリソースタイプのみ扱う 例) Message リソース専用の ImportMessage / ExportMessage を定義し、 これらカスタムメソッドは、 Messageリソースのみ扱う 何でも扱える ImportMisc / ExportMisc は定義しない 戻り値は LRO (10章参照) を返却し、同期的な処理は実装しない 扱うリソースによっては、 Import / Export 処理に時間がかかるため ストレージシステムとのやりとり ◆ポイント ベースとなるインターフェースを用意し、このインターフェースを継承する形で個別のストレージシステムごとにインターフェースを定義する アンチパターン : ベースとなるインターフェースに、全個別ストレージの設定プロパティを定義する形 利用したいストレージシステムについて、必須なプロパティが何かわかりにくくなる 似た名前が複数登場するなど Import と Export のインターフェースは必ず分離すること この二つは別の概念を表現しているため 扱うプロパティが似通うことになっても、必ず分離する リソースとバイト列の変換 import / export 用の設定インターフェースである InputConfig と OutputConfig を使用して、各種設定を制御する。 設定の例を示す。 ContentType : "json", "csv" など、シリアライズ/デシリアライズ のフォーマット compressionFormat : "zip" , "bz2" など、 import /export 処理後にリソースを圧縮したい場合、そのフォーマットを指定できるプロパティ filenameTemplate: ファイル名のテンプレート。複数のリソースをまとめてexport する場合に使用するものと思われる maxFileSizeMb : 最大ファイルサイズ 一貫性 エクスポートするデータのサイズや個数によっては、エクスポート処理に時間がかかる。 この時、エクスポート中にデータが更新される可能性も十分考えられる。 これには、以下の解決策がある。 基盤となるストレージシステムが提供するスナップショット機能、またはトランザクション機能を使用する ある時点でのリソースを一括で読み込むことで、一貫したデータを確保できる なお、エクスポート中に発生したデータの変更は、反映されない エクスポートされるデータが最新であることを保証しない(言い換えるとベストエフォート) ある時点でのデータを正確に表現できないことを意味する エクスポート処理中は、変更処理を一切受け付けない スナップショット機能、トランザクション機能未サポートかつ、最新であることを保証しなければならない場合の選択肢 ユーザーに不便を強いることになるので、あまりとりたくない選択肢 基本的には、選択肢2を取ることになる。 また、エクスポートとバックアップは異なる。 export カスタムメソッドを、バックアップ目的で使用しないよう、利用者に示すこと。 識別子と衝突 エクスポート処理にて、識別子ごとエクスポートすると、タイミング次第でインポート時に識別子の衝突が発生し得る。 「エクスポートは、外部からデータを新規に取得する」と考え、識別子ごとリソースを(必要ならバッチで)新規作成する。 この場合、重複したリソースが複数誕生する。 そもそも、 import と export カスタムメソッドは、一貫性が重要となるバックアップ & リストアを意図していない。 そのため、重複を許さない(または一貫性が重要な)リソースに対して、 import と export カスタムメソッドを提供しない(または保証しないことを明記する)こと。 関連リソースを扱う import と export カスタムメソッドは、APIサービスと外部ストレージシステム間のデータの中継処理を取り除くことを目的としている。 子リソースごとに処理を選択できるような、高度な機能の提供は想定していない。 そのため、 import と export カスタムメソッドは、「子リソースを持たず、関連リソースが無くても有用なリソース」に限定して提供する。 複合リソースタイプのインポートおよびエクスポートの話が出たら、要件を疑おう。 複合リソースをインポート/エクスポートできたとして、本当に価値はあるの? 失敗と再試行 ◆インポートの失敗 検証をはじめとした何らかのロジックによるエラーか、一過性のエラーかで対応が変化する。 前者(何らかのロジックによるエラー)の場合、何度再試行しても失敗するため、利用者にエラーを通知する必要がある。 後者の場合、再試行処理によって、インポートに失敗した分のデータが重複してしまう。 これに対処するため、 インポート処理をトランザクション処理にて実行する 途中で失敗したら、変更をロールバックする リクエスト重複回避(26章)を実装する インポートするデータ単位で識別子を生成 インポートに成功した識別子をキャッシュすることで、再試行時にスキップするか判断できるようになる ◆エクスポートの失敗 エクスポート処理は、基本的には一貫性を保証しない。 そのため、再試行したエクスポート処理は、再試行前のエクスポート処理と同じ結果になるとは限らない。 また、失敗したエクスポート処理に失敗したデータは、以下の理由により、どのエクスポートプロセスで失敗したかに関わらず、残すこと(言い換えると、勝手に削除しないこと)を推奨する。 API が、外部ストレージシステムのデータを削除する権限を持つとは限らないため エクスポートに失敗したデータに価値があるか、API側では判断がつかないため 100%でなくても、例えば98%でもエクスポートに成功できれば十分かもしれない フィルタリングとフィールドマスク エクスポートはフィルタリング機能を提供できた方がよい。 この時、フィルタリング文字列は、 エクスポート用設定ファイルではなく、 export カスタムメソッドのリクエストボディに含める。 エクスポート用設定ファイルは、リソースの束をバイト列の束に変えることだけに焦点を当てている 他のことに焦点を当てたくないため、フィルタリング文字列を含めないようにする インポートのフィルタリングは、API側でサポートするべきではない。 API側で、専用のフィルタリングルールを実装する必要がある API側で各種フィルタリングルールの実装に責任を持つ必要があり、労力が伴う インポート前に、利用者の責務で、リソースをフィルタリングしてもらう。 トレードオフ このパターンの目的は、「APIサービスと外部ストレージシステム間のギャップを埋める」こと。 もっと言うと、データ転送にだけ責務を持つ。 これにより、以下の欠点を持つ。 複数のリソースタイプを扱うよう設計されていない 言い換えると、データ転送にのみ責任を持つため、細かなデータ変形には責務を持たない 複数のリソースタイプを扱うような処理は、ただのデータ転送ではなく、重要なビジネスロジック 別途カスタムメソッドを定義しよう インポートとエクスポート機能を、バックアップ & リストア機能と混同されがち 扱う事 インポートおよびエクスポートの具体的な定義 リモートストレージシステムとの直接的なやり取り シリアライズされたバイト列のAPIリソースへの変換 変わりやすいデータセットをエクスポートする際の一貫性の処理 インポートおよびエクスポート時の一意な識別子の処理方法 関連リソースをインポート、エクスポートするときの対処法 インポート / エクスポート操作が失敗した時の処理 インポートとエクスポートのバックアップやリストアとの違い 入力時でのデータと出力データのフィルタリング まとめ カスタムの import , export メソッドによって、 APIサービスと外部ストレージシステム間で直接データを移動できるようになる これらのメソッドはバックアップ/リストア機能を意図していない バックアップ / リストアを意図して使用した場合、思わぬ結果につながる 本書籍でのAPI定義では、以下に焦点を当てている 外部ストレージシステムにバイト列を出し入れする バイト列をAPIのリソース表現に / から変換する import , export メソッドで扱うデータは、常に最新のデータとは限らない システムがポイントインタイムデータの読み込みをサポートしていない場合を除く データのインポートとエクスポートは、一度に単一のリソースタイプに制限されるべき 子リソースやその他参照リソースなど、複数のリソースタイプを扱いたい場合は、代わりにバックアップとリストア機能を使用すること

WebAPI

2023年12月09日(土)

2.0時間

API デザインパターン 21章 ページ分割 読了

やったこと API デザインパターン 21章 ページ分割 読了 学んだこと ポイント ページ分割で使用する、以下3つのフィールドの意味 pageToken maxPageSize nextPageToken maxPageSize で指定した数をキッチリ返すのではなく、ベストエフォートとする理由 ページトークンの情報は簡単に解析されてはならない。その理由は? オフセットとリミットを使用するべきではない理由 学び 「ページ分割パターン」によって、レスポンスを分割できる ページ分割パターンでは、以下のフィールドを活用する pageToken : 分割したページの位置を表す「トークン」情報 maxPageSize : レスポンスとして要求する、1ページ当たりのデータ数 nextPageToken : 次の「ページ」を要求するために必要な「トークン」 maxPageSize で指定した数だけ、ベストエフォートで検索して返す これにより、検索にタイムアウト時間を設定できるようになる ページトークンから実装の詳細が漏れると、APIの変更が難しくなる ページトークンは、簡単に解析できないような値にする オフセットとリミットは、「RDBを使用している」という実装の詳細 将来RDBをやめた後でも、オフセットとリミットをサポートし続けなければならない メモ 対象とする問題 APIで扱うリソースサイズや数が大きくなると、一度のAPIリクエストで扱うのが困難になる。 総計100Gや10億個のデータを扱うような場合 酷く時間がかかったり、そもそも扱えなかったりする。 こういった場合は、データを分割して提供することになるが、どういったインターフェースとなるか。 広く普及している「ページ分割」パターンについて述べる。 概要 データを「ページ」という単位に分割する。 利用者は、「ページ」単位でAPIリクエストを送信する。 API側は、対応する「ページ」と、利用者が次の「ページ」を要求するために必要な「トークン」と呼ばれるデータを返す。 実装 以下の3つのフィールドを使用する。 pageToken : 分割したページの位置を表す「トークン」情報 maxPageSize : レスポンスとして要求する、1ページ当たりのデータ数 nextPageToken : 次の「ページ」を要求するために必要な「トークン」 nextPageToken に何らかの値が存在する場合、次のページが存在することを示す。 利用者は、レスポンスで受け取った nextPageToken の値を、リクエストの pageToken フィールドに設定してリクエストを送信することで、次の「ページ」情報を取得できる。 ◆初回リクエスト { maxPageSize: xxx } ◆レスポンス { result: [...], nextPageToken : "hogefuga" } ◆次ページを取得するリクエスト { maxPageSize: xxx, pageToken : "hogefuga" } 【ポイント1】 ページサイズ ◆maxPageSize は ベストエフォート maxPageSize で設定した値は、ベストエフォート(≒努力目標)。 10 を指定したからと言って、必ず10件返却するわけではない。 努力目標とすることで、タイムアウト時間を設定できるようになる。 これにより、「検索が終わるまで長時間待つ」といった問題を解消できる。 そして、追加でデータが欲しければ、pageTokenを使って追加でデータを取得できる maxPageSize を 100 , タイムアウト を 0.2 秒 に設定。 最大0.2秒間の間でデータを検索 - 0.2秒 以内 100件以上見つかれば、100件返す - 0.2秒 経っても、82件しか見つからなければ、 見つけた82件だけ返す - 本当は100件以上あるが、検索に時間がかかりすぎて、0.2 秒という制限時間内では82件しか見つからなかった - データが82件しかなかった ◆デフォルト値 maxPageSize には、利用者が驚かない程度に適切なデフォルト値を設定する。 デフォルト値の存在はドキュメントに明記する。 可能な限り他のAPIのデフォルト値でも、同じデフォルト値を設定する(APIの一貫性を担保)。 【ポイント2】ページトークン カーソルのような動作をする。 ◆終了基準 サーバー側は、全データの検索終了を、pageTokenに空の文字列を設定することで表現する。 そして、レスポンスの結果が空かで判断してはならない。 最初の0.2秒では0件、次の0.2秒で1件以上ヒットする ... という可能性は十分考えられる 以下のレスポンスを考える。 { result: [], nextPageToken: "xxxxxx" } これは、「0.2秒検索した結果、何も見つかりませんでした。このトークンを使う事で、続きから検索できます。」という意味になる。 ◆不透明性 ページトークンは、サーバー側でリスト順次処理する際に使用する。 そして、ページトークンの実装は利用者から隠匿すること。 -「 { offset : 10}のBase64値」のような、簡単に解読されるような値ではいけない 簡単に解読されることは、利用者に実装の詳細を漏らすことを意味する。 これにより、利用者が、自身のアプリケーションに実装の詳細を盛り込む可能性が生まれ、API定義を変更しにくくなってしまう。 ◆書式 WebAPIで使用する以上、トークンは文字列で扱うことになる。 ◆一貫性 途中でデータが変化した場合でも、最新のページを返却できる方法が必要。 この問題に対する簡潔な答えは存在しない。 ポイントインタイムスナップショットをサポートするDBであれば、この情報をエンコードしてページトークンとして用いることで、強い一貫性を提供できる 他には、以下の手段が考えられる。 - ドキュメントにて、ページ情報が最新ではない可能性を説明しておく - 最後の検索結果をカーソルとして使用し、次のページトークンで中断した場所から拾い上げるよう実装する ◆有効期限 一般的に、ページトークンの有効期限は設定しない 期限切れトークンはリトライしてもらうしかなく、単に利用者に不便を強いるだけ 有効期限を明確にすると、「利用者は、ページング操作をいつまでに行えばよいか予想できる」という利点が存在する。 この場合、一般的なユースケースで十分な、60分程度を有効期限とするのが良い。 【ポイント3】総数 [xxx件中10~20件]といったUI向けに、全データの総数を返すか。 データがそこまで多くなく、総数の計算に時間がかからない => 正確な総数を返す データが非常に多く、総数の計算に時間がかかる => 最良の推定値を総数として返す どちらの場合も、総数は、レスポンスのフィールドに設定する。 { result: [...], pageToken: "xxx", totalResults: n // 総数 } 【ポイント4】リソース内でのページ分割 一つのリソースが巨大化した場合、一つのリソースを分割し、複数回のリクエストに分けて取得する方法が考えられる。 この場合、本項で取り上げているページ分割をそのまま応用できる。 この時、リソースの読み込み処理は強い一貫性を持つこと。 言い換えると、読み込み中にリソースの変更があった場合、読み込みを中断する事。 トレードオフ ◆双方向ページング ページ分割では、双方向ページングが提供できない。 nextPageTokenを使う事で「次のリソース」を要求することはできるは、前のリソースは要求できない 一部のユースケースを除き、基本的に双方向ページングが必要になる可能性は非常に低い。 双方向ページングを実装する場合、ページ分割の結果をキャッシュすることで対応できる。 任意のウィンドウ 任意の位置のページをリクエストすることはできない。 この機能も、一部のユースケースを除き、基本的には必要になる可能性が非常に低い。 避けるべきパターン : オフセットとリミット RDBの OFFSET と LIMIT に合わせる形で、APIで OFFSET と LIMIT フィールドを扱う。 APIの OFFSET と LIMIT フィールドの値を、そのままSQLに渡すだけ。 OFFSET と LIMIT はRDBの機能。 OFFSET と LIMIT を提供すると、RDBから別のデータストアに移行した後も、これらの機能のサポートを続けなくてはならなくなる。 言い換えると、OFFSET と LIMIT フィールドは実装の詳細であり、利用者に漏らすべきではない。 扱うこと ページ分割により、大きなデータセットを少しずつ利用する方法 ページ分割により、APIの利用者はどのようにして大きなデータセットを少しずつ利用できるようになるか データを構成するページの具体的なサイズ APIサービスは、ページングの完了をどのようにして伝えるべきか ページトークンの形式はどのように定義するか ひとつのリソース内の大きなデータチャンクをページングする方法 まとめ ページ分割は、3つの特別なフィールドを使用することで、APIレスポンスを分割し、一口サイズのチャンクで送信できるようになる 3つの特別なフィールド : maxPageSize , pageToken , nextPageToken ページがまだ存在する場合は、 nextPageTokenが何らかの値を持つ この値の使用方法 : 後続リクエストの pageToken フィールドに設定する 以下の理由により、正確なページサイズではなく、最大ページサイズを用いる 正確なデータを取得するために必要な時間がわからない 完全なデータが手に入る前でも、レスポンスを返す必要がある ページングが終了するタイミングは、レスポンスの nextPageTokenに値が存在しない場合 結果フィールドが空の時ではない

WebAPI

2023年11月19日(日)

1.5時間

API デザインパターン 22章 フィルタリング 読了

やったこと API デザインパターン 22章 フィルタリング 読了 学んだこと ポイント 二種類あるフィルターの指定方法は何か また、どちらで実装する方が良いか フィルターの実装でやってはいけないこと3つ フィルター関数の引数についてやってはいけないことは? 配列フィールドをもつリソースに対してやってはいけないことは? フィルタリング中に発生したエラーの対処でやってはいけないことは? より複雑なフィルター機能を提供したい場合、最善の手段は何か 学び フィルターの指定方法には二種類ある 構造化されていない文字列 構造化されたフィルター フィルタリング関数は、外部リソースの影響を受けないようにする そのためにも、フィルタリング関数の引数は "フィルタリング用文字列" と "フィルタリング対象のリソース"の二つに絞る 配列の要素を指定できるような機能を提供しないこと 配列の順序が変わるだけでフィルタリング結果が変わってしまう 一貫性が保てず、利用者側も結果を予測できなくなる フィルタリング式のエラーは、利用者に知らせる 言い換えると、無視したり、隠したりしない 勝手な推測で意図しないデータを返却する方が、利用者にとってマイナス 基本機能では達成が困難な、複雑なフィルターが必要な場合、目的に特化したヘルパー関数を提供する 安易にワイルドカードを開放してはならない ## メモ 対象とする問題 ある特定の条件を満たすリソース群を抽出したい。 しかし、フロントエンド側で "全件取得 & フィルタリング" を行うのでは非効率。 どうするか? 概要 「特定の条件を満たすリソース群を返却する」APIを実装する。 { filter: any; maxPageSize: number; pageToken: string; } これは、考えることが多い。 フィルター条件の書式はどうする? どういったフィルターをサポートする? 対象のフィールド、完全一致、部分一致など 全てのフィルタリングが、実装に値するわけではない。 まずは、以下を明確にする。 重要な懸念事項(フィルターの指定方法など) サポートする価値のある機能は何か 実装 フィルタリングを提供したいリソース全部に対して、共通のフィルタリングインターフェースを決めること(一貫性の担保)。 構造 主に、以下のフィルタリング技術がある。 構造化されていない文字列 構造化されたフィルター どちらも、フィルタリングという目的は達成可能。 しかし、構造化されていない文字列によるフィルターの方が利点が多い。 文字列は自由度が高い フィルターの評価はAPIサーバー側で完結 新フィルター導入の際も、文字列中に新フィルター構文を記述するだけですぐ利用可能 言い換えると、構造化されたフィルターは、新フィルターを扱えるSDKへのアップデートが必要 SQLに近く、RDB経験者は理解しやすい 言い換えると、構造化されたフィルターは、フィルターを使いこなすための独自機構を学習する必要がある 複雑なクエリを処理する場合、構造化されたフィルターと比較して理解しやすい実装になる フィルター構文と動作 ◆実行時間 実行時間短縮の観点から、フィルター評価関数が扱うのは以下の二つのみ。 フィルター文字列 フィルタリング対象のリソース(一つだけ) 比較変数を増やすことで、フィルタリングの柔軟性は向上するが、共通のフィルタリングインターフェースを提供できなくなる(≒一貫性を担保できなくなる)。 このため、フィルター関数は、フィルター文字列とフィルタリング対象のリソースのみ扱う。 また、フィルター関数は、外部システムと通信する能力を一切持たせるべきではない。 言い換えると、外部リソースの影響を受けないようにする。 これにより、フィルターの責務を大幅に減らし、機能を単純化できる。 その結果、「実行時間が極端に長くなる」や「計算コストが高くなる」といった問題に直面する機会が減る。 ◆配列のインデックスによるアドレス指定 配列フィールドは、「順序が決まっていない」または「順序が不定である」という前提でフィルターを実装する。 言い換えると、配列フィールドの特定のインデックスを参照できるようなフィルターは提供するべきではない 別の問題(例えば、配列の要素が一つ以上存在するかなど)を解決したいことが多い 配列フィールドのインデックス参照は、配列の順序が変わるだけで結果も変化する。 結果として一貫性の担保が難しくなり、API利用者が結果を予測しにくくなることにつながる。 これは、API利用体験の低下につながるため、避けるべき。 ◆厳密性 フィルターに柔軟性を持たせるか。 結論として、意図の推測は避け、意図が明確な場合にのみ柔軟性を持たせる。 柔軟性を持たせると、利用者が意図しないフィルター式を入力し、意図しない結果が返ってくる可能性が上がる 柔軟性を下げると、意図しない結果をエラーとしてすぐに知ることが出来るが、エラーばかり発生し、利用者をイライラさせる可能性が上がる 無効なフィールド参照した場合はエラーを投げること。 エラーを投げず、勝手な推測で意図しないデータを返却する方が、ずっと利用者をイラつかせる。 ◆カスタム関数 共通のフィルタリング機能として提供したくはないが、利用者が希望する複雑な処理を提供したい場合はどうするか。 集計やパターンマッチなど 複雑な処理に特化した、ヘルパー関数を提供する。 endsWith(...) = "xxx" , substring(...) = "xxx" など 通常の文字列フィルタリングと同様、文字列中にヘルパー関数を入力して使用する。 トレードオフ 多くのユースケースにおいて、フィルタリングはサポートした方が良い。 クライアントでフィルタリング処理を実装する必要がない データ転送量を削減できる APIサーバー側の計算量を削減できる フィルターの実装に、 "構造化フィルター" と "構造化されていないフィルター文字列" のどちらを選択するか。 扱うこと なぜ標準の list メソッドがリソースのフィルタリングをサポートするべきなのか? 標準のlistリクエストの一部としてフィルターを伝える方法 リソースのフィルタリングを行う際の動作のガイドライン フィルタリングの条件においてサポートすべき機能、サポートすべきでない機能 まとめ 標準のlistメソッドでフィルタリングを提供することで、利用者は、興味のあるデータだけ取り出すことが出来る フィルタリングの仕様は、一般に、構造化されたインターフェースではなく、特定の構文(SQLのようなもの)に従った文字列で同じ意図を伝えるべき フィルター文字列は、評価用のコンテキストとして単一のリソースだけを受け取るようにすべき 実行時間が無制限に増加するのを防ぐため フィルターは、繰り返されるフィールド(たとえば、配列)の位置やインデックスに基づいてリソース上の項目を比較する方法を提供すべきではない フィルターの評価中に発見されたエラーは、すぐにわかるようにすべき 隠したり無視したりすべきではない 基本的な比較機能では利用者にとって十分ではない場合は、フィルタリング中に解釈・実行可能な関数群とそのドキュメントを提供する必要がある

WebAPI

2023年11月19日(日)

1.5時間

API デザインパターン 20章 匿名書き込み 読了

やったこと API デザインパターン 20章 匿名書き込み 読了 学んだこと ポイント ポイント 標準のcreateメソッドと比較した、writeメソッドの特徴 writeメソッドの実装が効果的なユースケース writeメソッドで書きこむデータに対する、一貫性の考え方 学び writeカスタムメソッドは、一意の識別子を持たない(≒持たせることに意味のない)データを書き込むためのメソッド writeメソッドは、集計値にこそ価値があるデータをAPIで書き込みたい場合に有効 個々のデータを識別したい場合は、標準の create メソッドを通して、リソースとして扱う writeメソッドで一貫性を保つことに、あまり意味はない メモ データを特定できないような、「書き込み」に特化した処理は、そのままではリソース指向には向かない。 ログデータなど、非常に小さな単位での書き込み処理が該当 そのため、「書き込み処理」という独自の概念を組み入れて、非リソース指向のデータを、リソース指向に適合させる。 ここでいう「匿名データ」とは、識別子を持たないデータを指す。 概要 以下の特徴を持つ、 write カスタムメソッドを実装する。 匿名のまま(≒一意な識別子を持たせず)書き込み可能 書き込んだデータ群に対して、集計値を要求できる そして、個々のデータを取り出すことはできない writeカスタムメソッドにて書き込むデータを、 リソースと区別して エントリー と呼称する。 実装 writeカスタムメソッドでは、以下のように振る舞う。 書き込みに成功した場合は、値を何も返さない(またはvoidを返却する) 書き込みに失敗した場合は、エラーを返す URLは、以下を推奨(議論の余地あり)。 /chatRooms/1/statEntries:write 一貫性 読み込みについて、一貫性を維持することはあまり重要ではない。 get 処理では 統計データを返すため、どのデータを追加したか知る意味が薄い 他の誰かが writeメソッドを呼んでいる可能性 そのため、writeメソッドは、バックエンドの処理を待たずに、 200 OKを返してよい。 LRO(Long Run Operation)を返すのもNG 結果に時間がかかる場合は、 202 Accepted を返すだけで十分 トレードオフ write メソッドは、分析用データやログデータなど、以下のような状況でのみ役立つ。 識別子を持たせることにあまり意味がない 個々のデータではなく、分析結果に価値がある また、そもそも分析データの取り込みを、APIでサポートするべきかも、議論のポイント。 扱うこと 匿名データとは何か リソースの作成に頼らず、APIに伝田を取り込む方法 取り込んだデータの一貫性に関する問題にどう対処するか まとめ データをシステムに取り込む必要がある場合、専用の writeメソッドを使用する 言い換えると、アドレス指定されない、個別のリソースを作成したりしない writeメソッドでAPIに書き込んだデータは、後でAPIから削除できない writeメソッド(およびそのバッチ版)は、結果のステータスコード以外のレスポンスは返さない 特殊な状況を除いて、LROリソースであってもリソースを返してはならない writeメソッドは、リソースではなく、エントリーを扱う エントリーは、アドレス指定できない、一時的なデータのこと

WebAPI

2023年08月11日(金)

1.0時間

API デザインパターン 18章 バッチ操作 読了

やったこと API デザインパターン 18章 バッチ操作 読了 学んだこと ポイント バッチ処理実装に関する、二つの戦略 バッチ操作にアトミック処理を採用するべき理由 バッチ操作の処理結果は、入力リソース順と同じであるべき理由 学び バッチ処理実装の戦略には、以下の二つの戦略がある 「リクエストのリストを処理するメソッド」として実装 つまり、既存の標準メソッドを繰り返し実行するような実装方法 リクエスト群から、処理に必要な最小限の情報(IDなど)だけ抜き出して、標準メソッドに頼らない独自処理を実装する方法 バッチ操作は、アトミックである方が利点が多い つまり、全操作に成功する/失敗する、のいずれかのみ 同じ型で、複数のリソースを操作するバッチメソッドは、コレクションをターゲットとする 〇 : POST /chatRooms/1234/messages:batchUpdate × : POST /chatRooms/1234:batchUpdateMessage バッチ操作の処理結果は、入力されたリソース(またはリクエスト送信)順と同じであるべき 順序が分かっていれば、探索処理が不要になり、処理速度向上が見込める ## メモ 複数のリソースを、一括して操作するための方法(いわゆるバッチ処理)を学ぶ。 トランザクション処理も、スコープに入る。 バッチ処理を行うカスタムメソッドを提供することで、バッチを処理を実現する。 バッチ処理は、常にアトミック性を持たせる。 実装 推奨すべきカスタムメソッド名を示す。 BatchGet<Resources>() BatchCreate<Resources>() BatchUpdate<Resources>() BatchDelete<Resources>() アトミック性 例えGet処理であっても、「完全に成功する」 or 「完全に失敗する」のどちらかに収束させる。 言い換えると、「一部成功(または失敗)」という状態は、あってはならない コレクションに対する操作 URLの例 /chatRooms/*:batchUpdateMessages /chatRooms/*/messages:BatchUpdate <= 推奨 結果の順序付け 新しいリソースを一括作成し、結果を受け取る状況を考える。 順序が分かっていると、配列のインデックスアクセスが使用できる。 わからない場合、バッチ処理に渡したリソースと、バッチ処理結果を照合する必要がある。 後の処理を考えると、以下のことが言える。 バッチ処理に渡すリソースは、順序を決めておく バッチ処理側は、作成したリソース群を上記順序で構成して返却する 共通フィールド バッチ処理の実装には、以下の二パターンが考えられる。 コレクションを扱うように、「リクエストのリストを処理するメソッド」として実装する リクエスト群から、処理に必要な最小限の情報(ID情報など)だけ抜き出して、既存のメソッドを実行する バッチ型 GET IDリストを受け取り、対応するリソースリストを返却する。 返却の際、IDリストと同じ順番で、リソースリストを構成する HTTP GETメソッドを使用するため、IDリストはクエリパラメータでサーバサイドに渡す。 GET処理にて、厳密なアトミック性が不要であれば、 list 標準メソッドの使用を検討すること。 バッチ型 Create と Update 標準メソッド用リクエストのリストを、入力として受け取る。 これにより、リクエストのオプション(フィルターマスクなど)を扱うことが出来る。 バッチ型 updateは、 他のバッチ処理と同じように HTTP POST を使用する。 トレードオフ アトミック性を優先した結果、一部のユースケースで問題を引き起こす可能性が生じる代わりに、一貫した動作(全部成功 / 全部失敗のみ)を提供できる。 バッチ処理メソッドの入力形式に、様々な種類をサポートした結果、一貫性が失われる代わりに、メソッド毎に最適な処理を実現できる。 バッチ処理は、標準的なリクエストを繰り返すだけでも実現可能 しかし、一部のバッチ処理(特に、get と delete )は、不要な処理を含んでしまう これを回避するため、IDだけ抜き出して処理をする ... という I/Fを持ち込んだ 扱うこと バッチ操作と、複数の標準メソッドの実行との違い バッチ操作が、アトミックでなければならない理由 バッチリクエストメソッドが共通のフィールドを持ち、繰り返しを避ることが出来る方法 バッチ型標準メソッド まとめ バッチメソッドあれこれ バッチメソッド名は、Batch<メソッド><リソース>とする バッチメソッドはアトミック性を持つ バッチ内のすべての操作を実行する/全く実行しない のいずれかである必要がある 言い換えると、一部だけ実行 ... などは厳禁 同じ型の、複数のリソースを操作するバッチメソッドは、一般的に親リソースではなく、コレクションをターゲットにするべき 〇 : POST /chatRooms/1234/messages:batchUpdate × : POST /chatRooms/1234:batchUpdateMessage バッチ操作の結果は、リソース(またはリクエスト送信)順と同じ順序であるべき 個々のリクエストで定義されるリソースの複数の親を示すために、ワイルドカードに - を使用する

WebAPI

2023年08月11日(金)

2.0時間

API デザインパターン 19章 条件に基づく削除 読了

やったこと API デザインパターン 19章 条件に基づく削除 読了 学んだこと ポイント purgeメソッドの特徴 purgeメソッドの危険性 purgeメソッドを実装する際のガイドライン 学び purgeメソッドの特徴 標準の list + バッチの delete purgeメソッドの危険性 ちょっとしたフィルターの設定ミスで、意図しないデータを削除してしまいかねない purgeメソッドを実装する際のガイドライン purgeメソッドは、必要な場合を除き、基本は実装しないこと purgeメソッドの危険性を緩和するため forceフラグを実装する デフォルトでは、削除処理の検証結果を返し、削除処理は実行しない forceフラグが立っている場合のみ、削除処理を実施する purge検証処理の戻り値は、以下を含めると良い 削除処理を実行した場合の、削除対象数 削除されるリソースのサンプルセット purge メソッドの一貫性を保証する方法はない 気づき メモ バッチ処理による一括削除ではなく、「特定の条件にマッチしたリソース群をまとめて削除する」というユースケースに対処する。 「特定の条件にマッチしたリソース群をまとめて削除する」というユースケースは、以下の問題を抱える。 削除する対象のIDが不明な場合は、検索処理が必要 検索処理と削除処理のアトミック性をどう維持するか 概要 以下の特徴を持つ、purgeというカスタムメソッドを導入する。 「特定の条件にマッチする」かを検索できる、フィルターを入力できる => list と バッチ deleteを組み合わせたイメージ purgeメソッドを呼び出すだけで、目的を達成できる purgeメソッドは、フィルターの入力ミスなどで、利用者が意図しないデータまで削除してしまう危険性がある。 そのため、以下の防御策を講じる。 forceフラグを導入する このフラグが立っている場合のみ、物理削除を行う このフラグが立っていない場合、削除処理のプレビュー結果を返却し、削除は実施しない プレビュー結果には、「削除される項目数」と「フィルターにマッチした項目のプレビュー」を含める 実装 filter フィールド 削除対象を検索(フィルタリング)するためのフィールド。 標準 listメソッドの filterと全く同じように動作する。 「filter未設定時、全リソースが返却される」ような実装は要注意。 filterの設定忘れなどで、全リソースが削除されかねない。 これを防御してくれるのが forceフラグ 防御策として、デフォルトでは削除しない purgeメソッドは、ちょっとしたミスで、意図しない大量のデータを削除してしまう危険性を持つ。 そのため、 デフォルトの動作は利用者にとって安全であるべき。 安全な動作として、「削除を実行した場合の検証結果」を返却する動作を、デフォルトにする。 本当にデータを削除したい場合は、 forceフラグを立てて purgeメソッドを実行する。 purge メソッド(検証)の戻り値 purgeメソッドの検証処理では、以下の二つを返す。 削除を実行した場合の、削除される項目数 削除を実行した場合の、結果のサンプルセット サンプルセット : 「削除対象となるリソースのID」のリスト フィルターで得られるデータセットが多い場合は、100項目程度を返却する フィルターで得られるデータセットが小さい場合は、全部返却する 一貫性 検証処理で帰ってきたデータが、その後の削除処理で削除されるデータと一致することを保証したい。 検証処理と削除処理を実行した時差により、意図しないリソースが削除(または削除失敗)する 結論として、保障することはできない。 そのため、頻繫に変更されるリソースに対して、 purgeの検証と削除はあまり役に立たない。 扱うこと カスタムのpurgeメソッドを使用して、条件にマッチするリソースを削除する方法 purge メソッドが危険である理由 意図した以上のデータを誤って削除しないための様々な安全対策 条件にマッチした結果の一貫性に関する問題にどのように対処するか まとめ purgeは、絶対に必要な場合のみ実装する 言い換えると、「あってもなくてもかまわない」ような場合は実装しない 必要性が判明してから実装する デフォルトでは、 purge リクエストは実際にリソースを削除するのではなく、検証にのみ使用されるべき すべての purgeレスポンスは、影響を受けるリソースの個数を含むべき purgeメソッドは、標準の listメソッドと同じ一貫性のガイドラインを守る必要がある

WebAPI

2023年08月11日(金)

1.5時間

APIデザインパターン 17章 コピーと移動 読了

やったこと APIデザインパターン 17章 コピーと移動 読了 学んだこと ポイント コピー・移動処理が複雑になる要因 コピー・移動処理実装時における、考えるべき点 学び コピー・移動処理は、動作要件や制限事項が多く、正しく実装することが困難 場合によっては、実装を避けることも選択肢に入る 独立したリソースのコピー・移動処理はそこまで難しくない 子リソースを持つ場合や、関連リソースをサポートしている場合、リソースのコピー・移動は、考えるべき問題が多くなる 特に移動(再配置)は、複雑な問題を抱えるため、可能な限りサポートするべきではない これといった、確実な解決策は存在しない 要件に応じて、コピーや移動ロジックは変化する可能性が高い 移動処理の実装は、慎重に検討すること 特に、親をまたいだリソースの移動は、リソースレイアウトに問題がある可能性が高い => リソースレイアウトから見直すこと 気づき 移動による参照更新について、リダイレクトテーブルを持つのはどうだろう? 「移動前のリソース => 移動後のリソース」 的なエンティティを持つイメージ どうしても参照を切りたくない場合は、有効かも メモ API 利用者が、リソースのコピーと移動を望む可能性は非常に高い。 APIにおける、リソースのコピーと移動を安全かつ確実に実行する方法を見ていく。 概要 リソースのコピーと移動は、カスタムメソッドで実装する。 コピーと移動の実装に際して、以下の点がポイントとなる。 IDは利用者が指定する?サービス側で採番する? 古い識別子はどうする? 親をまたぐ場合とそうでない場合に違いはある? 親をまたぐ : 別の親に属しているだけで、以前と同じ識別子が必要な場合 そうでない : 識別子を変更できれば良い 子リソースを持つ親リソースをコピーする場合、子リソースはどう扱う? コピー(または移動)中、該当リソースを、他のユーザが操作できないようにするには? アクセスポリシーを始めとしたメタデータをどう扱う? 実装 識別子 一般的には、サービス側で識別子を発行する方が良い。 識別子の生成をコントロールできるため コピー処理の場合、createメソッドと同じように動作させる。 サービス側で識別子を発行している場合は、利用者側で識別子を指定できないようにする 利用者側で識別子を指定できる場合は、copy メソッドでも指定できるようにする 指定した識別子が使用済みの場合は、 409 Conflict エラーを発生させ、処理を中断する 移動処理の場合は、目的は大きく「親リソースの変更」と「リネーム」に大別できる。 親子関係をもつリソースであれば、 「親リソースの変更」を目的とした移動処理は実装する価値あり 利用者指定識別子をサポートするのであれば、「リネーム」を目的とした移動処理は実装する価値あり 親子関係を持ち、利用者指定識別子をサポートするのであれば、両方を目的とした移動処理を実装できる 反対に、どちらも満たさないのであれば、そもそも移動処理を実装するべきではない 子リソースの扱い コピー・移動処理のどちらでも、子リソースもまとめて扱う。 「コピーされたリソースは、コピー元と同じように振る舞うこと」という考え方が大事。 子リソースに手を加えない ... ということは基本ない 子リソースがあることを前提とした親リソースなどが機能しなくなるため ただし、移動処理の場合、移動前のリソース識別子がリンク切れとなる。 関連リソース ◇ここでいう関連リソース 「コピー(または移動)したいリソース」を参照しているリソースのこと リソースをコピー(または移動)する場合、関連リソースをどう扱うかが問題となる。 こうだ!という具体的な解決策はないため、問題点を明示する。 ◇参照の整合性問題 リソースをコピー(または移動)する場合、コピー(または移動したい)リソースを参照するすべての関連リソースについて、関連リソースが持つ参照情報を更新するか決定する必要がある。 更新するためには、変更を追跡する必要があるが、これが非常に困難なのは想像に難くない。 ◇関連リソースを複製するべきか問題 コピー(または移動)したいリソースに応じて、関連リソースの挙動も変化する。 関連リソースが持つ参照情報を更新する場合もあれば、関連リソースもコピーする必要がある状況も考えられる つまり、リソースのコピー(または移動)に伴う関連リソースの更新処理は一定ではなく、アプリケーションの仕様によってほぼ確実に変化する。 Message リソースを含むChatRoomリソースをコピーする場合 ... - Messageリソースの参照を持つ関連リソースは、コピー後のMessageリソースの参照を保持する - ChatRoom リソースに所属するUser リソースは、複製してはならない ◇外部参照 具体的には、あるAPIのリソースを、インターネット越しの外部アプリケーションが参照している状況を考える。 以下の理由により、深く考えない。 外部アプリケーションの参照までサポートすることは不可能 Webは基本的にベストエフォート 問題があれば何が起こるか想像がつく 問題があれば 404 が返ってくることを知っている 外部データ ◇ここで言う外部データ DB管轄外かつアプリケーションが管轄するデータ(主にストレージ上のファイル) リソースのコピー(移動)に合わせて、外部データもコピー(移動)するべきか。 アプリケーションの要件に従うことは大前提として、基本的にはプログラムでいう「参照コピーか値コピー」と同じ問題。 継承されるメタデータ リソースのコピーによって、新たな(制約を含む)メタデータを作成することがある。 この時、コピー後のリソース次第では、コピーしたリソース(特に子リソース)が、新しい制約に違反する可能性がある。 例) - コピー前 (Chatroom) のMessageリソースは text が140文字まで - コピー後 (NewChatroom) のMessageリソースは text が 100文字まで => Chatroom の Messageのうち、100文字を超えるMessageが、NewChatroomでは制約違反に この場合、制約に違反しているとして、コピー処理を拒否すると良い。 - 利用者側で、「新しい方の制約を見直す」や「データを修正する」などの対応を選択できるようにする 他にも、以下のような対応策が考えられるが、あまりよろしくない。 Messageリソースが、制約を破れるようにする Messageリソースに対する update がうまく機能しなくなる可能性がある 制約に合うよう、Message リソースを修正する 利用者が不利益を被る可能性がある アトミック性 コピー処理でアトミック性を担保する場合を考える。 システムが提供するスナップショット機能を使用する選択肢がある。 使用しているシステムデザインスナップショット機能が提供されていない場合、以下の選択肢がある。 アトミック性を保証しない 書き込みロックを実施する copy 処理を対象としたDoS攻撃が成立する、というデメリットがあるため非推奨 移動処理でアトミック性を担保する場合を考える。 基本的には、コピー処理と同じ方法を採択する。 コピー処理と違い、 アトミック性を保証しない という選択は、データの更新に失敗する危険性をはらむため、極力選択しないこと。 トレードオフ 「コピーを実装したい」という要件は十分考えられる。 リソースの移動は、慎重に検討する。 親をまたいだリソースの移動は、リソースレイアウトが問題の可能性有り まずはレイアウトの見直しから 不適切な識別子や、参照関係とすべきところを親子関係で表現している ... といったことが原因の可能性が高い 扱うこと API内のリソースの場所を変更する、「コピー」と「移動」カスタムメソッドの使用方法 コピーと移動の操作について、適切な識別子(または識別子ポリシー)を決める 親リソースをコピー・移動する場合の、子リソースの扱い コピー・移動されたリソースに対する、外部からのデータ参照を処理する コピー・移動メソッドにはどのようなアトミック性が必要か まとめ API 利用者が、リソースの複製・再配置を望む可能性は非常に高い リソースの複製・再配置は、標準メソッドではなく、カスタムメソッド( move や copy)を使用する 親リソースに対する複製・再配置は、子リソースにも同じように適用されるべき ただし、これはケースバイケース どのようなケースでも、複製・再配置したリソースへの参照は、最新の状態に保つべき リソースが外部データを扱う場合、APIメソッドは、以下を明確化する必要がある 複製されたリソースが参照コピーか、値コピーか 複製・再配置カスタムメソッドは、(基礎となるストレージシステムの制約を考慮しつつ)できるだけアトミック性を保つこと

WebAPI

2023年07月30日(日)

2.0時間

API デザインパターン 16章 ポリモーフィズム 読了

やったこと API デザインパターン 16章 ポリモーフィズム 読了 学んだこと ポイント ポリモーフィックリソースとポリモーフィックメソッド ポリモーフィックメソッドを使用するべきではない理由 どのような時にポリモーフィックリソースを使用するべきか ポリモーフィックリソースの構造 学び ポリモーフィックリソース ポリモーフィックメソッド こちらは使用するべきではない ポリモーフィックリソースを使用するべきタイミング 汎用的な型を扱うことが合理的な場合 オブジェクト指向で I/Fを扱う時と同じ動機 ポリモーフィックリソースは、 typeフィールドで、具体的な型情報を管理する データ部分のチェックも大事 ## 気づき ポリモーフィックリソースを使用するべきかの判断は、オブジェクト指向にて「I/F型と具象クラスの型、どちらを宣言するか」と同じ基準 設計者の意図を反映できる 読み手は、APIの型から、設計者の意図をくみ取ることが出来る メモ オブジェクト指向の文脈で登場する「ポリモーフィズム」を、リソース指向APIの世界に持ち込む。 そのための方法やガイドラインを見ていく。 概要 ポリモーフィックリソース ... 汎用的な型でありつつ、具体的な型情報をもつリソースのこと。 これにより、汎用的な型用の処理を使いまわすことが出来る。 言い換えると、具体的な型一つ一つに、専用のメソッドを作成する必要がなくなる 実装 ポリモーフィックリソースを使用するべきタイミング 汎用的な型(Messageなど)を扱いたい場合は、ポリモーフィックリソースを使用する。 この場合、全メッセージの一覧を取得するAPIなど 具体的な型(Messageに対して、 VideoMessageなど)を扱いたい場合、具体的な型専用のリソースを扱うことになる。 考え方は、オブジェクト指向で「I/F型と具象クラスの型、どちらを宣言するか」と同じ感じ。 構造 ポリモーフィックリソースは、具体的な型情報を保持する type フィールドを持つ。 ポリモーフィックリソースを扱う側は、 type フィールドを参照することで、リソースの具体的な型情報を知ることができる。 具体的な型の個別フィールド 具体的な型の個別フィールドは、content などの共通フィールドに集約させる。 リソースを扱う側は、 typeフィールドの値を確認することで、 共通フィールドが保持する値を特定できる。 ポリモーフィックリソースによる抽象化は、データ構造が実用的な意味を失わない範囲にとどめる。 Messageの例だと、 Messageオブジェクトの責務(メッセージ情報の取り扱い)を逸脱しない範囲で抽象化すること データのチェック type の値に応じて、共通フィールドの値をチェックし、必要ならエラーを返す type がMessage の content : string は、ただの文字列である可能性 typeがPhotoMessageのcontent : stringにて、contentにURIが期待されている場合、URIの構造に則った文字列であることをチェックする 余分なデータが渡されてきた場合、余分なデータは破棄し、処理を続行する ポリモーフィックな振る舞い ポリモーフィックメソッド ... リソースの typeに応じて、振る舞いを変えるメソッド。 結論として、ポリモーフィックメソッドを使用するべきではない。 メソッドは、似たような名前でも、扱うリソースに応じて、振る舞いは微妙に異なる。 これらを無視して共通化すると、APIの変更が困難になる ... というトラブルが発生する。 あるリソースだけ検証リクエストを挟もうとしても、共通化しているため、全リソースに対して検証リクエストを飛ばしてしまう ちょっと処理を変更すると、あるリソースにとっては最適解となっても、他のリソースではエラーが発生する など そのため、ポリモーフィックメソッドに変更を加える場合、ポリモーフィックメソッドで取り扱う全リソースの振る舞いを、一斉に変更する必要がある。 以上より、ポリモーフィックメソッドは使用するべきではない。 トレードオフ ポリモーフィックリソースは非常に強力なツールだが、APIに複雑さを持ち込む。 特に、リソースの表現方法が固定されてしまい、将来変更することが難しくなる そして、ポリモーフィックメソッドは、使用するべきではない。 扱うこと ポリモーフィズムとは何か 利点は何か、APIではいつ利用するべきか ポリモーフィックリソースの構成方法 リソース指向APIにおいてポリモーフィックメソッドを避けるべきる裕 まとめ APIにおけるポリモーフィズムは、リソースが様々な型を取ることを可能にし、共有機能の重複を避けることが出来る API利用者が複数の種類のリソースを同時に取得するケースがある場合、ポリモーフィズムは有効 様々な型の Message オブジェクトをまとめて取得したい場合 ... など ポリモーフィックリソースの型情報は文字列フィールドであるべき リソースの変更、追加、削除は、既存のクライアントへの影響を十分に考慮すること 無効な入力データに対しては、必要なデータが存在するかどうかチェックし、無関係なデータは無視する 無関係なデータを確認しても、エラーを投げるべきではない ポリモーフィックメソッドは硬直化する(≒変更が非常に困難)ため、利用は極力避ける

WebAPI

2023年07月22日(土)

1.0時間

APIデザインパターン 14章 アソシエーションリソース 読了

やったこと APIデザインパターン 14章 アソシエーションリソース 読了 学んだこと ポイント アソシエーションリソースとは アソシエーションリソースの利点 アソシエーションリソースが持つべき特性 標準メソッドの動作 一意性 更新 学び アソシエーションリソースは、リソース間の関係情報を保持するリソース アソシエーションリソースを提供するメリット メタデータを持たせることが出来る エイリアスを定義することで、より直感的なI/Fを提供できる アソシエーションリソースは、以下の点が、他のリソースとは扱いが異なる 標準メソッドの動作 一意性 更新処理 メモ 対象とする問題 多対多のリソースをAPIで扱いたい。 RDBでは「結合テーブル」を使用して管理するが、これをAPIで公開する方法を検討する。 そして、「結合テーブル」の行を、二つのリソース間の関連を表す個別のリソースとして公開する具体的な方法を提供する。 概要 「結合テーブル」を、アソシエーションリソースとして、APIで提供する。 アソシエーションリソースもリソースの一種なので、標準メソッドで扱うことが出来る。 フィルターも定義可能 加えて、アソシエーションリソースに、リソース間関係に関するメタデータを持たせることが出来る。 持たせる情報の例) いつ関連付けられたか、役割は何か、など アソシエーションエイリアスメソッド アソシエーションリソースを使用したフィルタリングのうち、よく使用されるフィルタリングに別名(エイリアス)を付けてアクセスしやすくする戦略。 良く使用されるフィルタリングは、カスタムメソッドとして定義してしまう。 実装 名付け 結構難しい。 悩む場合は、 "Membership"や"Association"といった単語を用いると良い。 汎用的な名前("Membership"だけ、など)を付ける場合は、コンテキストによって意味が識別できなければならない。 標準メソッドの動作 create , list, deleteは、リソース間の関連付け情報を操作するため必須。 get,updateは、メタデータを使用する場合は実装する。 つまり、getおよびupdateは、メタデータに対してのみ実施する。 アソシエーションリソースの一意性 あるリソース間のアソシエーションリソースは、常に一組でなければならない。 言い換えると、全く同じアソシエーションリソースを複数作成することが出来ない。 そのために、アソシエーションリソース作成時に検証を挟む必要がある。 作成を試みた場合、 409 conflictなどでエラー扱いする事 アソシエーションリソースの更新 リソース間の関連を更新する場合、 updateでは実施しない。 古いアソシエーションリソースを削除後、新しいアソシエーションリソースを作成する形で更新する。 updateで更新できるのは、アソシエーションリソースのメタデータのみ。 参照整合性 アソシエーションリソース一方を削除する場合、以下の動作が考えられる。 オプション 動作 カスケード 削除されたリソースを指すアソシエーションリソースを、全て削除する 制限 他のリソースが参照しているリソースを、削除しないようにする nullを設定 削除されるリソースへのポインタをすべてnullにする 何もしない リソースを削除しても、ポインターは何もせず、無効なままにしておく どれを選択するかは、アプリケーションの要件次第。 良く設定されるのは、「制限」と「何もしない」の二つ。 トレードオフ 複雑さ アソシエーションリソースは非常に柔軟性が高い。 一歩間違えると、I/Fは複雑化し、直感的ではなくなる。 加えて、アソシエーションリソースの分だけ、考慮するリソースが増える。 開発側は保守対象が増え、利用者側は学習コストが増加する。 アソシエーションによる分離 対象としているデータは同じなのに、リソースとしては「アソシエーションリソース」と「アソシエーションリソースにリンクするリソース」の二つに分離されてしまう。 扱うこと アソシエーションリソースで多対多の関係を扱う方法 アソシエーションリソースに付ける名前 アソシエーションリソース用の標準メソッドのサポート方法 参照整合性をどのように扱うか まとめ APIでは、「アソシエーションリソース」を使用することで、リソース間の多対多関係をモデル化できる 二つのリソース間の関係を表現する最も柔軟な方法 二つのリソース間の関係に関するメタデータも保持できる ChatGPT それぞれの方針には、利点と欠点があります。以下にそれぞれの利点と欠点をまとめてみましょう。 古いデータを削除した後、新しいデータを作成する(DELETE + INSERT) 利点: シンプルな手法であり、データの削除と新規作成のステップが明確で理解しやすい。 データの整合性が保たれやすく、更新処理による問題が起こりにくい。 新しいデータが元のデータと全く異なる構造を持つ場合でも対応できる。 欠点: データの削除と挿入の操作が二つに分かれているため、トランザクション管理が複雑になることがある。 大量のデータを扱う場合、削除と挿入のオペレーションに時間がかかる可能性がある。 データの削除後にエラーが発生した場合、元のデータは失われる可能性がある。 既存のデータに対して更新処理を実施する(UPDATE) 利点: 一度の操作でデータを更新できるため、トランザクション管理が容易になる。 更新のみが行われるため、DELETE + INSERTよりも効率的である場合がある。 データの削除が発生しないため、データの損失が防げる。 欠点: 更新処理が複雑になることがあり、特に複雑なデータ構造の場合や複数のテーブルにまたがる更新の場合には工夫が必要になる。 更新操作によってデータの整合性が崩れる可能性がある。特に、複数のユーザーが同時に更新を行う場合には注意が必要。 一部のデータのみを更新する場合でも、データベースの全体をスキャンする必要がある場合があるため、パフォーマンスが低下する可能性がある。 どちらの方針が適しているかは、具体的なデータの特性やアプリケーションの要件によって異なります。データベースの機能や制約、トランザクション管理、パフォーマンス、データの整合性などを考慮して、適切な方法を選択する必要があります。

WebAPI

2023年07月02日(日)

1.0時間

APIデザインパターン 13章 相互参照 読了

やったこと APIデザインパターン 13章 相互参照 読了 学んだこと ポイント 参照先を識別するフィールドの名前 参照先が削除済みの場合を考える 参照と値、両者の特徴 学び 参照先を識別するフィールド 文字列型を使用する サフィックス(末尾)をIDとし、名前で識別子であることを表現する 参照先が削除済みの場合、取れる選択肢は3つほど考えられる いずれもトレードオフがある 参照と値は、どちらも利点/欠点が存在する 要件に合わせて、どちらを使用するか検討する GraphQLの導入は、より良い解決策の一つ メモ 取り扱う問題 リソースは、同じAPIや、外部APIが保持する「他のリソース」を指すことが出来る。 この「他のリソース」を参照する方法は実装者の判断に委ねられる。 例)「他のリソース」を削除できるのか? 削除できる場合、削除した時、「他のリソース」の参照元はどう振る舞うべきか? 後処理が必要? そのままでOK? そのため、リソースを参照するガイドラインと、その参照を支える動作パターンの定義が必要になる。 相互参照パターンの概要 文字列型の一意な識別子を使用して、「他のリソース」を識別し、参照できるようにする。 相互参照パターンによって、参照元と「他のリソース」を分離できる。 これは、柔軟なAPI設計を実現できる一方で、識別子情報が古く(例: 参照先である「他のリソース」が削除された場合など)なり、識別子が無効となることを考慮しなければならない。 実装 参照フィールドの名前 サフィックス(末尾)に IDと付与し、識別子であることを名前で表現する。 データの整合性 参照先のリソースを削除する(または既に削除されている)場合、参照元のリソースは、以下の選択肢を取ることが出来る。 そもそも参照先のリソース削除を禁止する 参照先のリソースは削除できるが、識別子情報にゼロ値を設定する 参照先のリソースを削除し、実行時に無効な識別子を処理する 1と2は、API提供側で何とかする方法。 更新するデータが膨大になると処理が遅くなったり、ポインタの循環参照が発生する恐れがある。 3は、API利用者側で例外処理をお願いする方法。 API利用者側に不便を強いるが、一貫性のある振る舞いを提供できる。 値か参照か 参照を用いると、参照先のデータが常に最新であることを保証できるが、別途データの取得処理を実行する必要がある。 つまり、参照元のデータと参照先のデータを取得する、計二回の処理が必要 値を用いると、データ取得処理は一回でよいが、以下の問題を考える必要がある。 値が古くなっている可能性の考慮が必要 値が持つデータが増えると、それだけ一つのレスポンスが膨大になる API利用者が参照元リソースを更新できる場合、混乱する恐れがある 参照先リソースを更新するには、どうすればよい? より良い解決策は、参照を単独で使用し、GraphQLで参照をつなぎ合わせること。 これにより、一つのクエリで必要なリソースの最新情報を取得できる。 扱うこと リソースを参照するフィールド名にふさわしい名前 参照フィールドは参照整合性の問題をどのように扱うべきか フィールドがリソースのコピーを保持するべきタイミングとその理由 まとめ 参照を格納するフィールドは、識別子の種類が何であれ、文字列型とし、サフィックスを"ID"とする hogehogeIdなど 一般的に、参照先のリソースの存続期間にわたって参照が維持されることを期待すべきではない つまり、「参照先のリソースが削除され、参照が無効になること」を考慮するべき リソースデータは参照しているリソースにインラインで保持されていることがある 参照データと比較して、インラインデータは更新作業が必要 更新しない場合、データが古くなる可能性がある

WebAPI

2023年06月25日(日)

2.0時間

APIデザインパターン 12章 シングルトンサブリソース 読了

やったこと APIデザインパターン 12章 シングルトンサブリソース 読了 学んだこと ポイント データを分離する動機 シングルトンサブリソースの階層構造 シングルトンサブリソースに対する標準メソッドの実装 トレードオフ 学び データを分離する動機はいくつか存在する シングルトンサブリソースは、常に親リソースを持つ get と updateは、シングルトンサブリソースに対して実施できる createとdeleteは提供しない これらは、親リソースcreateとdeleteに合わせて実施する トレードオフ データを、親リソースとシングルトンサブリソースに分割すると、両者を同時に操作することが出来なくなる 上記制約(≒デメリット)と、二つに分けるメリットが、トレードオフとなる メモ 対象とする問題 あるデータAについて 設計上、リソースのプロパティに設定することが最善。 しかし、いざプロパティに設定すると様々な問題が生じる。 データ量が多すぎて(または複雑すぎて)帯域幅と計算資源を浪費してしまう セキュリティ要件上、分離することが推奨される 他のプロパティと、明らかに異なるアクセスパターンを持つ場合 特に、頻繫な更新 書き込み競合が頻繫に発生し、下手をするとデータ消失につながる そんなケース。 こういった場合は、問題が生じるデータを分離した方が良い。 シングルトンサブリソースの概要 あるリソースの構成要素を、単純なプロパティから、独立したエンティティへ分離すること。 その際、分離したエンティティは、親リソースのプロパティとして機能するのはもちろん、個別のリソースとしても機能できるようにする。 分離したエンティティを、リソースとして定義。 親リソースは、上記定義をプロパティに持つ。 type Car = { //親リソース id: string; location: Location; // シングルトンサブリソース } 委譲関係のイメージ。 実装 階層構造 シングルトンサブリソースは、親リソースを必ず持つ リソース階層のトップに存在してはならない シングルトンサブリソースは、他のシングルトンサブリソースを持たない 持たせることもできるが、価値はない 標準メソッド シングルトンサブリソースに対する標準メソッドは、次の通り。 get ... シングルトンサブリソースを取得 update ... シングルトンサブリソースを更新 create と delete ... シングルトンサブリソースには実装しない シングルトンサブリソースは、親リソース生成時にのみ、自動生成する。 また、シングルトンサブリソースは、親リソース削除時に、併せて削除する。 そして、シングルトンサブリソースのみcreateとdeleteすることはない。 必ず、親リソースへの createとdelete に合わせて、シングルトンサブリソースを createまたはdeleteする 以上より、シングルトンサブリソースを個別に createまたはdeleteするAPIはサポートしない。 シングルトンサブリソースの初期化に際して、値を渡すことは難しい。 そのため、適切な初期値を設定してやる必要がある。 あとは、 updateメソッド経由でシングルトンサブリソースを更新する。 reset メソッド 要件によっては、シングルトンサブリソースが保持するデータを、初期値へ戻したい場合がある。 この場合、上記要件をサポートする reset カスタムメソッドを実装する。 POST /drivers/1/location:reset トレードオフ データを、親リソースとシングルトンサブリソースの二つにわけたことで、この二つを同時に(≒アトミックに)操作する手段がなくなる。 同時操作できなくなるデメリットよりも、二つに分けるメリットが大きい場合、シングルトンサブリソースパターンは効果がある。 扱うこと シングルトンサブリソースとは データをシングルトンサブリソースに分割する理由と、そのタイミング 標準メソッドはサブリソースに、どのように作用するか シングルトンサブリソースはリソース階層のどこに配置するか まとめ シングルトンサブリソース プロパティとリソースのハイブリッド リソース固有のデータを、分離された別の場所に保存する get,update標準メソッドはサポートする リソースを能動的に作成/削除できてはならない そのため、create,delete標準メソッドはサポートするべきではない リソースの属性を初期状態へ戻す resetメソッドもサポートする必要がある 親リソースにアタッチされるべき そのリソースはそれ自体が別のシングルトンサブリソースであってはならない 以下の理由により、データを、リソースからシングルトンサブリソースへ分割する サイズ 複雑さ 個別のセキュリティ要件 異なるアクセスパターンとその結果生じるボラティリティ

WebAPI

2023年06月25日(日)

1.0時間

183件中の 26-50件 を表示