ドメイン駆動設計の理解度チェック?

Domain-Driven Design(DDD)の第II部は、"The Building Blocks of a Model-Driven Design" という表題で、モデリングの基本要素とその実装方法を解説している。

モデリングの基本要素


Layered Architecture で、分離独立させたドメイン層をモデリングする、基本要素は、

・Associations ( 関連は最小化する )
・Entities パターン ( 識別する )
・Value Objects パターン ( 説明する )
・Modules パターン ( 分ける。まとめる。)

・Aggregates パターン ( ブドウの房 )
・Factories パターン ( コンストラクションの分離 )
・Repositories パターン ( 情報の保存と取出し )

ひとつひとつのパターンの説明は、いまいちピンとこない内容も多い。

パターンを実際に使ってみる


DDD のII部の最後、第7章で "Using the Language" ということで、上記のパターンの、具体的な使い方を説明している。

パターン言語というのは、設計の成果そのものではない。
モデルや設計をいろいろ検討してり議論するためのボキャブラリ(道具)。

7章では、エバンスは、「貨物輸送の追跡システム」を例に、パターンを、どうやってモデリングや設計の現場の文脈で使うか、道具の使い方を具体的に書いている。

話しの流れとしては、

・貨物追跡システム、および関連する、予約システム、請求システムの概要説明。
・簡単な概念クラス図の提示。
・まずドメイン層の独立させる。(アプリケーションの関心事をシンプルにまとめる)
・次に、概念モデルのオブジェクトを Entities と Value Objects を区別する
・そして、ひとつひととの関連を丁寧に検討し、意味を明確にしていく。

・Aggregate を定義する(いつも一緒に扱いたいカタマリの発見)
・Repositories を選ぶ ( 「関連」の意味を丁寧に検討して、ほんとうに必要な Repository を発見していく)

・モデルのウォークスルー (サンプルアプリケーションの利用シナリオでモデルを検証してみる)

・オブジェクトを生成するコードの検討(識別情報の落とし穴や、Aggregate の一貫性破りのリスクの検討)
・一息入れて、モデルをリファクタリング ( Aggregate の別案の検討 )

・モジュール化の検討 (問題領域の言葉でパッケージを作る)
・関連システムとの役割分担と連携の検討
・パフォーマンスチューニングのためのモデリングの選択肢の検討

理解度チェック


7章は、DDD の前半のハイライトだと思います。

6章までに登場するパターンは、パターン別の説明を読むだけでも、いろいろ参考になるし、モデリング、設計・実装のヒントになる。
でも、ぴんとこないところも多いし、自分でも、理解があやふやと感じる。

7章は、それぞれのパターンの理解度をチェックする格好の材料だと思います。

それまでの章は、「素振り」や「壁打ち」みたいなもの。基礎テクニックの反復練習みたいなところがあって、面白みが足りない。

それに比べ、7章は、ミニゲーム形式で、実際の開発プロジェクトの感じで、パターンが登場する。興味が持てるし、個別のパターンの説明では理解できなかった点が、サンプルを読んではじめて理解できたこともたくさんあった。

もっとも、7章を読んで、かえって、わからなくなったと感じた点もあります。
また読み返すたびに、自分が勘違いしていたり、見落としていたことが、必ずでてくる。

なかなか読み応えのある本です。

ざっと斜め読みして、あとは、作業の合間に、目に付いたところだけ拾い読み、というのが、いけないでしょうけど。

ドメインロジックのパターンは、ドメインモデルだけなの?

ドメイン層の設計、実装について、考えてみた。

私たちの今のやり方


・独立したドメインモデル
・UIとの境界に、薄いサービス層
・データアクセスとの境界に、Repository クラス
という構造にしている。

ファウラーのエンタープライズ・アプリケーション・アーキテクチャ・パターン(PoEAA)の、

・ドメインモデルパターン
・サービス層パターン
・リポジトリパターン

や、エバンスの Domain-Driven Design (DDD) の

・Layered Architecture パターン ( の Application 層 )
・Repository パターン

を参考に、ドメインモデル+サービス層+Repository というやり方を実践してている。

顧客登録のアプリケーションだと、

・CustomerRegisterService クラス ( 薄いサービス層 )
・Customer クラス ( ドメインオブジェクト )
・CustomerRepository クラス ( 仮想のデータベース )

という感じのクラス構成になる。

それ以外のドメインロジックパターン


PoEAAの9章「ドメインロジックのパターン」には、ドメインモデル、サービス層以外に、2つの興味深いパターンが載っている。
トランザクションスクリプトとテーブルモジュールだ。

それぞれの用途、メリット、デメリット、ドメインモデルパターンとの比較など、いろいろ面白いネタがある。
ネットでも、そういう話を時々見かける。(最近は見かけることが少ないかな?)

ドメインモデル+薄いサービス層+Repositories を実践している私たちの観点だと、こんな感じに見える。

トランザクションスクリプト


サービス層のクラスに、ドメインの知識、ビジネスルールを集めていくのがトランザクションスクリプト。

CustomerRegisterService クラスは、そのままで、ここにビジネスの知識を書き込んでいく。

純粋なデータの入れ物クラスとして、Customer オブジェクトを使う手はあるかもしれない。
ちゃんとした(?)トランザクションスクリプトなら、必要なデータは、サービスクラス内にインスタンス変数として持つのかな?

データベースアクセスのロジックは、トランザクションスクリプト(ドメインのロジック)とは分離しておきたいので、Repositories パターンはそのまま使ってもいいのかもしれない。

そうすると、

・CustomerRegisterService クラス
・CustomerRepository クラス

の2クラス構成になる。

テーブルモジュール


Repositoryクラスに、ドメインの知識、ビジネスルールを集めていくのがテーブルモジュールパターンですね。

この場合も、データの入れ物として、ドメインオブジェクトという選択肢はありそう。
サービス層も別にいらないだろうから、テーブルモジュールの場合は、

Customer オブジェクトひとつでOK、ということか。

でも、結局は、ドメインモデル?


どのパターンでも、実アプリケーションになれば、コード量が増え、変更もいろいろ発生してくる。

で、読みやすく、扱いやすいコードを目指してリファクタリングしていくと、どうなるか?

データの入れものは欲しい


トランザクションスクリプトでも、テーブルモジュールでも、顧客を表すデータの入れ物は欲しくなる気がする。

トランザクションスクリプトクラスやテーブルモジュールクラス内に、String フィールドをずらっと並べるより、 getter/setter 付きで、データを外だししたほうが、コードがすっきりしそう。

トランザクションスクリプトの場合は、「登録サービス」以外に「変更サービス」とかでも、このデータの入れ物が使えそう。

そうすると、個々のデータごとの制約ルール(形式や値の範囲)などの業務のルールや、知識は、データの入れ物のほうに、移動したほうが良さそう。

CustomerRegisterService と、 CustomerUpdateService とで、そういう制約ルールのコードを重複させなくてすむ。

という感じで設計を改善していくと、データ入れ物クラスだったものが、ドメインオブジェクトとして成長を始める。

トランザクションスクリプトをリファクタリングしていくと、ドメインモデルパターンと似た感じがでてきそうな気がする。

データアクセスロジックの分離


トランザクションスクリプトでも、テーブルモジュールでも、

・ドメインの知識を表現している箇所
・データアクセスの手続き

は、もちろん分離すべき。

そのほうが、コードの見通しがよくなるし、変更も簡単に安全になる。
ドメインの知識に関する変更と、データベースの実装やアクセス方法に関する変更とを分離するだけでも、コードは安定するはず。

テーブルモジュールから、「データアクセスのロジック」を外だしして、ドメインのロジックに抽象化していくと、なんか、こっちもドメインモデルに似たものになりそう。

実際、私たちのドメインモデルパターンでも、

・Customer オブジェクト

・CustomerList オブジェクト

を使っている。

CustomerList オブジェクトは、Customer オブジェクトのコレクションをラッピングしたクラス。

業務の視点で、顧客一覧について、やりたいことをメソッドとして宣言する。

テーブルモジュールから、データアクセスのロジックを除外したものと、 私たちの CustomerList オブジェクトは、たぶん、同じものになる。

ドメインモデルだけじゃないけど、結局は、ドメインモデル?


自分たちがドメインモデルでいろいろやっているからかもしれない。
でも、トランザクションスクリプトも、テーブルモジュールも、

・関心事の分離
・カプセル化
・変更の影響範囲の局所化

とか、の設計原則を追いかけてリファクタリングしていくと、

・関連するデータ
・関連するロジック

を、うまく凝集した「ドメインオブジェクト」が増え、ドメインモデルが成長していく気がする。

パターンというのは、設計の出発点ではなく、いろいろな試行錯誤や改善の結果。

最初は、トランザクションスクリプトやテーブルモジュールで、ドメインロジックを表現していても、ソフトウェアを改良しながら育てていくと、結局、ドメインモデル的なものになる気がする。

まあ、自分たちが、ドメインロジックの表現パターンとして、ドメインモデルを選択した時点で、そちらに振り切っているわけだから、こういう結論になっちゃうんでしょうけど。

ドメインオブジェクトとテーブルのマッピング

Repositories パターンは、ドメインモデルを、純粋にドメインの関心事だけに保つモデリングテクニック。
ドメインオブジェクトのモデリング、設計・実装は、シンプルで、わかりやすくなる。

でも、永続化の実装がシンプルになるわけではない。
特に、ドメインオブジェクトとテーブルのマッピングは、悩みどころですよね。

マッピング自体は、iBatis とか、O-R マッピングのフレームワークを使えば、それなりに実装できる。
でも、ドメインモデル(オブジェクトモデル)とデータモデルの、モデルの不一致は O-R マッピングツールが解決してくれるわけではない。

既存データベースと無理やりマッピング


Repositories パターンを使い始めたころは、既存のデータベースを使うアプリケーションだった。
ドメインモデルとデータモデルが、かなりギャップがあったけど、iBatis SqlMap でがんばった。

「既存データベース」と無理やりあわせる、この状況は、現実には、多いですよね。

Repositories パターン、O-R マッピングフレームワークは、まあ、こういうギャップを埋める技術、と素直に考えていた。

データベースも新規設計


データベースも含めて、新規に設計するプロジェクトがはじまった。

さて、ドメインモデルとデータモデルはどうする?

A案:できるだけ一致させる
B案:ドメインモデルはドメインモデル、データモデルはデータモデル

最初はA案


ドメインオブジェクトといっても、フィールド数が多く、画面をそのままオブジェクトにしたようなモデルだった。

だから、
・画面(のブロック)
・ドメインオブジェクト
・テーブル
は、ほぼ同じサイズで、同じ構造で設計・実装した。

・顧客詳細画面
・Customer オブジェクト(単独の大きなオブジェクト)
・Customer テーブル

ドメインオブジェクトは小さく、テーブルも小さく


Domain-Driven Design を読み始めた影響もあって、オブジェクトを小さくする方向に転換した。

ひとつの大きな Customer オブジェクトを、Entity と Value Object に分解。
もちろん、Aggregate にして、「いつも一緒」に扱うようにした。

このとき、テーブルもそれなりに分解することにした。

厳密に粒度の一致を重視したわけではないけど、ドメインモデルで、オブジェクトを別にするのは、業務の考え方がそうなっているから。 だったら、テーブルもそういうサイズで扱うのが正しいはず。

自然に、テーブルも、小型のオブジェクトに対応させる方向になった。
(よく考えて、そうやったわけではない)

やってみたけど...


テーブルを小さくしてみたけど、あまり良いことはなかった。

逆に、問題がいろいろでてきた。

SQL文で、10以上もテーブルを JOIN するとか、ひとつの Aggregate のために、複数の SQL 問い合わせを実行するとか、コードが込み入ってきた。

ドメインオブジェクトの実装コードは、オブジェクトを小さくわけたほうが、可読性や、変更性がよくなることを実感。
でも、テーブルは、小さくわけても、メリットは何も感じなかった。

テーブルの意図がわかりにくく、コードが複雑で、変更がたいへんになっただけ。

マーチンファウラーの「エンタープライズアプリケーションアーキテクチャパターン(PoEA)」を読み直したら、

「小さなオブジェクトにテーブルを一致させるなんてばかげたことやるやつはいない」というようなことが書いてあった。

なんだ、そうなんだ。

テーブルは Entity 単位に


その後は、だいたい、Entity と 関連する Value Objects をひとつのテーブルにしている。
Aggregate に複数の Entity があれば、Entity ごとのテーブルにする。

とは書いてみたが...


実際は、そんなにちゃんとモデリング、設計・実装ができてるわけじゃないな。

・Entities パターン
・Value Objects パターン
・Aggregates パターン

いちおう、意識はしているけど、なんとなく、

・オブジェクトは小さい単位に
・テーブルはもうちょっとまとまった単位に

という「感覚」でやっているのが実情ですね。

たぶん、「テーブルは、こんな感じのまとまりで」と考えているところを、ちゃんとやるのが、Aggregates パターンなんだと思う。

Repositories パターン : データベースの実装は忘れようね

エンタープライズアプリケーションを構築するとき、データベースは中心課題のひとつ。
大量のデータを効率的に保存し、取り出す仕組みが必須。

実装やテストで、かなりの時間は、データベースを意識し、依存した作業になる。

ドメイン駆動設計的には、そういう永続化の「実装」の関心事と、ドメイン(問題領域)の関心事を分離することを重視する。
そのためのモデリングや設計のテクニックが Repositories パターン。

そうはいっても、実装しなきゃ


実際のプロジェクトでは、もちろん、動くソフトウェアを作らなきゃ話しにならない。

データモデリングして、テーブル定義して、テストデータ投入して、SQL文書いて、デバッグして、チューニング。
バックアップとリカバリも考えなきゃいかんし、テーブル設計が、いろいろ変更が続くし、データベースまわりの作業が多いし、たいへん。

小さい規模でも、エンタープライズアプリケーションであれば、データベースは、中心課題のひとつですね。

実装技術としても、O-R マッピングとか、DAO とか、フレームワーク、アーキテクチャパターン、デザインパターンとか、ネタは山ほどある。
リレーショナルデータベースかオブジェクトデータベースか、XMLデータベースか、なんてことまでネタになったりする。

ツールも、いろいろ便利なものがでてくるようになった。
データモデリングツールから、データベースの変更管理ツールとか、テストデータの管理ツールとか、使いこなせば、便利そうなものがいろいろあるみたい。

かといって、実プロジェクトで、なんでもかんでも、トライアルモード、研究モードというわけにもいけない。

結局、自分たちなりにある程度、実用的な実装方法を見つけると、そこに定住しがち。

私たちの場合は、3年くらい前に、StringBuilder でSQLを組立と JDBC API をそのまま使う方式から、Spring + iBatis に切り替えたのが大きな転換だった。
それ以降は、フレームワークのバージョンアップとか、より良い使い方とか、少しは変化があるけど、基本は3年間同じ。

最近、データベースを継続的に進化させるというか、データモデルの変更を、自動的に安全に反映したり、テストデータのセットを管理したり、ということに取り組みはじめた。
ここらへんのツールややり方に習熟してくると、来年の今頃には、データベースまわりの作業のやり方や設計の考え方がちょっと(だいぶ?)変わっているかもしれない。

実装技術の話しのネタはつきないが....


ほんと、データベースまわりの実装技術のネタはつきません。

だからこそ、Repositories パターンなんですね。

「実装技術」を忘れて、「ドメイン」の関心事だけにフォーカスして、永続化をモデリングしましょう、ということ。

Repository クラスは、メモリ上のコレクションみたいに見えるようにする。

内部に

Map<Entity, Set<ドメインオブジェクト>>

があって、その Map にアクセスするメソッド群を提供するイメージ。

永続化の実体は、iBatis + PostgreSQL かもしれないし、ファイルでも、XML データベースでもよいかもしれない。
ドメインモデリングしているときは、そういうことは、全て忘れて、ドメインモデルを洗練することに集中するためのテクニックが、Repositories パターンというわけだ。

Aggregateを表現する手段


Repository は、Aggregate に対で作成する。

Aggregate 自体は、実装方法がなく、絵と決め事だけの、かなり脆弱な存在。

でも、Repository は、りっぱ(?)な、実装クラス。
Aggregate ( = These objects go togegher as a whole. )というコンセプトを実現する手段になる。

つまり、

Map<Entity(ID), Aggregate>

が、Repository なんですね。

これ以外のデータベースアクセスを禁止することで、Aggregate 内部のオブジェクトを直接参照したり、取り出すことを制限するわけです。

Aggregate というドメインモデリングの概念的な存在を、形にするのが、Repository の大きな役割。

ドメインの情報の利用方法を表現する手段


Repository に用意するメソッドは、ドメインの関心事の表現手段の一つ。

findByID( id )
findByName( name )
matching( criteria )
...

こういう感じで、業務で、「こうやって情報を取り出したい」というニーズを、Repository のメソッドとして明らかにしていく。

ドメイン駆動設計の基本コンセプトは、「ドメインそのもののモデリング、表現にフォーカス」すること。
そのテクニック、ノウハウのひとつが、Repository パターン。

データベース(永続化)という大きな実装課題と、ドメイン駆動設計との接点となるクラス。
もちろん、実装側の詳細を隠蔽し、疎結合にすることが、たいせつ。

ドメインのモデリングに集中し、ドメインモデルを洗練するために。

Factories パターン:コンストラクションは分離せよ

ドメイン駆動設計の Factories パターンも、大きな発想の転換だった。

ドメインオブジェクトのコンストラクションは、別のオブジェクトの責務にするのが良い。
ちょっとびっくりしたけど、説明を読むと、なるほど、と納得。

空っぽのオブジェクトと setterで作る


ドメイン駆動設計に出会うまでのやり方がこれ。

Customer customer = new Customer();

customer.setName( name );
customer.setContactInfo( contactInfo );
...

という感じで、

・デフォルトのコンストラクタで、空っぽのオブジェクトをつくる
・後から、中身を setter で埋めていく

というのがあたりまえだと思っていた。

(サンプルプログラムって、ほとんんど、そうなっていません?)

初期値はコンストラクタでセットすべし


ドメイン駆動設計を実践しはじめてから、この方針に変更。

オブジェクトの状態を変えるというのは、あまりよいことではない。
Value Objects パターンの問題意識ですね。
setter で後から状態を変えるのは、ソフトウェアを脆弱にする。

というわけで、

Customer customer = new Customer( name, contactInfo );

というパラメータ付きのコンストラクション方式に変更。

このコンストラクタは、Customer の必須情報を宣言しているというドメイン知識の表現にもなっている。

もっとも、フレームワークを使う関係で、

・デフォルトのコンストラクタ
・setter 群

は、書く必要はある。

こいつらは、ドメインの知識ではなく、実装だけの都合なので、クラスの下のほうにひっそりと書くようにした。

「コンストラクタ」というグループで並べたくなるけど、ドメインの知識の表現、という価値観で、

・最初に、パラメータ付きのコンストラクタを宣言
・その後に、ドメインの知識をあらわすフィールドを並べる
・最後に、フレームワークでしか使わない デフォルトのコンストラクタと getter/setter 群

というレイアウトに変えた。
実装だけの関心事は、できるだけ下の方に記述する。

けっこうドメイン駆動設計っぽいレイアウトで気に入っている。

コンストラクションのロジックの意味と置き場所の検討


ドメイン駆動設計をなんどか読み返しているうちに、Factories パターンの意図を誤解していたことが発覚。

Factory クラスは、使っていたけど、

・データベースからシーケンスで識別番号を取得する
・複雑な Aggregate のオブジェクト群を「いつも一緒」に生成する

程度にしか使っていなかった。

ところが、読み返してみると、エバンスは、コンストラクションは、ドメインオブジェクトの責務としてふさわしくない、といっている。

DDD では、車のエンジンを例にとって説明している。(Aggregates パターンのイメージ写真も、自動車の組立風景)

エンジンが完成した後は、エンジンの責務は、シャフトを回転させること。

このエンジンオブジェクトが、組立の知識を持っているのは変でしょ? という話し。

・ピストンにシリンダを組み込んで、
・バルブをつけて
・点火プラグをつけて、配線して
・空冷用のファンとベルトをつける
...

エンジンというルート Entity のコンストラクタに、こういう自分自身を組み立てる知識を持たせ、責任を負わせるのは、良くない、という考え方。

本来の責務ではなく、余計な知識でドメインオブジェクトを汚染(?)している。

エンジンオブジェクトを生み出すための知識・責務は、別のオブジェクトに持たせて、エンジンオブジェクトは、シャフトを回す自分の本来の責任だけに専念すべき。

これは、けっこう、目から鱗だった。

たしかに、コンストラクションの内容は、ドメインオブジェクトの本来の責務ではないことが多い。
言語仕様とか実装技術の関心事が、ドメインオブジェクトに混入している。

ドメインオブジェクトを、ドメインの本来の関心事だけに単純化することで、より洗練されたドメインモデルに進化する。

この実装の関心事のコンストラクションを別オブジェクトの責任として外だしするのが、Factories パターン。
ドメインモデルをクリーンに保つ空気清浄機が、Factory オブジェクトというわけですね。

というわけで、顧客オブジェクト生成の最終形は、

Customer customer = CustomerFactory.createCustomer( name, contactInfo );

となる。

ドメインモデルではないけど、ドメイン層の住人


Factory クラスは、ドメインモデルの一部ではない。
でも、ドメインモデルを直接支える役割として、ドメイン層の住人とするのが良いらしい。

インフラ層ではない、という消去法だと、確かにドメイン層である。
でも、Factory という名前も含めて、ちょっと違和感があるなあ。

まあ、とりあえず、CustomerFactory としておきましょう。

新規作成以外の用途


Factory は、純粋な新規作成用とは限らない。

・既存のオブジェクトを元にして、類似オブジェクトを生成(コピーして作成、というやつ)
・データベースやXMLファイルで、ドメインオブジェクトとは異なる構造で永続化されたデータから再生。

後者は、O-R マッピングや O-X マッピングのフレームワークを使えば、実際に書くコード量は少ない。
その場合は、Factory クラスは、そういうマッピングをカプセル化して隠蔽する役割。

グローバルなID、ローカルなID


Factory の大事な役割が、管理番号などの発番機能のカプセル化。

・ルートのEntity には、グローバルに一意な識別番号を発行する。(受注ヘッダーの受注番号)
・Aggregate 内部の Entity には、ローカルの識別番号を発行する。(明細の行番号)

ここらへんは、ビジネス知識の一部という感じもする。ドメインモデルの一部?

発番のロジックが、単なる連番とかじゃなくて、業務の知識が必要な場合は、その知識は、Factory ではなく、別のドメインオブジェクトとして明示すべきでしょうね。

初期登録のルールや手順が複雑な場合


BtoB だと、顧客の新規登録には、いろいろルールや手順が必要なことが多い。
与信調査とか、与信枠の設定とか。

こういうルールや手順は、Factory では、別途 Service オブジェクトとかで、ドメインの知識として、明確に表現すべきなんでしょうね。

まあ、現場では、Aggregate ( いつも一緒のオブジェクトたち ) を生成するとき

・アプリケーションService クラスに手続き的に書く
・Aggregate のルート Entity に書く

などがおきがち。

ここらへんは、格好のリファクタリングのテーマですね。

・ドメインの知識であれば、ドメインオブジェクトに
・実装だけの関心事・制限ならば、Factory クラスに

という設計改善をしながら、ドメインモデルを、洗練させていくためのテクニックが、Factories パターンというわけですね。

ドメイン駆動設計:Aggregates パターン <ブドウの房>

Domain-Driven Design(DDD)の第6章は "The Life Cycle of a Domain Ojbect"。

これだと、一つのオブジェクトのライフサイクルをテーマにしているようだけど、実際は、いくつかのオブジェクトをいっしょに扱うことが、ほんとうのテーマ。

小さなオブジェクトをたくさん設計・実装すると、

○ひとつ、ひとつのオブジェクトの役割が単純でわかりやすい
○変更が、局所化されるので、変更が安全になる

でも

×たくさんのオブジェクトを扱うので、全体の見通しが悪い
×コード量やファイル量が増える( public class がたくさんできる )

という点は課題。

特に、全体の見通しが悪く、オブジェクトをひとつひとつ、ばらばらに扱っていたのでは、わけがわからなくなってくる。

Aggregates パターン


この課題の解決パターンが、 Aggregate ( 集約 ) というわけだ。

UML記法なら、関連に白ダイヤモンドを追加するやつですね。

白ダイアモンドの有無で、どう実装が変わるか?
これが6章のテーマともいえる。

イメージトレーニング?


Aggregateのイメージは、DDD の Aggregates パターンのイメージ写真「ぶどうのひとふさ」がぴったり。

ひとつひとつのオブジェクト(粒)は、スイカやパイナップルの大きさじゃない。
リンゴやみかんよりも、ずっと小さい。

でも、けしの実ほど、小さいわけではない。

小さいけど、それなりにひとつひとつの粒を楽しめるののがブドウというわけだ。
(まあ、巨峰とデラウェアだとだいぶ大きさが違うんだけどね)

Aggregates パターンとは、ブドウの房のように、小さいけどおいしい粒が集まって一塊にすること。

※タネなしが Value Object で、タネありが、Entity か ?

キャッチフレーズ?


Aggregate とは、

"These Objects go together as a whole."

ということ。

オブジェクトが群れをなして、しかも「一体(as a whole)」で扱うパターンが、Aggregate ( 集約 )。

モデル上の表現


UML の集約記号(白ダイヤモンド)で、いちおう表現できるけど、直感的にわかりにくいんですよね。

エバンスが、DDD の中で使っているのが、クラス図に手書きで、Aggregate の範囲を囲むこと。

この線から内側がひとかたまり。
線の外側のオブジェクトとは「一線を画す」わけだ。

モデリング上の意味


「いつも一緒か?」をいろいろ考えるのが、Aggregates パターンの目的。

ほんとうに、このオブジェクトたちは、いつも一緒なのか?
利用シーンによっては、こっちのオブジェクトは、独立して参照するんじゃないか?

こういうことを、ドメインの言葉に耳を傾けながら、いろいろ議論するわけだ。

DDDで使っている例


DDD で例をあげているのは、

・受注ヘッダーと受注明細は、いつも一緒。
・商品オブジェクトは、参照可能だけど、受注明細といつも一緒ではない。

というモデル。

「いつも一緒」というのは、いつもオブジェクトを同時に作るか?と考えるとわかりやすかもしれない。

たとえば、リポジトリから受注記録を取り出すとき、

A. 受注ヘッダーオブジェクトと、明細オブジェクトを生成。
B. 受注ヘッダー、明細、さらに注文しているすべての商品オブジェクトを生成。

普通は A ですよね。

商品オブジェクトは、単独で参照したり、追加や変更をすることが多い。
だから、受注の Aggregate ( 集約 ) の中に含めるべきではない、という議論。

こういう「いつも一緒の範囲」をネタに、いろいろ絵にして、話しをすると、問題領域(ドメイン)のほんとうの構造とか、深い知識が発見できる。
それが、Aggregates パターンというモデリング手法なわけだ。

実装?


Aggregate の実装については、Java なんかだと、言語側に Aggregate 宣言できるメカニズムとかわるわけではない。
他の言語でもそうかな。

強いて言えば、SQL 文の JOIN 句が、イメージ的には近いかな。

エバンスは本の中で、「Aggregateを宣言する適切なフレームワークがあれば使ったほうがよい」みたいなこと書いているけど、そんなフレームワークがあるのかしら?

実際、エバンスも続けて、「そういう技術が利用できない場合」として、かなり、長々と、実装上のルールを書いている。
チーム内の決め事でがんばるしかなそうな感じ。

そのルールを要約すると、

・ひとつのルートEntity だけが、外部から参照の保持が可能
・Entity は、グローバルの識別IDが必要。(内部オブジェクトのローカルに一意であればOK)
・データベースアクセスは、ルートエンティティをキーに一体でやる(個別のオブジェクト単位のデータアクセスは不可)
・削除はいつも一緒
・内部のオブジェクトを変更する場合、ルートEntityが整合性・一貫性維持に責任を持つ

という感じ。

Aggregate は概念モデル、実装は Factory と Repository で


結局、私たちがどうやっているかというと、

・モデル上で、Aggregate の範囲を検討する。白ダイアモンドと手書きの囲み線の併用。
・実装は、Factory クラス、Repository クラス。

という感じでやっている。
(というか、まだまだ Aggregates パターンごっこのレベルですけど)

Factories パターン、Repository パターンについては、別の記事にします。

ドメインオブジェクトの複雑な参照関係を扱うパターン

ドメイン駆動設計では、ドメインのオブジェクトを、

・シンプルに
・小型に
・役割を単純に

することが良い設計。

オブジェクト間の関連は、最小化の原則、つまり、できるだけ単純な関連がよい設計。

このモデルを、メモリ上で生成し、短時間で消滅させるなら、話しは比較的簡単。
コンストラクタとガベッジコレクタが、やっかいな仕事の多くをやってくれる。

やっかいなのは、エンタープライズアプリケーションの「大量のデータの記録(永続化)と参照」という課題。

ドメインオブジェクトの複雑なWeb


ビジネスのデータは、単独では存在していない。
必ず、他のデータとの絡み合い、参照関係の中で意味を持つ存在。

ドメインモデリングで、関連を本質的なものだけに、いくら単純にしても、それなりに複雑なオブジェクトのネットワークになる。

ましてや、永続化した過去のデータも含めた参照関係を維持するドメインオブジェクトのネットワークは、複雑で巨大になる。
長い長い参照パスをたどる必要がある。

こういう複雑なドメインオブジェクトのネットワークで、一貫性を保ち、整合性を維持するのは、かなりたいへん。

ドメイン駆動設計は、たくさんの「小型のオブジェクト」を良い設計としている。

そういう大量の小型オブジェクトを安全にあつかうにはどうする?

三つの基本パターン


Domain-Driven Design (DDD)の第6章 "The Life Cycle of a Domain Object" では、この「巨大で複雑なドメインオブジェクトのネットワーク」を扱うために、

・Aggregates パターン ( 集約 )
・Factories パターン ( 生成 )
・Repositories パターン ( 保管 )

の三つのパターンを提唱している。

正直いって、この三つのモデリングパターンは、最初に Domain-Driven Design を読んだときには、ぴんとこなかった。

今になって考えると、原因ははっきりしている。

最初に読んだ頃


私たちのモデリング、設計・実装は、すごく大きなオブジェクトとテーブルを使っていた。

顧客オブジェクトは、フィールド数で、20や30はあたりまえだった。
顧客テーブルも、ゆうに100カラムを超えていた。

関心事がぜんぜん分離できていなかった。

こういう巨大なクラスがあたりまえだと思っていた。

そうすると、

・Aggregate
・Factory
・Repository

は、目的や使い道が理解できない。

最初から一つのクラスに集約しちゃっているし、コンストラクタがあるし、永続化は O-R マッピングで、...。

こういうモデリングパターンが必要なイメージがわかないのも当たりまえですね。

ぴんとくるようになった


ドメイン駆動設計とか、いろいろやりはじめて、「シンプルで小型のオブジェクト」が、だんだん、あたりまえになってきた。

そうすると、永続化がらみのデータアクセスのコードとか、初期化のコードが膨らんできた。

書くのがたいへん。
読むのもたいへん。
変更するのはもっとたいへん。
...

そしたら、自然に、Aggregate を設計して、 Factory や Repository を導入するようになった。
そのほうが、分かりやすいし、楽になるのは、すぐわかったから。

まあ、いまでも、Aggregate の設計がうまくできているとはいえないけど、Aggregate の設計は大切、ということは実感している。

しばらく、この 三つのパターンについて、書こうと思う。

DDDの Entities パターンと Value Objects パターンを理解する

ドメイン駆動設計の基本が、Entities パターンとValue Objects パターン。

本を読んでも、なぜ、これが基本になっているのか、最初はピンとこなかった。

私はこういうふうに理解した、という話し。

name-value ペア


ソフトウェアの世界では、name-value ペアをいろいろ使う。

NAME=値

の形式。 "=" は、タブのこともあれば、":" のこともある。

XML が一般的になる前は、設定ファイルといえば、これ一辺倒だった。
HTTPのリクエストとか、SMTP のメールヘッダーとかもそう。


Amazon の SimpleDB なんかもそうですね。

Java の Map は、 key-value ペアを扱うコレクション。これも、name-value ペアといっしょですね。

こういう情報の扱い方は、なじみが深いし、なにも考えずに使っていますよね。

これが、そのまま、 Entity と Value Object の関係なんです。

Name : Value
Key  : Value
 ↓   ↓
Entity : Value Object

Entity が識別情報で、Value Object が、説明情報という感じが、なんとなくわかりますか?

・name や key 役のオブジェクトが、Entity (識別オブジェクト)。
・value 役のオブジェクトが、Value Object (説明オブジェクト)。

・参照のキーになるのが、Entity。
・参照される内容が、 Value Object。

で、"=" とか ":" が、オブジェクトの「関連」になる。

name-value ペアよりは、ちょっと複雑になるけど、考え方や役割はいっしょ。

Entity は、name, key など、内容を参照するための情報
Value Object は、内容を説明する値。

Entity の空間 ( name や key の空間 ) と、Value Object の空間 ( value の空間 ) を、区別して、モデリング、設計・実装しましょう、ということ。

これが、ドメイン駆動設計の基本、Entities パターンと Value Object パターンなんですね。

Java 風に記述すると、

Map<Entity,Set<Value Object>> ドメインオブジェクト空間;

という感じ。

わかりづらいか...

データモデリング


データモデリングの考え方も、実は同じ構造。

データモデリング、データ項目を、識別子 と 属性 に分けて考える。

情報(データのカタマリ)を扱う場合に、

・識別に使う情報
・内容を表す情報

に分けて考える。

DDD の Entities パターン と Value Object パターンの議論も、これとまったくいっしょの考え方ですね。

DDD の説明だと、正直ぴんとこなかったけど、name-value ペアとか、データモデリングの 識別子/属性 と同じなんだと気がつけば、理解も簡単でしょう。

サロゲート(代理)キーではなくナチュラル(自然)キー


注意点。

データモデリングの識別子と、テーブル設計の主キー設計、それも、シーケンスなどを使った、人工的な サロゲートキーの視点を明確にわけること。

ドメイン駆動設計やデータモデリングの「識別」は、ビジネスの現場で使われている情報の話し。
つまり、ナチュラルキーが、関心事。

name-value ペアの name や、 Map の key も、できるだけ、ビジネスで意味のある言葉にするのが、ドメイン駆動設計。

ドメイン駆動設計の視点と、実装技術の視点は、意識して使い分けましょう。

ドメイン駆動設計 :どこまで実践できている?

Domain-Driven Design(DDD)の5章は、

・関連
・クラス
・パッケージ

というクラスモデリングの基本テクニックの話し。

本で解説しているパターンは、

・Associations ( 関連の最小化の原則 )
・Entities パターン
・Value Objects パターン
・Services パターン
・Modules ( Packages ) パターン

の5つ。
ここ、2週間くらい、この日記でもいろいろ書いてきた。

5章のまとめ的な意味で、自分たちの実践状況の振り返り。


ユビキタス言語とモデル駆動設計


DDD に取り組むには、まず、

・ユビキタス言語(コードにも、業務の言葉を使え!)
・モデル駆動設計(コードの前に、モデリング)

への発想の転換が必要。

ユビキタス言語については、とにかく、コードも、日報も、業務の言葉を使おう運動で、なんとか、意識改革。

モデル駆動設計は、ICONIX プロセスに取り組んだのが、うまくいった。
ドメインモデル、クラス図を描いてレビューすることが日常の作業になった。

ここまでが、一番たいへんだったのかもしれない。
ドメイン駆動設計の入り口に、ようやく、たどりついた、という感じかな。

それでは、基本テクニック(パターン)の実践の振り返り。

Assosiations ( 関連の最小化の原則 )


ドメイン駆動設計の考え方の通り、重要な関連だけに限定してモデリングする、ことができるようになってきた。

とにかくに関連を表す線を引きまくることはなくなった。
関連が一本もないクラス図も見なくなった。

いちおう、モデル上の関連と、Java の参照や SQL という実装とも連動するようになってきているかな。

課題としては、まだまだ関連の使い方が「浅い」。

ドメインの知識を収集し、深く理解するための分析手段として、モデリングの要素「関連」を活用できているか?
そこまではいっていない感じ。

でも、関連の有無、方向性、多重度などはレビューで話題になるので、レベルアップが期待できそう。

Entities パターン (識別オブジェクト)


これは、たぶん、ぴんときていない。

・オブジェクトをシンプルに小型に
・オブジェクトの役割を単純に

ということはできている。

結果的に、Entity オブジェクトらしきものを発見・設計・実装はしている。

でも、「識別」という視点でのモデリングを意識的にやっているレベルではない。

まだ、「識別」というと、データベースの主キーや一意制約とか、オブジェクトIDとかと理解がごっちゃ。

ビジネス上の意味を持たない、サロゲートキー(代替キー)という実装テクニックの視点が強すぎる。

ドメイン駆動設計の Entities パターンは、ビジネスの現場で意味を持っている、ナチュラルキー(自然キー)に注目すること。

業務のモデリングをする時に「ビジネスの現場でキーにしている情報」というドメイン駆動の視点を持つように、レビューとかで指摘を続けようと思う。

Value Objects パターン (説明オブジェクト)


これは、初心者向けに、

・オブジェクトは小さくする
・できるだけ不変にする

というルールでやっているので、それっぽいモデリングと設計・実装になってきた。
前にあげた、BusinessDate などが、うまくやっている例。

時間とともに、 役に立つ、気の利いた、 Value Object を増やしていけそう。

Services パターン


意識的に、このパターンを使った、モデリングはできていないなあ。

たぶん、ドメインモデルのレイヤ化とかで Policy レベルのオブジェクトのモデリングとかを意識できるようになると、自然に、Services パターンがぴんとくるようになると思う。

ドメインモデルのレイヤ化は、いまは、初心者向けの「ごっこ」の状態。

とりあえず、レイヤ別に

・Decision
・Policy
・Operation
・Resource

というパッケージを作っている。
(こういうテクニカルなパッケージ分割は、アンチパターンなんですけど)

Modules パターン


パッケージ図は、前よりは使うようになったかな。

・パッケージを小さく保つ (クラスは多くても10個程度)
・依存関係に気にする

ことが、少しできはじめたレベル。

日常の作業の中で、パッケージ図に整理してみたら、全体がわかりやすくなった、という体験をする機会が少しでてきた。
パッケージを使う動機付けとスキルのレベルアップは、なんとかなりそう。

総括


Domain-Driven Design 5章までの、ドメイン駆動設計の基本事項については、

・いちおう、実践するようになった
・もっと、意識的に使えるレベルを目指す
・部分的だけど、自律的に発展できそうな感じはでてきた。

3年くらいかかって、こんな感じ。

・朝から晩まで、エディタでコードと格闘
・視点も用語も、実装技術一辺倒

こういうチームが3年でここまで変わったんだから、けっこうすごいことかもしれない。

ドメイン駆動設計の基本テクニック : パッケージ

ドメイン駆動設計(DDD)の基本テクニックの最後が、Modules (パッケージ) パターン。

Domain-Driven Desing では、Modules という言葉を強調しているけど、Java で実装するときは、パッケージなので、主に、パッケージという言葉を使います。

パッケージ図でわかるモデリングスキル


パッケージ図の使い方を見ると、その人のモデリングの経験やスキルが分かる。

パッケージ図を使わない人:モデリングの初級者
パッケージ図を使う人:中級

パッケージ図で、全体構造をさらっと描ける人:上級

「パッケージ」は、ものごとを整理する手軽で便利な手段。

二つの使い道がある。

a. 詳細を隠して、全体を示す (森を見る手段)
b. 特定の範囲に限定して、そこの詳細だけを示す (木を見る手段)

全体を示すときには、パッケージ間の依存関係も大切なモデリングの要素。

◎凝集度を高める
◎疎結合にする

この設計原則を理解して実践できている人は、パッケージ図の描き方がうまい。

パッケージ図を使わない人、うまく描けない人は、凝集度や結合度が、怪しい設計をしがち。

ドメイン駆動設計でのパッケージ


用途は二つ。

Isolating Domain ( ドメイン層の分離 )の設計


レイヤー構造にして、ドメイン層を独立分離するためのアーキテクチャのモデルは、パッケージ図で描く。
大切なのは、依存関係のモデリング、議論。

ドメインは、他のパッケージ( UI や 基盤 ) から、参照される。他のパッケージは、ドメインに依存している。
ドメインは、他のパッケージに依存しない、独立した存在。

こういう依存関係を、特定の方向と関係に限定することが、よいアーキテクチャの基本ですね。
(関係の最小化の原則は、ここでも重要)

もちろん、モデリングだけでなく、実装レベルでも、この依存関係の限定は、徹底すること。

エディタで、コードだけで考えている、簡単に別のクラスを参照して、密結合にして、参照関係を複雑にしがち。

いつも、パッケージ間の依存関係の最小化原則を守ることが大切。

ドメインモデルのパッケージモデリング


ドメイン駆動設計のモデリングの基本テクニックとして、オブジェクトは、シンプルで小型になる。

・Entities パターン (モノゴトの識別に焦点をあてたオブジェクト群)
・Value Objects パターン (モノゴトの説明に焦点をあてたオブジェクト群)
・Services パターン ( モノゴト間にまたがる関係や知識に焦点をあてたオブジェクト群)

ひとつひとつのオブジェクトは、役割が単純でわかりやすくなる。
しかし、オブジェクトの数が増えるので、全体の見通しが悪くなる。

パッケージ図は、この見通しをよくする基本テクニックであり、最良の手段。

ドメイン駆動設計的なパッケージ図の使い方


単純に、たくさんのオブジェクトを分類・整理する入れ物としても、パッケージは有用。

ドメイン駆動設計では、さらに、積極的に本格的に、このパッケージ図を使って、モジュール化することを提唱している。

ユビキタス言語


まずは、パッケージの名前。
もちろん、業務の専門家の概念(用語)を、素直に表現するパッケージ名にする。

・顧客パッケージ
・出荷パッケージ
・請求パッケージ
...

業務の大きなかたまりを、パッケージで表現する。

クラス図を前に、うんうん唸りながら、パッケージを考える必要はありません。
業務の専門家と話しをして、注意深く耳を傾ければ、パッケージの単位は、自然に明らかになってきます。

図にして示すと、違和感があれば、簡単に指摘してもらえるので、正確な理解に早くたどりつける。

依存関係


パッケージ図の依存関係も、ドメイン駆動設計の重要なテクニック。

ドメインオブジェクトが増えてくると、依存関係はだんだん膨らんでくる。

パッケージの単位の依存関係で、まず、基本的な構造を検討する。

そして、その依存関係の実体を、それぞれのパッケージ内のオブジェクト間の関連として、さらにモデリング。

もちろん、パッケージの依存関係も、オブジェクトの関連も、最小化の原則。

ドメインモデル内でも

・凝集度を高く
・疎結合

を追求する。

継続的なリファクタリング


もちろん、パッケージ図もリファクタリングの対象。

最初のパッケージ図がきれいに描けているわけがない。(業務の基本構造が最初からわかれば苦労しない)

だから、業務の知識を広げながら、深めながら、パッケージ図もリファクタリングしていく。

・名前の変更
・パッケージ間のオブジェクトの移動
・パッケージの抽出
・パッケージの依存関係の変更
...

こうやって、改良されたパッケージ図は、業務の全体像を、正しく、わかりやすく表現したモデルに成長する。

パッケージ図のアンチパターン


ドメインモデルで、パッケージを使うときのありがちなアンチパターン。

画面単位のパッケージ分け


Smart UI アンチパターンのの別バージョンですね。
ドメインモデルの構造は、画面単位の構造とは別になるのが自然。

担当者別のパッケージング


担当者別は、業務の構造とは無関係ですね。
ドメイン駆動設計では、いつも、「業務の概念」を表現することにこだわる。

テクニカルなパッケージング


エバンスが、Domain-Driven Design の中でも説明しているアンチパターン。

ドメイン駆動設計の基本は、オブジェクトを、

・Entities
・Value Objects
・Services

に分解すること。

で、パッケージも、

・Entities パッケージ
・Value Objects パッケージ
・Services パッケージ

に分ける。

パッケージは、業務の視点で、業務の用語で、構成しましょう。
DDD のテクニックで分けることに、意味はありません。

DDD の用語を使っても、ちっともドメイン駆動設計になっていない。

最初に作ったパッケージのまま


パッケージは、名前も、構造も、最初に作ったまま。
ありがちなアンチパターンですね。

最初に、業務の構造を知りつくして、よいパッケージ図を描くことはできません。

パッケージも丹念に、リファクタリングして、改良していきましょう。

calendar
1234567
891011121314
15161718192021
22232425262728
2930     
<< November 2009 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM