Slide 1

Slide 1 text

スキーマ駆動開発による 品質とスピードの両⽴ 私達は何故、スキーマを書くのか 2024/4/18 #APINight

Slide 2

Slide 2 text

⾃⼰紹介 2024/4/18 #APINight @KentarouTakeda / 武⽥ 憲太郎 Webアプリケーションエンジニア • サーバサイド: PHP - 2002年頃〜 • 型の扱いが⽐較的緩かった⾔語 • フロントエンド: TypeScript - 2013年頃〜 • 発表当初より厳密な型システムを持っていた⾔語 APIへの型付けや仕様と実装の乖離に、 課題感を持ち続けている。 スキーマ駆動開発による品質とスピードの両⽴ 1

Slide 3

Slide 3 text

アジェンダ 2024/4/18 #APINight •APIファーストが 解決した課題ともたらした困難 •スキーマ駆動開発による 品質とスピードの両⽴ スキーマ駆動開発による品質とスピードの両⽴ 2

Slide 4

Slide 4 text

APIファーストが解決した課題ともたらした困難 2024/4/18 #APINight • 仕様書の無い開発 • 仕様書のある開発 • 「仕様書」は依存に値するか? • 仕様書の冗⻑化に過ぎない実装 • 隠蔽されない知識 スキーマ駆動開発による品質とスピードの両⽴ 3

Slide 5

Slide 5 text

仕様書の無い開発 2024/4/18 #APINight • サーバサイドが出⼒するプロパティ名と フロントエンドのそれとが連動 • フロントエンドのコンポーネントが サーバサイドの実装に依存 // サーバサイド: UserController.php public function show(User $user): array { return response()->json([ 'name' => $user->name, 'email' => $user->email, 'birthdate' => $user->birthdate ->toIso8601String(), ]); } // フロントエンド: UserComponent.tsx useEffect(() => { axios.get("/api/users/" + id) .then(response => setUser({ name: response.data.name, email: response.data.email, birthdate: new Date(response.data.birthdate), }) }); スキーマ駆動開発による品質とスピードの両⽴ 4

Slide 6

Slide 6 text

仕様書の無い開発 2024/4/18 #APINight 依存関係 • インターネットを跨いで依存している。 • 担当者を跨いで依存している。 • リポジトリを跨いで依存している。 スキーマ駆動開発による品質とスピードの両⽴ 5

Slide 7

Slide 7 text

仕様書のある開発 2024/4/18 #APINight SOLID原則: 依存関係逆転の原則 上位のモジュールは下位のモジュールに依存してはならない。どちらの モジュールも「抽象」に依存すべきである。 抽象(仕様書)への依存により詳細(インターネット)を隠蔽 APIファーストはここが出発点 スキーマ駆動開発による品質とスピードの両⽴ 6

Slide 8

Slide 8 text

「仕様書」は依存に値するか? 2024/4/18 #APINight • ⾃然⾔語 • 表現⼒の限界 • 解釈の余地 • 管理 • リビジョン • ブランチ • マージ • 強制⼒ • 仕様と実装の乖離 スキーマ駆動開発による品質とスピードの両⽴ 7

Slide 9

Slide 9 text

仕様書の冗⻑化に過ぎない実装 2024/4/18 #APINight メールアドレス パスワード 名前 生年月日 スキーマ駆動開発による品質とスピードの両⽴ 8

Slide 10

Slide 10 text

仕様書の冗⻑化に過ぎない実装 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 9 axios.post("/api/users", { email: form.email, password: form.password, name: form.name, birthdate: form.birthdate, // 省略 }).then(response => { // 省略: レスポンスを描画 });

Slide 11

Slide 11 text

仕様書の冗⻑化に過ぎない実装 2024/4/18 #APINight class CreateUserRequest extends FormRequest { public function rules(): array { return [ 'email' => [ 'required', 'email', 'max:128' ], 'password' => [ 'required', 'string', 'max:32' ], 'name' => [ 'required', 'string', 'max:20' ], 'birthdate' => [ 'nullable', 'date' ], 'organizations' => [ 'required', 'array' ], 'organizations.*' => [ 'string' ], ]; } } スキーマ駆動開発による品質とスピードの両⽴ 10

Slide 12

Slide 12 text

仕様書の冗⻑化に過ぎない実装 2024/4/18 #APINight 仕様書と全く同じ内容を 別の書き⽅に書き直しているに過ぎない • ⾔語もコードベースも異なるため、再利⽤が効かない。 • ⼿作業にによるコードのため、ミスのリスクが付きまとう。 単純作業に割かれる多くの時間 スキーマ駆動開発による品質とスピードの両⽴ 11

Slide 13

Slide 13 text

隠蔽されない知識 2024/4/18 #APINight API仕様書に準拠した実装 • ⽬的: IDを指定しユーザー情 報を取得する。 • ⼿段: 仕様通りに URLを組み ⽴てGETリクエストを送る。 ネットワークの知識が フロントに漏れ出している。 抽象化された実装 • ⽬的: IDを指定しユーザー情 報を取得する。 • ⼿段: IDを指定しユーザー情 報を取得する。 適切な隠蔽が ⽬的と⼿段を⼀致させる。 // GET /api/users/{id} const users = await axios .get('/api/users/' + id); // UserApiクラスのメソッド呼び出し const user = await userApi .getUserById(id); スキーマ駆動開発による品質とスピードの両⽴ 12

Slide 14

Slide 14 text

スキーマ駆動開発による品質とスピードの両⽴ 2024/4/18 #APINight • ⾼品質で安定したものに依存する • フロントエンド開発 • サーバサイドでのスキーマ管理と統合 • TDDとスキーマ駆動開発 • スキーマの破壊的変更 • エラー原因の提供 • リリース後の品質向上 スキーマ駆動開発による品質とスピードの両⽴ 13

Slide 15

Slide 15 text

⾼品質で安定したものに依存する 2024/4/18 #APINight プリミティブな値は Json Schemaで表現可能 • 数値 { "type": "number" } • 整数 { "type": "integer" } • ⾃然数 { "type": "integer", "minimum": 1 } • non-empty-array { "type": "array": "items": { "type": "string" }, minItems: 1 } プリミティブなドメインは OpenAPI Specificationで表現可能 • ⽂字列・UI上はマスクを推奨・8⽂字以上 { "type": "string", "format": "password", "minLength": 8 } • ⽇時・ISO8601形式の⽂字列 { "type": "string", "format": "date-time" } • URL⽂字列 { "type": "string", "format": "uri" } 「解釈の余地」が問題を招く → インターフェース記述⾔語による厳密化 スキーマ駆動開発による品質とスピードの両⽴ 14

Slide 16

Slide 16 text

⾼品質で安定したものに依存する 2024/4/18 #APINight インターフェース記述⾔語の導⼊により: • 間違いが機械的に指摘される。 • 補完(エディタ・IDE) • Lint / 静的解析 • Spectral • IBM OpenAPI Validator • 実⾏時検査 • 履歴が残る。 • Git ⼈間の判断は間違える → マシンリーダブルな⾔語で判断を⾃動化 スキーマ駆動開発による品質とスピードの両⽴ 15

Slide 17

Slide 17 text

⾼品質で安定したものに依存する 2024/4/18 #APINight 安定したインターフェース記述⾔語へ依存することで、 秩序を強制し、品質を向上させる、開発⼿法 •強制が、メリットでありデメリット。 •強制のコントロールが、開発の成否を決める。 スキーマ駆動開発による品質とスピードの両⽴ 16 「スキーマ駆動開発」とは

Slide 18

Slide 18 text

⾼品質で安定したものに依存する 2024/4/18 #APINight OpenAPIドキュメントそれ⾃体に強制⼒は無い。 実⽤的に使うには、これと同じ強制が必要。 class User implements Authenticatable { } // Fatal error: // Class UserModel contains 6 abstract methods and must therefore be declared ... スキーマ駆動開発による品質とスピードの両⽴ 17

Slide 19

Slide 19 text

⾼品質で安定したものに依存する 2024/4/18 #APINight • コード⾃動⽣成: OpenAPI Generator • 仕様に準拠(=正しく依存)したライブラリが⾃動⽣成される • 「ビルドが通れば問題なし」と判断できる • バリデーション⾃動化 - OpenAPI PSR-7 Message Validator • ドキュメント: Redocly • ⼿動テスト: Swagger UI • APIテスト: runn • プラットフォーム: Integrate Postman with OpenAPI • ドキュメント /⼿動テスト / APIテスト / バリデーション⾃動化 • デリバリー / 監視 スキーマ駆動開発による品質とスピードの両⽴ 18

Slide 20

Slide 20 text

⾼品質で安定したものに依存する 2024/4/18 #APINight OpenAPIによるスキーマ駆動開発 多くの⽤途への正しい転⽤がポイント スキーマ駆動開発による品質とスピードの両⽴ 19

Slide 21

Slide 21 text

フロントエンド開発 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 20 OpenAPI Generatorによる⾃動⽣成ライブラリは: • tagがクライアントライブラリのクラスに変換される。 • operationIdが各クラスのメソッドに変換される。 • schemaが各⾔語の型定義やクラスに変換される。 • enumが各⾔語のenumや定数に変換される。 • リクエストレスポンスへの型付けが⾏われる。 • descriptionやsummaryが各⾔語のDocBlockに反映される。 • deprecatedなどのメタデータもDocBlockに反映される。 • date-timeが各⾔語の⽇時型と透過的に変換される。 以上の機能を持つ。

Slide 22

Slide 22 text

フロントエンド開発 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 21 • 型安全 • エディタ補完・ホバーヒント • 静的解析・コンパイルチェック • 抽象化 • URLやHTTPメソッドを隠蔽したAPIクライアント // UserApiクラスのメソッド呼び出し const user = await userApi .getUserById(id); // メソッド補完

Slide 23

Slide 23 text

サーバサイドでのスキーマ管理と統合 2024/4/18 #APINight • デメリット • サーバサイド(APIサーバ)のコードに責務が集中する • メリット • API仕様はサーバサイドの担当者が書くことが多い • URL(ルーティング)はサーバサイドに実装される • 型(バリデーション)はサーバサイドに実装される • 仕様と実装とを⼀致させやすい • 他の⽅法にいつでも移⾏できる スキーマ駆動開発による品質とスピードの両⽴ 22

Slide 24

Slide 24 text

サーバサイドでのスキーマ管理と統合 2024/4/18 #APINight • 仕様をアノテーションで宣⾔。すぐ下に実装。 • レスポンスの型定義(仕様)と⽣成(実装)とを紐づけ。 ここでの定義はOpenAPIドキュメントとしていつでも出⼒可能 #[OA¥Schema( title: 'User', properties: [ new OA¥Property(property: 'name', type: 'string'), new OA¥Property(property: 'email', type: 'string', format: 'email'), new OA¥Property(property: 'birthdate', type: 'string', format: 'date-time'), ], required: ['name', 'email', 'birthdate'], )] class UserResource extends JsonResource { public function toArray(Request $request): array { return [ 'name' => $this->resource->name, 'email' => $this->resource->email, 'birthdate' => $this->resource->birthdate->toIso8601String(), ]; } } スキーマ駆動開発による品質とスピードの両⽴ 23 仕様 実装

Slide 25

Slide 25 text

サーバサイドでのスキーマ管理と統合 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 24 • 全てのリクエストレスポ ンスに強制的に介⼊ • OpenAPIドキュメントに 基づきバリデーション • 400 Bad Request • 500 Internal Server Error • 成功時のみ200 OK • 200 OK時の仕様準拠を 保証

Slide 26

Slide 26 text

TDDとスキーマ駆動開発 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 25 • Red: • 失敗するテストを書く • テストは失敗する • Green: • テストに通るコードを書く • テストは成功する • Refactor: • テスト成功を維持しつつリファクタ リング • Schema: • スキーマを先に書く • リクエストは失敗する • Code: • 仕様準拠したコードを書く • リクエストは成功する • Refactor: • リクエスト成功を維持しつつリファ クタリング リクエストレスポンスのテストを1本書けば Red / Green / Refactor が回る。

Slide 27

Slide 27 text

TDDとスキーマ駆動開発 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 26 コードファースト • 迅速なプロトタイピング • 柔軟性 APIファースト • 設計品質 • DX向上 スキーマ駆動 • イテレーティブ • 仕様と実装の同期

Slide 28

Slide 28 text

スキーマの破壊的変更 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 27 破壊的変更の発⽣条件 • リクエストの型を狭める • レスポンスの型を広げる • 型を変更する SOLID原則: リスコフの置換原則への違反 • 事前条件を、派⽣型で強めることはできない。 • 事後条件を、派⽣型で弱めることはできない。 破壊的変更の条件を予め知っておくことで 発⽣の際の対応も容易に

Slide 29

Slide 29 text

エラー原因の提供 2024/4/18 #APINight •400 Bad Request フロントエンドに不⾜なく理由を 知らせる(レスポンス) •500 Internal Server Error サーバ再度が容易に理由を知れる ようにする(ログ・レスポンス) { "title": "Response Validation Failed", "status": 500, // エラー位置 "pointer": [ "data", "status" ], // エラー理由 "detail": "Value cannot be null", // オリジナルのレスポンス "originalResponse": { "data": { "id": 42, "status": null, "name": "yokawasa", "content": "APINight" } } } スキーマ駆動開発による品質とスピードの両⽴ 28

Slide 30

Slide 30 text

エラー原因の提供 2024/4/18 #APINight スキーマ駆動開発による品質とスピードの両⽴ 29 本番リリース後も仕様外レスポンスを追跡 • クラッシュさせずログに残しアラートと連携 • リリース直後など⼗分に安定していないサービス • 既存サービスへ後からスキーマ駆動開発を導⼊するケース • 開発時と同じようにエラーとする • 堅牢性よりも正当性を優先しなければならないサービス • バリデーションそのものを外す • ⻑期の運⽤を経て⼗分に安定したサービス • スループットが重要なサービス

Slide 31

Slide 31 text

私達は何故、スキーマを書くのか 30

Slide 32

Slide 32 text

⽯をどのように積むのかは、君次第だ。 31 © 2023 Hayao Miyazaki/Studio Ghibli

Slide 33

Slide 33 text

だけど、残念ながら世界には まだまだ良いAPIが少ないんだ THE API-FIRST WORLD Abhinav Asthana / © POSTMAN, INC.

Slide 34

Slide 34 text

積みやすい⽯を作るのが、私達の仕事だ。 #APINight