6425


設計の指針で迷いがあるので、教えてください。

下記の要件を含むTODO管理ツールを作っているとします。

  1. ユーザは、作成したタスク（Taskと命名）に色（TaskColorと命名）を付けることができる。
  2. TaskColorは、TaskColorマスタに登録されたものから選んで、Taskに設定する。
  3. TaskColorマスタは、ユーザが自分で内容を変更できる。

TaskColorは、下記の情報を持ちます。

  * 色コード：RGBとか
  * 意味：ユーザが任意で設定できる文字列、メモのようなもの

私の認識では、エンティティか値オブジェクトかの判断を下すには、もう少し要件が必要です。

（１）TaskColorマスタの「意味」や「色コード」を変えると、全ての作成済みTaskに付けられたTaskColorにも変更が反映される場合  
各Taskに設定されたTaskColorの同一性を追跡できる必要があるため、エンティティではないかと考えています。  
また、TaskColorには識別子（TaskColorId）を付け、TaskにはTaskColorIdだけを持たせることになりそうです。

（２）TaskColorマスタを変更しても、作成済みTaskに反映しなくて良い場合  
単純にこれだけなら、Taskの永続化の際に、TaskColorの属性値も一緒に埋め込んでしまえるので、TaskColorは値オブジェクトで良さそうに思えます。  
しかし、Taskに埋め込まれるまでは、TaskColorマスタ上でそれぞれのTaskColorの同一性が確保されているような気がして、少しもやもやしています。  
とはいえ、ここで私が感じている同一性は、TaskColorの用途において同一性の確保が必要とされたものではなく、結果的に同一性があるっぽく見えているだけの幻、とも思えます。

他にもケースはあるとは思いますが、まず上記（１）と（２）について、それぞれどのように考えるのが、ドメインモデルとして正解なのでしょうか？


CC BY-SA 3.0
2015-02-12T08:30:48.720
6373
2015-02-20T04:47:51.673
2015-02-12T09:41:33.857
8078
8078
question
ドメイン駆動設計
ドメインモデルにおいて、マスタ情報はエンティティか値オブジェクトか


エリックエバンスのドメイン駆動設計(DDD)の文脈で考えると、次のようになると思います。

（１）ＩＤを持たせて、属性は変更されることを想定する

これは、エンティティですね。

DDD では、エンティティを、「参照オブジェクト(Reference Object)」とも呼んでいます。  
IDなどの識別情報を手がかりに、なんらかの情報を「参照」する場合の設計は、基本的にエンティティとして設計を検討するのが良いと思います。

この例だと、別のIDだが、同じ色や同じメモを持ってもよいかどうかとか、ユーザが認識するID（識別情報）は、何か。それから、TaskColor
自体を追加したり削除するときのID体系をどうするか、とか、バージョン管理まで必要か ... など、エンティティらしい議論がいろいろでてきそうです。

（２）変更をタスクに反映しない場合

こちらは、値オブジェクトでしょう。

考え方としては、値が同じであれば、別のオブジェクトでも同一視してよければ、それは値オブジェクトですね。  
逆に言えば、値（たとえば色）が同じでも、別モノ、という区別をしたいのなら、それは、エンティティになると思います。

ちょっと話はずれますが、(1)のエンティティの方向で設計するなら、TaskColor は、個人的には、TaskType とか TaskCategory
のほうがしっくりきます。Colorは識別の手段の一つ。識別したいことは「色」というよりは、タスクの「種類」とか「グループ」なのかなあ、ということです。

参考になれば幸いです。


CC BY-SA 3.0
2015-02-13T03:57:04.730
6425
2015-02-13T03:57:04.730
8260
6373
answer


こんにちは。

masuda220さんのおっしゃるとおりですが、私も回答を書いてみようと思います。

ストーリ的にはこんな感じですかね？

  * ユーザは、タスクを作成できる  
  

    * タスクには名前、説明、 **色** を指定できる
  

  * ユーザは、ユーザIDを指定して、複数のタスクを取得できる
  * ユーザは、タスクIDを指定して、タスクを取得できる
  * ユーザは、タスクIDを指定して、タスクを更新できる
  * ユーザは、タスクIDを指定して、タスクを破棄できる

**色** をエンティティにするか、値オブジェクトにするか？ですかね。

おっしゃるように色をタスクに従属するただの値と見るか、たとえば同じ赤色であってもこの赤色と、あの赤色は違うと見分けるのであれば、エンティティになる可能性がありますね。

（１）はエンティティですね。  
以下は、あくまで実装のイメージの一つですが、こんな感じになりますかね(実装コード自体はイメージなのであまり細かい点は考慮していないのでその前提でみてください)。TaskはTaskColorIdを保持しているので、TaskColor本体の属性がどのように変化しても識別可能ですね。  
これらは別々の集約になるので、TaskColorRepository,
TaskRepositoryのそれぞののリポジトリによって永続化可能です。もしDBに対応したリポジトリを実装するのでしたら、TASKテーブルとTASK_COLORテーブルに対応づくでしょう。

[code]

    public final class TaskColor {
      private final long id;
      private int red;
      private int green;
      private int blue;
      public TaskColor(long id, int red, int green, int blue) {
        this.id = id;
        this.red = red;
        this.green = green;
        this.blue = blue;
      }
      public final long getId() { return id; }
      public int getRed() { return red; }
      public void setRead(int red) { this.red = red; }
      public int getGreen() { return green; }
      public void setGreen(int green) { this.green = green; }
      public int getBlue() { return blue; }
      public void setBlue(int blue) { this.blue = blue; }
      @Override public boolean equals(Object obj) { /*ID値のみで等価判定*/ }
      @Override public int hashCode() { /*ID値のみでハッシュ値計算*/ }  
    }
    
    public class Task {
      private final long id;
      private final String name;
      private long taskColorId;
      public Task(long id, String name, long taskColorId) {
        this.id = id; // require not null
        this.name = name; // require not null
        this.taskColorId = taskColorId;
      }
      public find long getId() { return id; }
      public long getTaskColorId() { return taskColorId; }
      public void setTaskColorId(long taskColorId) { this.taskColorId = taskColorId; }
      @Override public boolean equals(Object obj) { /*ID値のみで等価判定*/ }
      @Override public int hashCode() { /*ID値のみでハッシュ値計算*/ }        
    }
    
    TaskColor red1 = new TaskColor(1, 255, 0, 0);
    taskColorRepository.store(red1);
    
    Task task1 = new Task(1, "task1", red1.id);
    taskRepository.store(task1);
    
    Task task = taskRepoistory.findById(1); // Task(1, "task1", red1.id)
    TaskColor taskColor = taskColorRepository.findById(task.getTaskColorId); // TaskColor(1, 255, 0, 0)
    
[/code]

（２）は値オブジェクトでよいですね。  
以下は、あくまで実装のイメージの一つですが、こんな感じになりますかね(実装コード自体はイメージなのであまり細かい点は考慮していないのでその前提でみてください)。TaskColorはTaskの集約の一部に含まれます。なので、TaskRepositoryのみなりますね。DBに対応したリポジトリであれば、TASKテーブルのみになります。この場合、カラーの値はrgbの値をTASK#COLOR_R,
COLOR_G,
COLOR_Bなどで方法で保存することになるでしょう。なので、TASK_COLORテーブルは不要ですね。(あー、まぁ、テーブル作ってもいいですが値オブジェクトは検索できないのでわざわざテーブルにするほどでもないかと)  
ということで、”TaskColorマスタ”がこのテーブルのことであれば、テーブルがあること自体がそぐわないということになると思います。

[code]

    public final class TaskColor {
      private final int red;
      private final int green;
      private final int blue;
      public TaskColor(int red, int green, int blue){
        this.red = red;
        this.green = green;
        this.blue = blue;
      }
      public int getRed() { return red; }
      public int getGreen() { return green; }
      public int getBlue() { return blue; }
      @Override public boolean equals(Object obj) { /*構成されるすべての値で等価判定*/ }
      @Override public int hashCode() { /*構成されるすべての値でハッシュ値計算*/ }  
    }
    
    public class Task {
      private final Long id;
      private TaskColor taskColor;
      public Task(Long id, TaskColor taskColor) {
        this.id = id; // require not null
        this.taskColor = taskColor;
      }
      public find Long getId() { return id; }
      public TaskColor getTaskColor() { reutrn taskColor; }
      @Override public boolean equals(Object obj) { /*ID値のみで等価判定*/ }
      @Override public int hashCode() { /*ID値のみでハッシュ値計算*/ }        
    }
    
    TaskColor red = new TaskColor(255, 0, 0);
    Task task1 = new Task(1, "task1", red);
    taskRepository.store(task1);
    
    Task task = taskRepoistory.findById(1); // Task(1, "task1", red)
    TaskColor taskColor = task.getTaskColor();
    
[/code]

TaskColorマスタという言葉が実装レイヤの言葉なので、今はドメインの話をしているので一旦忘れたほうがよいと思います。そもそもドメインモデルはどの永続化技術にも依存してはいけないので。そのモデルをどのように使いたいか、ストーリなどがから考える感じです。私は、いつもでDBなどの永続化はあとで考えています。


CC BY-SA 3.0
2015-02-19T06:30:07.293
6736
2015-02-19T06:30:07.293
5610
6373
answer


TaskColor に変更を許している以上、(1)(2) 両方ともTaskColorはEntityで良いと思います。

(1) の場合は、Task は素直に TaskColor のIDを属性として持てば良いです。

(2) の場合は、TaskColorValue という値オブジェクトがあって、Task も TaskColor も、TaskColorValue
を属性として持っている感じです。そのとき、TaskColor は「今現在利用できる色」を表現したモデルになり、その「色」を表現したものが
TaskColorValue となります。


CC BY-SA 3.0
2015-02-19T08:00:18.013
6756
2015-02-19T08:00:18.013
5997
6373
answer


Junya さんの回答に賛成です。

識別子を備え属性を持っている TaskColor はエンティティとしか言いようがないですね。その属性である「色」と「意味」は値です。TaskColor
の属性値が Task にコピーされるからといって、TaskColorが値になるわけではありません。

商品の属性値である「価格」が注文書にコピーされたからといって、商品がエンティティでなくなるわけではないですよね。同じことと思います。


CC BY-SA 3.0
2015-02-20T04:47:51.673
6817
2015-02-20T04:47:51.673
8429
6373
answer