JavaでWebサービスを作成しています。これは、DBから情報を読み取り、S3に書き込まれる複数のJSONファイルを生成します。ファイルのタイプごとに、JSONにシリアル化されたPOJOがありますジャクソンを使用して。
ファイルのスキーマは時間とともに変化する可能性があります。新しいフィールドを追加したり、既存のフィールドを削除したりできます。スキーマを変更すると、新しいバージョンのファイルを作成する必要があります。したがって、いつでも、S3にファイルの複数のバージョンが存在する可能性があります。
現在のアーキテクチャは非常に単純です。DBからデータを取得するためにいくつかのクラスを呼び出すControllerクラスがあります。その後、そのデータをFileGeneratorクラスに渡します。FileGeneratorクラスは、複数のPOJOを作成し、Repositoryクラスを介してそれらをS3に書き込みます。
この設計を変更して、よりモジュール化し、ファイルの複数のバージョンを処理する必要があります。
今までこれが私が思いついたものです:
各タイプのファイル(またはPOJO)を生成するためのジェネレータークラス(File1Generator、File2Generatorなど)
各タイプのファイルのFileManagerクラス。ジェネレーターを呼び出し、POJOをシリアル化してから、データストアに書き込みます。 (File1Manager、File2Managerなど)
これで、Controllerクラスはすべてのファイルマネージャーを1つずつ作成します。
ただし、ファイルの複数のバージョンを処理できるようにシステムを設計する方法がわかりません。 POJOの場合、バージョンごとに個別のPOJOを用意する必要があります-File1POJO_V1、File1POJO_V2。しかし、その後、追加のジェネレーターと追加のマネージャーも追加します。ジェネレーターに渡される情報は、バージョンによって異なる場合があります。マネージャーと同じです。コードを簡単に拡張できるように、Niceクラス階層を作成するのに苦労しています。以下は私が考えたコードの骨組みです:
class File1POJOV1{
private String prop1;
private String prop1;
..........
public setProp1(){}
public setProp2(){}
}
class File1Generator_V1{
public File1POJOV1 createFile(String prop1, String prop2 ...){
//Perform manipulations and create File1POJO_V1
}
}
class File1Manager_V1{
private File1Generator_V1 fileGenerator;
private Serializer serializer; //JSON serializer
public void createFile(String prop1, String prop2,.., String directory){
File1POJOV1 file = fileGenerator.createFile(prop1, prop2 ...);
byte[] data = serializer.serialzer(data);
String path = generateFilePath(); //dynamic based on properties
writeToS3(data, path, directory); //write data to S3 bucket
}
}
class Controller{
File1Manager_V1 manager1_V1;
File2Manager_V1 manager2_V1;
generateFile(String prop1, prop1..., directory){
manager1.createFile(prop1, prop2..,directory);
manager2.createFile(prop3, prop4.. directory)
}
}
問題は、ファイルの新しいバージョンがある場合、以前のバージョンよりも多い(または少ない)フィールドを持つPOJOが個別に存在することです。メソッドへの入力パラメーターが変更されると、この変更はジェネレーターとマネージャーに伝達されます。メンテナンスが簡単なこのシステムのエレガントなデザインの仕方がわかりません。
[〜#〜] edit [〜#〜]:ファイルの連続するバージョンでフィールドを削除しないようにするための良い提案があります。しかし、私の質問は残っています。フィールドを削除せず、新しいバージョンがそれぞれ古いバージョンのサブクラスであると仮定すると、各サブクラスごとに別個のジェネレーターとマネージャーを作成する必要があります。これは回避できますか?より良いデザインは何でしょうか?
ここでは、他のコンポーネント/サービス/クライアントが使用するためにJSONファイルが生成されると想定しています。したがって、フィールドを削除しないようにしてください。新しいフィールドを追加するだけの場合は、JSONファイルのコンシューマーが知らないフィールドを無視する限り、新しいバージョンを作成する必要はありません。
消費者が知らない機能を無視するインターフェースはより堅牢です。たとえば、V0でデータを消費し、1年間、隔週で新しいフィールドを追加し、1年後にV26に到達したとします。次に、V27で、使用したいフィールドも追加します。 V1からV26までの間に追加されたフィールドを使用しない場合でも、それらを処理するようにコードを更新することに煩わされるべきですか?私はそうは思いません。
一方、フィールドの削除は別の獣です。まれに、大量に行う必要があります。 セマンティックバージョニング のメジャーバージョンとマイナーバージョンの全体的な考え方は、それについてです。何かを追加した場合、それはマイナーバージョンであり、ユーザーには影響しません。削除した場合、メジャーアップデートであり、依存コードが破損する可能性があります。
これは、サブタイプ、多型、および substututability の概念とも一致します。基本的に、新しいフィールドをFirstPOJO
に追加するには、それを変更するか、SubPOJO
を追加してFirstPOJO
を拡張するsomeField
にサブクラス化します。 。 FirstPOJO
に対して作成されたコードは、SubPOJO
を透過的に処理できます。もちろん、物事を削除し始めると、コードが壊れる可能性があります。
これはあなたの質問に正確に答えるものではありません。しかし、基本的な問題は、拡張できないコードアーキテクチャがあることです。そもそもスケーリングの必要性を減らすことで、問題を回避できます。
答えは、ファイルの書き込み以外に達成しようとしていることに依存すると思います。
とりあえずPOJOを省いてみましょう。DBからデータを読み取り、fileMangerクラスを介して特定のファイル形式に書き込みます。 DB読み取りは、必要な列が存在する限り問題ないので、いくつかの汎用データリーダータイプを返します。
同じDBデータを別の形式で書き込みたい場合は、別のfileMangerV2クラスを作成できます。
DBにフィールドを追加する場合も、問題ありません。
フィールドを削除すると、問題が発生し、下位互換性の問題が発生するため、先に進んでそのデータを削除することはできません。
つまり、これは基礎となるデータに関するものです。それがすべてのファイルバージョンで同じである場合、そのデータ構造に一致する1つのPOJOと、そのファイル形式への読み取りと書き込みを処理するファイル形式ごとに1つのfileManagerが必要です。
ただし、一致するPOJOを使用してファイル形式をコードに公開し、基になるデータ構造が同じである場合は、継承する(すべての基本クラスプロパティを公開してもかまわない場合)またはラップする新しいクラスを作成できます。ベースPOJO
あるPOJOから別のPOJOへの変換は比較的簡単で、基本となる基本クラスを公開するだけです。このコードは、fileManagerではなくオブジェクト自体に含めることができます。これにより、すべてのPOJOに、GetFieldsForWritingToFile()または同様の何かを公開する共通のインターフェイスICanBeWrittenToAFileを実装させることにより、リフレクションまたはそれ以上で任意のファイル形式を出力できる単一のfileManagerクラスを使用できるようになります。
コントローラとfileManagerは、特定のPOJOではなくICanBeWrittenToAFileを入力として受け取ることができ、1つのfileManagerを挿入するだけで済みます。
ここで、ファイルのバージョンに関係なく、データに対して実行できるようにするビジネスロジックXがあるとします。 POJOにICanPerformLogicXなどを実装させることで、同じ概念を拡張できます。