コンテンツへスキップ
DedicatedPHP 接触

フロントエンドとPHPバックエンド間の有用な契約を保つ方法

明確な契約、適切な配置のバリデーション、実際の利用状況を反映したテストにより、独立したインターフェースとPHPアプリケーションの変更を調整します。

データスキーマと契約テストを伴い、APIを介してPHPアプリケーションに接続されたフロントエンドの図

フロントエンドとPHPバックエンドを別々に開発またはデプロイすると、各チームはそれぞれのペースで変更をリリースできます。この自律性は作業の整理に役立ちますが、摩擦も生みます。APIがすでに返さなくなったフィールド、形式、状態を、インターフェースが期待し続けることがあるためです。この問題は、両チームが常に同時に変更することで解決するのではありません。明示的な契約、明確に区分された責任、そして統合が実際にどう使われるかを確認するテストによって解決します。

フロントエンドとPHPバックエンド間の契約は、一方が提供し、もう一方が利用するデータと振る舞いを定義します。単なるルートの一覧ではなく、メソッド、パラメーター、レスポンススキーマ、エラー、権限、互換性ルールを含みます。各側が相手の内部実装に依存せずに進化できるようにすることが目的です。

独立したバージョンが摩擦を生む理由

独立したバージョンが摩擦を生む理由 — guía visual de DedicatedPHP

インターフェースとサーバーではリリースサイクルが異なることがあり、すべてのユーザーが新しいバージョンを同時に受け取るとは限りません。キャッシュされたフロントエンドが、バックエンドの更新後も古いリクエストを送信し続ける場合があります。逆に、APIに追加された機能をインターフェースがまだ認識していないこともあります。

互換性の問題は、一見小さな変更から生じることがよくあります。プロパティ名の変更、数値から文字列への型変更、任意フィールドの必須化、日付形式の変更、異なる構造でのエラー返却などです。サーバーのコードとしては妥当な変更でも、既存の利用側を壊すことがあります。

実装と契約は区別する必要があります。公開済みの振る舞いを維持する限り、バックエンドはAPIに影響を与えずにデータベースを変更したり、内部サービスを再編成したりできます。同様に、フロントエンドはコンポーネントを作り直しても、ビジネスルールを変更する必要はありません。

インターフェース、API、ドメインそれぞれの責任

インターフェースが担うのはユーザー体験です。情報の表示、ユーザーの案内、形式エラーへの即時応答、読み込み・成功・結果なしといった表示状態の管理を行います。こうしたチェックは操作性を高めますが、セキュリティ上の防壁ではありません。公式インターフェースを使わずにリクエストを変更したり送信したりできるためです。

APIは外部プロトコルとアプリケーションの間を仲介します。リクエストの形式を検証し、必要に応じて認証・認可を行い、データを変換して、予測可能なレスポンスを返します。内部クラスや永続化スキーマが変わっても、安定した表現を維持する必要があります。

ドメインには、どの操作がビジネス上有効かを決めるルールが含まれます。たとえば、状態遷移が許可されているか、操作に事前条件が必要かといったルールです。こうしたルールをJavaScriptだけに置いたり、複数のコントローラーに独立して重複させたりしてはいけません。操作を受け入れるか拒否する権限を持つのはバックエンドであるため、バックエンドがルールを適用する必要があります。

ユーザーを案内するため、フロントエンドでルールを事前チェックしてもかまいませんが、最終判断はサーバーで再検証します。画面上のチェックは、バックエンドのバリデーションや認可の代わりにはなりません。

入力、出力、エラー、状態を定義する

各操作について、少なくともメソッドとルート、パラメーター、必須・任意フィールド、型、形式、関連する制約、レスポンス形式を文書化します。また、フィールドが欠落している場合、nullの場合、空のリストを含む場合の意味も明確にしてください。これらは常に同じ状態を意味するわけではありません。

レスポンスは実装の詳細を明かすのではなく、利用側が理解できる結果を表す必要があります。HTTPステータスコードと、安定したコードまたはカテゴリ、インターフェースに適したメッセージ、必要に応じてフィールドに関連付けられたエラーを含む一貫したエラー構造を合意します。スタックトレース、SQLクエリ、機密情報は公開しないでください。フロントエンドは、変更される可能性がある内部メッセージに依存するのではなく、既知のコードをローカライズ済みメッセージに変換できます。

該当する場合は、リソースが存在しない、アクセスが拒否された、現在の状態と競合する、バリデーションに失敗した、リクエストが制限された、といったフローに影響する状態も記述します。ページネーションされたデータでは、結果、上限、続きの表現方法を定義します。日付では形式とタイムゾーン、金額では通貨と単位を指定します。各チームが推測に頼る範囲が小さいほど、食い違いが生じる可能性は低くなります。

ドメインを重複させずに両レイヤーで検証する

目的が異なるため、繰り返すのが適切なバリデーションもあります。インターフェースはフォーム送信前に必須フィールドが空でないか確認できますが、APIも有効なリクエストだけを受け入れるため、再度確認する必要があります。前者は操作時の摩擦を減らし、後者はシステムの整合性を守ります。

ドメインロジックをすべてフロントエンドに複製する必要はありません。ルールが複雑、頻繁に変わる、またはサーバーのデータに依存する場合は、バックエンドをルールの権威とし、明確に対応できるエラーを返します。インターフェースで結果を事前に把握する必要があるなら、照会または事前検証の操作を設計します。ただし、確定操作の前に状態が変わる可能性がある点に注意してください。変更を確定する操作では、条件を再検証する必要があります。

入力バリデーションは、認可、アクセス制御、濫用防止策の代わりにもなりません。各リクエストは、対象となるユーザーとリソースに基づいてサーバーで評価する必要があります。PHPでは、トランスポート層のバリデーション、ルールの適用、永続化の間に明確な境界を設け、自社のインターフェースから送られたという理由でデータを信頼しないことが重要です。

互換性のある変更を導入し、互換性のない変更を調整する

既存の利用側がコードを変更せずに使い続けられるなら、その変更は通常、互換性があります。任意フィールドの追加も、クライアントが未知のフィールドを許容し、新しいフィールドが既存フィールドの意味を変えない限り、互換性を保てる場合があります。プロパティの削除や名前変更、型の変更、古いクライアントが使うデータの必須化は、通常、互換性のない変更です。

契約を変更する前に、利用側、デプロイ済みバージョン、キャッシュ、APIを呼び出すバックグラウンドプロセスを特定します。互換性を保った進化では、サーバーに新しい振る舞いを追加しつつ、古い振る舞いを一時的に維持できます。フロントエンドは新機能を使い始め、古い利用側が存在しないことを確認した後、明示的な判断を経て旧機能を廃止できます。

互換性を保てない場合は、新しいバージョン、または日程、責任者、廃止基準を定めた移行を検討してください。異なる利用側を管理できる場合にのみ、バージョン追加に価値があります。小さな変更ごとにバージョンを付けると、保護よりもコストが増えることがあります。コードのデプロイは、機能の有効化と必ずしも同じではありません。テストが完了するまで無効のままデプロイしたり、段階的に有効化したりできます。この制御された有効化を、ユーザーへの情報開示と混同してはいけません。

実際の利用シナリオで契約をテストする

APIテストでは、バックエンドが有効な入力を受け入れ、無効な入力を拒否し、合意したスキーマと状態を返すことを確認します。フロントエンドのテストでは、レスポンスを正しく解釈し、各状態を適切に表示することを確認します。統合テストでは、製品に応じて作成、照会、更新、エラー処理など、重要なフローで両者を組み合わせて実行します。

契約テストでは、実装全体をテストせずに、特定の利用側に対する具体的な期待値を定められます。有用なテストにするには、公開前に食い違いを検出するプロセスで実行し、該当する場合は任意フィールドの欠落、バリデーションエラー、アクセス拒否レスポンスなどのケースを網羅します。API仕様などの記述的な契約は、レビューや型生成を容易にしますが、それだけで実装が契約を守る保証にはなりません。統合フロー内でのテストまたは検証が必要です。

ルートの一覧だけでなく、実際の操作とリスクに基づいてシナリオを選びます。エンドポイントが正しい型を返していても、状態遷移や権限のないユーザーへの対応に問題があることはあります。自動テストに変更レビューとオブザーバビリティを組み合わせてください。クライアントエラー、予期しないレスポンス、バージョン間の差異は、デプロイ後の問題特定に役立ちます。

公開前のチェックリスト

公開前のチェックリスト — guía visual de DedicatedPHP
  • 誰がこの操作を利用し、どのバージョンが引き続き稼働している可能性があるか明確ですか?
  • フィールド、型、任意か必須か、形式、エラー、関連する状態を文書化しましたか?
  • インターフェースに同等の補助機能があっても、ビジネスルールと認可はバックエンドで適用されますか?
  • 変更は既存の利用側との互換性を保ちますか。それとも移行と廃止戦略が必要ですか?
  • 重要なエラーを含め、契約と統合フローのテストがありますか?
  • 機密データを露出させずに、ログとメトリクスで非互換性を検出できますか?
  • 問題が発生した場合に、変更を安全に無効化またはロールバックする方法がありますか?

有用な契約はアーキテクチャを固定するものではなく、システム間の境界で予期せぬ事態を抑えるものです。責任を明確にし、原則として互換性のある変更を行い、観測可能な振る舞いに焦点を当てたテストを実施すれば、チームは変更のたびに緊急の手動調整を行うことなく、別々にデプロイできます。

これらのアイデアをあなたのプロジェクトに活用してみませんか?あなたのPHPプラットフォームについて話し合いましょう。
関連サービスを見る