私の経験では、私たちが見つけた最もクリーンな(しかし最も簡単ではない)ソリューションは5つの部分に分かれています。
- 信頼できるデータモデルと常に最新のバックエンド(DAL /データベース/サービス)を用意します。
- システムがすべて理解できるバージョン固有のデータを持つ:複数データモデル。
- バージョン間の変更が適切に識別および追跡され、変更が元に戻せることを確認します。ルールは、それを処理するために明示的に定義する必要があります(それは難しいかもしれません):コンバーター。
- クライアントに、使用するバージョンを明示的に通知させる:クエリ文字列/ヘッダー。
- 常に最新であるが、異なるデータバージョン間を移動する方法を知っている信頼できるビジネスロジックを持っている-下位互換性:コントローラー。
たとえば、私たちが持っているデータモデルはサプライヤーです。
(1)バックエンド(つまり、DAL /データベース)はV5にあります。ビジネスロジック(つまりサービス)もV5にあります。
(2)ただし、V1、V2、V3、V4ではクライアントサプライヤーに、V5では最新のクライアントにすべて同時にサービスを提供する必要があります。データモデルのV1からV5を作成しましょう:SupplierV1、SupplierV2 ...(名前空間、またはそれらを区別する他の方法を使用できます)
(3)コンバーターを定義して管理する必要があります。データモデルバージョン間の下位互換性と下位互換性の両方を処理する必要があります。これは、ストラテジーパターン、ラムダ、DIコンバーターを備えたマネージャーなどで実行できます。V1-> V2-> V3-> V4-> V5だけでなく、V5-> V4->V3->V2も処理する必要があります。 ->V1。
コンバーターのルールは最も難しい部分です。簡単な場合もあります-新しいフィールドのデフォルト値。それ以外の場合は、ビジネスロジックを実行する必要があります。既存のデータを変換/マージする必要がある場合があります。あなたはそれが可逆的であることを確認する必要があります!たとえば、値がV1で大文字と小文字が混在していて、V2で大文字に変換した場合、どの文字が大文字か小文字かわからないため、V1に戻すことはできません。その2つの方法を処理できます。
- V1の場合は大文字のままにします。結局のところ、それを必要とするのはV2だけであり、V1はおそらくすべての大文字で機能します(明らかにキーでは機能しません)。
- V2のフィールドに古い値を保持します。たとえば、フィールドStateを大文字にすることにした場合、大文字と小文字が混在するOriginalStateを保持し、V1に戻るときにStateにコピーできます。
あなたが見ることができるように、あなたはそれらについて一生懸命考える必要があり、それはしばしば自明ではありません。
(4)次に、コントローラーでは、必要に応じてコントローラーの内外で変換を行うためにクライアントがどのバージョンで動作するかを知る必要があります。このために、クエリ文字列、ヘッダー(たとえば、X-API-VersionまたはAcceptですが、一部のホスト/プロキシがそれらの一部を削除することに注意してください)、postパラメーターなどを使用できます。
(5)最後に、コントローラーがデータモデルを受信したら、クライアントが送信したバージョン(V2など)を確認し、バックエンド(V5)の最新バージョンにアップグレードする必要があります。次に、それを適切に使用します。その後、データを送り返す必要がある場合は、クライアントバージョン(V2)にダウングレードする必要があります。これを行うには、カスタムバインディング、カスタムリクエストアクション、カスタムアクションの結果などを実行できます。たとえば(自動化してください)。
public IHttpActionResult DoSomethingWithSupplier(JToken supplier) // Receiving Json Supplier
{
// TODO: Get the client version type, for example in the X-API-Version header/query strings
// (beware, some proxy/hosts strips some headers)
// Type clientType = ...
var clientSupplier = JsonConvert.DeserializeObject(supplier.ToString(), clientType);
// You should probably detect latest version automatically (instead of typeof)
var latestSupplier = VersionManager.Upgrade(clientSupplier, clientType, typeof(SupplierV5));
outputSupplier = DoSomething(latestSupplier);
// You should probably detect latest version automatically (instead of typeof)
var clientOutputSupplier = VersionManager.Downgrade(outputSupplier, typeof(SupplierV5), clientType);
return Ok(clientOutputSupplier);
}
これは、アイデアを示すための非常に大雑把な方法です。これは、私たちのシステムの1つで行ったことです。タイプとバージョンを検出して変換自体を処理するマネージャーを作成したり、依存性注入を使用してアセンブリ/コンバーターを動的にロードしたり、カスタムバインディング/リクエストアクションでほとんどの変換を自動化したりすることができます。
注:考慮する必要があるかもしれない部分(6)もあります。データベース内のクライアントデータを実際にV5に更新するには、V5への移行(データのバッチ移行)時に更新するか、実行時に更新します。SupplierV1を受け取ったら、それをデータベース(V1データのまま)からロードし、V5にアップグレードして、更新されたデータをすべてコンバーターに保存します。これは、バックエンドをオンザフライで移行できることを意味します。明らかに、同じデータベースで両方のバージョンをサポートする必要があるため、思ったほど簡単ではありませんが、変更やデータの種類によってはうまくいく場合があります。