web-dev-qa-db-ja.com

オブジェクトモデルから異なるバージョンのコントラクトまたはDTOへのマッピングを処理する方法

APIコントラクトの異なるバージョンを維持するのパターンまたはベストプラクティスを探しています。 APIのバージョン管理方法(URLとヘッダーなど)についてはインターネット上に多くの情報がありますが、この質問は1つのオブジェクトモデルから異なるバージョンのコントラクトタイプへのマッピング(DTO)に関連しています。そのため、実際には情報を見つけることができませんでした。

エンドポイントを持つWebプロジェクトと、そのエンドポイントを使用してWebプロジェクトと通信するクライアントアプリ(デスクトップまたはモバイル)があるとします。エンドポイントはクライアントアプリでのみ使用されます。つまり、エンドポイントはパブリックではなく内部エンドポイントです。 Webプロジェクトとクライアントアプリ間の通信は、クライアントアプリがエンドポイントにデータを送信することで機能し、Webプロジェクトはそれを受信して​​処理し、応答オブジェクトを返します。エンドポイントに送信され、エンドポイントから送り返されるデータの構造がどのように見えるかについては、契約があります。これは、オブジェクトタイプの数とそのネストのタイプの点でかなり複雑です。ユーザーがクライアントアプリをいつアップグレードするかを制御することはできません。下位互換性を維持することはビジネス要件です。

APIコントラクト(DTO)とオブジェクトモデルを分離したので、進化するオブジェクトモデルがありますが、問題はありません。オブジェクトモデルからコントラクトに、またはその逆にマッピングするマッピングコードがあります。

コントラクトに互換性のない変更を加えることができますが、互換性のない変更を行う必要がある場合は、新しいバージョンのコントラクトが必要であり、オブジェクトモデルから新しいコントラクトにマップできることを確認する必要があります。 。ただし、下位互換性が必要なため、オブジェクトモデルから以前のバージョンのコントラクトに何らかの形でマップできることを確認する必要もあります。

私の質問は次のとおりです。どのようにこれをアーキテクチャ的に処理しますか?これに使用できるパターンまたはベストプラクティスを知っていますか?

問題は、オブジェクトモデルを変更して新しいコントラクトを作成するときに、古いコントラクトが機能し続ける方法があることを確認する必要があることです。 一方通行オブジェクトモデルからコントラクトの各バージョンへのマッピングを維持することで、これを実行できると思います。

別の方法は、オブジェクトモデルから最新のコントラクトへのマッピングを維持するだけで、異なるバージョンのコントラクトからの移行パスを維持することができます(したがって、v3からv1にマッピングする必要がある場合、オブジェクトモデルから最新のコントラクト(v3)にマップし、次にv2にマップしてから、v1にマップします)。

考慮すべきもう1つの問題は重複の量です。コントラクトに多くのタイプがあり、タイプの1つから1つのフィールドを削除する必要がある場合、これは重大な変更であり、新しいバージョンが必要です。契約全体(すべてのタイプ)をプロジェクトの新しいフォルダーにコピーしてから、1つのフィールドを削除しますか?これは、ほぼ同一の契約を結んでいることを意味し、多くのタイプが複製されます。この場合の複製は正当化できますが、もっと良い方法はありますか?

ポインタ、洞察、提案は高く評価されています。

2
René

あなたが説明する問題はオブジェクト直列化システムにも同じ形で存在し、これらの問題はすべて長年にわたって解決されてきました。コントラクトとして説明するものは、上位レベルでオブジェクトのシリアル化形式と見なすこともできます。

シリアライゼーションの場合、一般的に言って、「契約」の単一バージョンのみが実装されています。この実装は、条件付きパスが取られていない最新バージョンをサポートしています。つまり、デフォルトの最も単純なコードパスが最新バージョンをサポートします。以前のバージョンとの下位互換性を維持するには、オブジェクト変更のタイプに基づいて条件を導入する必要があります。

  • プロパティの削除(私は.netのようにWordプロパティを使用していますが、オブジェクトのパブリックフィールドです):通常、下位互換性のために何もする必要はありません。
  • プロパティの追加:プロパティに有用なデフォルト値を提供する場合、APIユーザーは新しいプロパティを初期化する必要はありません。 Wordの「プロパティ」を使用して、直交するデータも参照しています。あるプロパティは別のプロパティに影響を与えません。これは、優れたAPI設計の一部であり、下位互換性の要件です。
  • プロパティのタイプまたは名前を変更します。この場合、条件コードを追加して、APIコンシューマーのバージョンを調べ、名前またはタイプを新しい名前またはタイプに変換する必要があります。
  • データ型の名前を変更します。変更も同様です。場合によっては、変換前にデータをロードできるように、古いデータ型を保持する必要があります。

次に、シリアライゼーションの互換性ではなく、APIの互換性を検討します。多くの点で、これはより難しい問題です。必要な正確なバージョンでAPI全体を提供する必要があるためです。そのAPIに追加できますが、古いAPIの機能を変更することはできません。すべての古いデータ型とすべての古いプロパティを保持する必要があります。一般的な戦略はこれです:

  • 古いAPIに干渉することなく、新しい機能と新しいデータ型を追加します。新しいプロパティを追加しても問題ありません。
  • 新しいコードを書くユーザーは最新バージョンを使用します。
  • 古いAPI全体をサポートしているため、以前に作成したコードを使用するユーザーは問題ありません。
  • バージョン管理の経過時間を追跡する:ある時点で、古いバージョンを維持するのが非常に難しくなります。この時点で、古い互換性コードをすべて削除したバージョンを作成します。古いバージョンのAPIを使用しているユーザーは、引き続き古いライブラリにリンクできます。

APIの進化に関する主な戦略は、決して変更することではありません。つまり、APIは非常によく設計されている必要があり、最も具体的なデータ型ではなく、最も一般的なデータ型を作成することを考えます。あなたは何年も先を計画しなければなりません。特定の分野で長年の経験があると役立ちます。入念な計画を立てても、APIは変更する必要がありますが、少なくとも変更を最小限に抑えることができます。

3
Frank Hileman