.Net の System.Runtime.Serialization を使った json データからのデシリアライズで、ドメイン駆動設計の ValueObject を表現するのに苦労したので知見も兼ねて記事にしておこうと思います。
まず、以下のような形で json データが存在するとします。
{
"user": {
"id": 1,
"type": 2
}
}
これを単純に DataContract を使って、デシリアライズする場合は以下のようになります。
[DataContract]
public class User {
[DataMember(Name = "id")]
public int Id { get; }
[DataMember(Name = "type")]
public int Type { get; }
}
これはドメイン駆動設計でもなくただのデシリアライズですが、Type
は権限を表していて、値ごとに管理者や一般であると表示するようとすると、モデルに処理を集約したくなります(上記の場合モデル以外で変換の処理が必要になる)。
[DataContract]
public class User {
[DataMember(Name = "id")]
public int Id { get; }
[DataMember(Name = "type")]
public Type Type { get; }
}
public class Type {
public static readonly AdminUser = new Type(1);
public static readonly GeneralUser = new Type(2);
public int Value { get; };
public Type(int value) {
this.Value = value
}
public string DisplayValue {
get {
switch (this.Value)
{
case: 1
return "管理者";
case: 2
return "一般"
default:
return "一般"
}
}
}
}
そう考えると上記の様な ValueObject が欲しいのですが、次の方法ではデシリアライズに失敗します。
[DataContract]
public class User {
[DataMember(Name = "id")]
public int Id { get; }
[DataMember(Name = "type")]
public Type Type { get; }
}
System.Runtime.Serialization ではユーザ定義のクラスはコンストラクタが呼べないので、使えないようです。
そのため、以下の方法でどうにかコンストラクタを呼ぶようにします。
[DataContract]
public class User {
[DataMember(Name = "id")]
public int Id { get; }
[DataMember(Name = "type")]
private int _TypeValue;
public readonly Type Type {
get {
return new Type(_TypeValue);
}
}
}
このようにすることで、デシリアライズで呼ばれるときは int
型に代入しますが、private
なので外部からは見えなくなります。
実際に使いたいインスタンスはreadonly
で変更不可にして呼び出しています。
応急処置としては良いと思いますが、getter
でインスタンスをnew
するのはどうなんだろうと考えものです。
上記以外でいい解決方法があればいいのですが......