2011-10-31 2 views
3

Javaでは、私のドメインモデルを表現するために不変なPOJOの階層を使用したいと思います。不変のビジネスオブジェクトとMessagePackメッセージの自動変換

final ServiceId id = new ServiceId(ServiceType.Foo, "my-foo-service") 
final ServiceConfig cfg = new ServiceConfig("localhost", 8080, "abc", JvmConfig.DEFAULT) 
final ServiceInfo info = new ServiceInfo(id, cfg) 

これらのPOJOには、getterやsetterを持たないpublic finalフィールドがあります。 (ゲッターのファンならば、フィールドはゲッターでプライベートであるとふりをしてください)

MessagePackライブラリーを使ってこれらのオブジェクトをシリアル化して、それらをネットワーク経由で渡し、ZooKeeperノードなど

問題は、MessagePackはパブリックな非最終フィールドのシリアル化しかサポートしないため、ビジネスオブジェクトをそのままシリアル化することができないということです。 また、MessagePackは enumをサポートしていないので、enum値を intまたは Stringにシリアル化する必要があります。 (これは、enumに注釈を追加した場合です。

これに対処するために、私はそれぞれのビジネスオブジェクト間の変換を伴う、 "メッセージ"オブジェクトの手書きの対応する階層を持っています対応するメッセージオブジェクト明らかにこれは理想的ではありません。重複したコードが大量に発生し、人為的なエラーによりフィールドが失われる可能性があります。

この問題の解決策はありますか?

  • コンパイル時のコード生成?
  • 実行時に適切なシリアライズ可能なクラスを生成するいくつかの方法はありますか?
  • MessagePackをあきらめますか?
  • 不変性をあきらめて、私のビジネスオブジェクトにenumを入れますか?
  • 変更可能なオブジェクト(メッセージオブジェクト)を不変のオブジェクト(ビジネスオブジェクト)にラップできる汎用ラッパーライブラリがありますか?私は自動的に近いソリューションに私を得ることがJava Beanが、へ/から不変オブジェクトに変換することができますので、もし

はMessagePackはまた、Javaの豆のシリアライズ(@MessagePackBeansアノテーションを使用)をサポートしています。

+0

[@OrdinalEnum](https://github.com/msgpack/msgpack-java/blob/master/src/main)を追加すると、MessagePackが 'enum'タイプをサポートできることを試行錯誤で発見しました。 /java/org/msgpack/annotation/OrdinalEnum.java)アノテーションをすべてのenumに追加します。 @Delegate注釈が何をしているのかを今すぐ解説してください... –

答えて

0

あなたのアプリケーションの読み書きの問題を分けたのではなく、マージしたようです。この時点でおそらくCQRSを考慮する必要があります。

私の経験では、不変のドメインオブジェクトは、ほとんどの場合、監査ストーリー(要件)に添付されているか、ルックアップデータ(列挙型)です。

あなたのドメインは、たぶん変更可能ですが、ゲッターとセッターはまだ必要ありません。その代わりに、あなたのオブジェクトに動詞を持たせて、ドメインモデルを変更し、ドメイン内で何か面白いことが起こったときにイベントを発生させるべきです(あなたの時間を費やしているビジネスビジネスにとって面白い)。ドメインオブジェクトではなく、ワイヤを渡すことに興味があるのはおそらくイベントです。たぶんそれはコマンドでさえあります(これらはイベントに似ていますが、ソースはドメインが存在する境界のあるコンテキストの外部のエージェントで、イベントはモデルの境界にあるコンテキストの内部にあります)。

イベントを永続化するサービス(およびコマンドを永続化させるサービス)を持つことができます。これは、監査ログ(監査ストーリーの実行)でもあります。

イベントをバスにプッシュするイベントハンドラを持つことができます。これらのイベントには、単純情報またはエンティティIDのいずれかが含まれている必要があります。これらのイベントに応答するサービスは、提供された情報を使用して職務を遂行する必要があります。または、指定されたIDを使用して必要な情報を問い合わせる必要があります。

ドメインモデルの内部状態を公開してはいけません。あなたはこれを行うことでカプセル化を破っています。それは本当に望ましいことではありません。もし私があなただったら、Axon Frameworkを見てください。 MessagePackだけでは実現できません。

+0

CQRSは実際に私の使用例に合っていません(サーバーは不変の構成情報をお互いに渡します。実際にドメインレベルのイベントは起こっていません)。しかし、私はCQRSの優れた説明とAxonフレームワークへのリンクに感謝します。以前はそのフレームワークを見たことがなかった。それは他のプロジェクトにとって非常に役に立つかもしれないように見えます。 –

1

偶然、私は最近あなたが描いているものをかなり正確に行うプロジェクトを作成しました。 の不変データモデルを使用すると大きな利点が得られますが、多くのシリアライズテクノロジは、後で考え直しとして 不変性に近づいているようです。私はこれを解決するものが欲しかった。

私のプロジェクトGrainsは、コード生成を使用して、ドメインモデルの不変実装 を作成します。この実装は、異なるシリアライゼーションフレームワークに適応できるほど一般的です。 MessagePack、Jackson、Kryo、および標準のJavaシリアル化がサポートされています。

ドメインモデルを記述するインターフェイスのセットを書くだけです。例えば:

public interface ServiceId { 
    enum ServiceType {Foo, Bar} 

    String getName(); 
    ServiceType getType(); 
} 

public interface ServiceConfig { 
    enum JvmConfig {DEFAULT, SPECIAL} 

    String getHost(); 
    int getPort(); 
    String getUser(); 
    JvmConfig getType(); 
} 

public interface ServiceInfo { 
    ServiceId getId(); 
    ServiceConfig getConfig(); 
} 

穀物 Mavenプラグインは、次にコンパイル時にこれらのインタフェースの不変実装を生成します。 (生成元は人間が読むように設計されています。)次に、オブジェクトのインスタンスを作成します。この例 は、二つの工事のパターンを示しています

ServiceIdGrain id = ServiceIdFactory.defaultValue() 
    .withType(ServiceType.Foo) 
    .withName("my-foo-service"); 

ServiceConfigBuilder cfg = ServiceConfigFactory.newBuilder() 
    .setHost("localhost") 
    .setPort(8080) 
    .setUser("abc") 
    .setType(JvmConfig.DEFAULT); 

ServiceInfoGrain info = ServiceInfoFactory.defaultValue() 
    .withId(id) 
    .withConfig(cfg.build()); 

は、私はあなたのpublic finalフィールドと同じくらい簡単か分からないが、継承と組成はゲッター とsetterなしで可能ではありません。そして、これらのオブジェクトを簡単に読み、MessagePackで書かれている:粒がフレームワークがあなたのために動作しない場合

MessagePack msgpack = MessagePackTools.newGrainsMessagePack(); 

byte[] data = msgpack.write(info); 
ServiceInfoGrain unpacked = msgpack.read(data, ServiceInfoGrain.class); 

、そのMessagePack templatesを検査して自由に感じます。 リフレクトを使用して手書きドメインモデルの最終フィールドを設定する汎用TemplateBuilderを記述することができます。トリック は、カスタムビルダーの登録を可能にするカスタムTemplateRegistryを作成することです。

+0

恐ろしい!本当に便利ですね。 –

関連する問題