読者です 読者をやめる 読者になる 読者になる

LagomにおけるJSONのシリアライズとマイグレーション

Lagom Java

もはや若干やけくそ感もあるLagomを試してみる第12回です。今回はLagomにおけるJSONシリアライズ/デシリアライスを見ていきたいと思います。

ここまで見てきたようにLagomはHTTPやWebScoketなど外部とのやりとりにはJSONを使用しますが、コマンドやイベントといった内部のアクター間のメッセージのやり取りにもJSONを使うことが推奨されており、Jsonableというマーカインターフェースを実装していればJSONシリアライズ/デシリアライズしてくれるようです。また、大きなメッセージの場合は代わりにCompressedJsonableインターフェースを実装することで圧縮を行うこともできるようです。

LagomのJSON変換はJacksonが用いられており、デフォルトでは以下のモジュールが有効になっているようです。

# The Jackson JSON serializer will register these modules.
# It is also possible to use jackson-modules = ["*"] to dynamically
# find and register all modules in the classpath.  
jackson-modules = [
  "com.fasterxml.jackson.module.paramnames.ParameterNamesModule",
  "com.fasterxml.jackson.datatype.jdk8.Jdk8Module",
  "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule",
  "com.fasterxml.jackson.datatype.pcollections.PCollectionsModule",
  "com.fasterxml.jackson.datatype.guava.GuavaModule"]

加えてJavaオブジェクトとJSONの変換を行うだけでなく、JSONスキーマ変更時の差分を吸収するための機能も提供しています。

プロパティの削除は特に気にせずフィールドを削除すればいいのですが、追加する場合でなおかつ初期値が必要な場合は@Value.Defaultアノテーション付きのメソッドを定義しておけばいいようです。

@Value.Immutable
@ImmutableStyle
@JsonDeserialize(as = ItemAdded.class)
public interface AbstractItemAdded extends Jsonable {
  ...
  @Value.Default
  default String getNote() {
    return "";
  }
}

フィールドのリネームや構造の変更など、複雑なマイグレーションの場合はJSONをバージョニングしておき、バージョン番号を見て任意の変換処理を走らせることができます。

この場合、以下のようにJacksonJsonMigrationを継承したマイグレーションクラスを作成する必要があります。

public class ItemAddedMigration extends JacksonJsonMigration {

  @Override
  public int currentVersion() {
    return 2;
  }

  @Override
  public JsonNode transform(int fromVersion, JsonNode json) {
    ObjectNode root = (ObjectNode) json;
    if (fromVersion <= 1) {
      root.set("itemId", root.get("productId"));
      root.remove("productId");
    }
    return root;
  }

}

そしてapplication.confJSONに変換するクラスとマイグレーションクラスを紐付けます。

lagom.serialization.json.migrations {
  "com.myservice.event.ItemAdded" = "com.myservice.event.ItemAddedMigration"
}

バージョンはアクターのメッセージのマニフェストに格納されているようです。Lagomのシリアライズ/デシリアライズの仕組みはAkkaのシリアライズ機構をうまく活用しているように見えますね。