home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

技術/ソフトウェア設計/ドメインモデル(Domain Model), Service Layer周辺 (v1)

技術/ソフトウェア設計/ドメインモデル(Domain Model), Service Layer周辺 (v1)

技術 / ソフトウェア設計 / ドメインモデル(Domain Model), Service Layer周辺 (v1)
id: 1286 所有者: msakamoto-sf    作成日: 2014-05-06 23:06:40
カテゴリ: Java システム開発 プログラミング 

会社で、Ruby on RailsのバックグラウンドがあるけどJavaはそれほど深くない人向けに、Javaでのオブジェクト指向設計とか、パッケージやクラスの分け方、置き方を説明した方が良いかなーという場面が出てきました。

が、いざ棚卸ししてみますと、特にドメインモデル周辺について自分も良く理解してない点が多々有りまして。なんかざっと記事を漁ってみまして、それのログというかメモ書きになります。

全体的に「如是我聞」な内容・・・どころか、そのまんま載せるのに力不足でもしかしたら曲解・誤解して書いてる内容があるかもしれません。とにかく参考資料(記事のURLや、PoEAA本のP数)については載せましたので、不審に感じたら元資料をあたってみてください。


開始点 : service layerとかdomainの分け方

うろ覚えになってたのが、「なんかservice layerとかdomain modelとかbusiness logicとかに分けるんだよなー」レベルでしたので、そもそも service layer ってなんだろうと適当にググりました。

・・・SOAは飛びすぎた・・・。

TERASOLUNA というフレームワーク用の解説になってますが、うろ覚えレベルの知識でも、「ああ、そうそう、こんなレイヤー分けだったわ。」的な図が載ってて分かりやすい。

キーワード:「ドメイン」

「ドメイン」というのが分かりづらいのですが、そのアプリの解決すべき問題を表現するものらしいです。

ここで分かりづらいのが、「ドメインロジック(domain logic)」とか「ドメインモデル(domain model)」みたいな用語が乱れ飛んでる点でした。

この記載を参考にすれば、システムに関わる実体とその関係を説明する、概念レベルの模型化、ということで、そんなに違和感は感じません。

ところが、Martin Fowlerの"Patterns of Enterprise Application Architecture"(PoEAA)にも「パターン」の一つとして「Domain Model」というのが出てきます。
こちらはUMLで図を書いて模型化する・・・という話ではなくて、概念に名前をつけた(ドメイン)オブジェクトに振る舞いも持たせるパターンがPoEAAの書くところのDomain Model パターン・・・らしい、です・・・。(これについて断言できるの、PoEAAの著者だけちゃうんか?めんどくさいから全部に「如是我聞」付けたろか。)

例えばblogシステムだったら、「記事」という概念がドメインとしてそのまま、Articleというオブジェクトで表現されます。記事の属性やテキストデータが、オブジェクトのプロパティとかフィールドに格納される。で、記事の作成とか編集が、Articleオブジェクトのメソッドとして公開されます。多分こんな感じ。

ところが、機能が増えてくると色々、本来の「記事」とは関連しないような操作が要求されるようになります。
例えばユーザがある記事を "watch" して、記事が更新されたりコメントが投稿されたらメールで教えて欲しいとか。
「コメント」は「記事」に関連する別概念として切り出したほうが良いでしょうか?そうなると、データの表現として、「記事」オブジェクトと「コメント」オブジェクトはどう関連付ければ良いのでしょうか?

ここで、どうやら2つの方向性があるっぽいです。(PoEAA, p117)
1つ目は、「シンプルな」Domain Modelと呼ばれてるもので、RDBなどでテーブルをそのまま1つのドメインとして切り出した形式で、Active Record パターンなどが使われるようです。
2つ目は、「リッチな」Domain Modelと呼ばれてるもので、もっと複雑なロジックやデザインパターンを活用して、1つのドメインに対応するデータベースの実体を特定できないほど複雑なタイプで、これは Data Mapper パターンが上手く当てはまるようです。

キーワード: "Service Layer" (ここから迷走が始まる)

アプリやシステムを構築していくと、Domain Modelに収まらないような概念や処理が出てきます。例えば決裁管理のワークフローを実現するようなアプリケーションだと、決裁処理が進むと、対象となる書類、申請者、決裁者、など複数の概念(ドメイン)が関連して状態が変化します。
そうなった時に導入を検討するのが、アプリケーションとして必要なドメイン処理をまとめたレイヤーで、これが PoEAA p133で "Service Layer"パターンとして紹介されてます。

多分、本来の Service Layer は複数のDomain ModelかTransaction Script (PoEAA p110)を連携させるだけの薄いラッパー処理になることを意図していたように思われます。
こんな感じで、ひとつの境界線として機能するわけです:

UI/Batch/HTTP <-> Service Layer <-> Domain Models / Transaction Scripts

ところがだんだん、Domain Model の扱いがおざなりになってきて、

UI/Batch/HTTP <-> Service Layer <-> SQLなど永続化処理、各種ビジネスロジック
                                <-> 値オブジェクト / エンティティ

のようになってきて、"Domain Model"がデータ表現だけを扱う状態になってしまい、Service Layerの中に処理がどんどん入ってきてしまう、そんな設計が増えてきてしまったようです。

キーワード: 「値オブジェクト (Value Object")」「エンティティ (Entity)」

「値オブジェクト」というのは、識別する必要のないラベル値、量などを表現するパターンです。

  • PoEAA, p486, "Value Object" : "A small simple object, like money or a date range, whose equality isn't based on identity."
  • ドメイン駆動設計・アプリケーション構築編・値オブジェクト - Strategic Choice

「エンティティ」というのは、ドメインの表現により強く関連し、ID値を持っていて、他のデータと区別する必要があるオブジェクトです。ユーザや記事、グループなど、一般的なCRUD操作が必要となるデータ表現全般に当てはまるオブジェクトです。

この辺の記事を読んでみると、例えば「ユーザ」の1人を表現するのが「エンティティ」で、その性別を表現するラベル付の値は「値オブジェクト」として考えて差し支えなさそうです。

"Service Layer"の誤用の話題に戻すと、本来のDomain Modelとの関係から離れてしまって、単なるValue ObjectやEntityオブジェクトを"Domain Model"と呼んでしまい、ドメインに関連するロジックがすべて Service Layer で実装されてしまう設計が問題視されたようです。

キーワード:「ドメインモデル貧血症」(AnemicDomainModel)

前述のような、Service Layerに処理が書かれすぎてしまい、肝心の Domain Model が Domain Model の本来の機能を果たしていない、という状況について Martin Fowler たちが「ドメインモデル貧血症」というアンチパターンを命名しました。

Javaとは離れてしまいますが、MSDNマガジンで「ドメインモデル貧血症」、「リッチなDomain Model」、「値オブジェクト」について分かりやすい記事が公開されています。

  • データ ポイント - ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 2 部)

ドメインモデル貧血症への対処について

まだ調査が進んでいないのですが、そもそもPoEAAでもDomain Model自体が「ものにする」のが難しいパターンで、コンサルタントを呼んで学んだり、トレーニングを積んだり、コーチングを受けて使えるようになるまでは Transaction Script やData Mapperを採用する手もある、と書かれているほどです。(PoEAA, p119, "When to Use It")

あくまでも私見ですが、それほどまでに習熟が難しい"Domain Model"パターンを、限られたリソースしか投入できない現実の開発現場で、本当にMartin Fowlerたちが目論んだどおりの意図に厳密に従い適用するのは、そもそも現実的な話なのでしょうか?

Data Modelのメリットとしては、OOPの作り方を活かせるため、処理が分散しない、という点が挙げられます。しかし、本当にそのメリットを享受するだけの価値が、Data Modelの学習コストでペイ出来るんでしょうか・・・???

Data Model「でなければならない」というのは、法律で定められてる訳ではないので、ある程度のリスクを取った上で、Transaction Script や Data Mapper を適用した方が適切なケースはある筈です。

Service Layerの切り分けについても、粒度の問題があります。アプリのユースケースに対して、Service Layer や Domain Model, Transaction Scirpt の粒度をどのように調整するのか、という観点が出てきます。
これについては以下の記事が参考になると思います。

  • いまさらきけない「ドメインモデル」と「トランザクションスクリプト」 - ひがやすを blog

上手くDomain Modelを組み立てられないような処理が出てきたので、徐々にService Layerが太っていった可能性もあり、そうした場合の解としてはやはり、Service に組み込む、というアプローチがあるようです。

キーワード:「データ転送オブジェクト」(Data Transfer Object)

実際にアプリケーションを設計する際は、プレゼンテーション層のことも忘れてはいけません。
ここで、Domain Model辺りを検討していると、「はたして、プレゼンテーション層がダイレクトにDomain Modelに触るのは適切だろうか?」という疑問が出てきます。
ましてや Service Layer を導入すると、そもそも Service Layer は境界線として機能するため、プレゼンテーション層が Service Layer を介して Domain Model のオブジェクトを取得できてアクセスできてしまうのはどうも一貫性に欠けている気もします。

そういう場合に、DTOに変換することで、データ転送のためだけの、シリアライズ可能なオブジェクトとして扱うパターンが「データ転送オブジェクト」(DTO)のようです。(PoEAA, p401)
シリアライズが出てくるのは、ネットワークを介した分散システムを想定しているからのようです。

MSDNマガジンの記事で、調度良いのがありました:

RailsのActiveRecordとDomain Modelについて

Railsの場合、ActiveRecordが"model"の役目も負っていることにより、なんでもかんでもmodelに入れたり、かなり設計技法や概念的な部分で開発者が困惑し、誤用したり乱用するケースがあるようです。

さらにこれが、MVCパターンの"Model"の部分と絡みあってしまい、混乱に拍車をかけてる感じが・・・。

"fat model", "fat controller" への対処

このへんの議論については、ひがやすをさんの意見が一番バランスが良い気がします。

そもそも、どこに何を置こうと、制約や当初の前提が崩れることはどうしても出てきてしまうので、そこはソフトウェアテストの自動化を適用してService Layerに対するテストを自動化した上で、リファクタリングで対処する。これが現実的なアプローチに思います。

EJBはどう考えようか?

ぶっちゃけEJBを使う予定は当分ないので、ググって見つかった上位記事だけを数点メモ。

参考資料

Martin Fowler氏による"Service Layer"パターンのPoEAA記事(PoEAA本とほぼそのままのよう)

DDDについての日本語解説記事

MSDNマガジンより。.NETテクノロジーをベースとした話になっていますが、クラス分けやレイヤー分けの考え方、アドバイスについてはJavaでも十分展開可能な内容となっており、参考になります。

SpringでのService Layer, Domain Model の実装例解説

PHPでのService Layer, Domain Model の実装例解説

その他の議論、解説:

感想

仏教史とかキリスト教史かじってるせいか、経典や師の言葉の解釈がどんどん枝分かれして、極端に走ったり揺れ戻ったり、互いに批判したり、誤用されたり濫用されたりとか、そのまんまですよ奥様・・・。

「パターン」は技法というか考え方、思想、表現方法であって、「こうでないと間違い」という類のものではないはずです。というか、「パターン」の本来の意図どおりに使われて無くても、結果としてそのアプリケーションなりシステムなりを、妥当な品質と妥当なコストと妥当なメンテナンス性と妥当なソースコード可読性でもって構築できていれば、現実的には問題は無いはずなんです。
・・・と考えるのは異端なんでしょうか。
(まぁ、まだ勉強途中の開発者がそのアーキテクチャをみて、「ふ~ん、これが○○というパターンの適用例なのか」と刷り込み学習してしまう危険性はありますが。)

あくまでも、超個人的な意見としてですが、一番気をつけなければイケないのはデータ表現方式のレイヤー間でのミスマッチだと思うんですよ。
次点でソフトウェアによるテスト自動化。

Webアプリにせよ、ネイティブアプリにせよ、あるいは他システム間連携のNW分散システムにせよ、ファイル読み書きしかしないバッチ処理にせよ、絶対に、「入力/出力」のデータフォーマットと、内部のビジネスロジックで扱い易いデータフォーマットにミスマッチがあるはずなんです。
もちろんシステムの規模や言語によってそれは変動して、単純なCSVレコードを処理するような場合だとほとんど入力・出力と内部処理の表現のミスマッチがない場合があるでしょうし、逆に、WebアプリだとHTTPでやってくるパラメータ形式と、OOPの言語やライブラリで扱い易いオブジェクト表現にはそうとうなミスマッチが出てくると思います。
もう一つミスマッチが発生しやすいのが、ビジネスロジックと永続化レイヤーの間です。
これも、SQLだったりシリアライズ処理だったりと、それぞれの言語や永続化機構、ライブラリ、フレームワークによりミスマッチの程度は異なってきます。

で、このミスマッチを、無理やり、メタオブジェクトプログラミングとか、リフレクションとかを使って「単純なケースなら自動マッピングOK」としてしまうと、色々崩壊してくると思うんですよ・・・ってこれも私見ですが。

自動マッピングならコード書かなくて済むじゃない・・・と言われると思うんですが、案外、テストさえ自動化できてれば、多少手作業でも素直に、愚直に、自前で変換処理を作ったほうが、後々自動マッピングの罠や制限で苦労しなくて済む場合が多そうに思うんですが・・・どうなんでしょうかね。もちろんそのへんの地雷とか罠とか、自動マッピング処理が適用できる限界点を把握した上で付き合う分には問題ナッシングと思います。

超個人的な意見ですが、あんまりOOPの継承とか派生をこのへんの仕組みに適用しすぎても、却って後々の変更に弱くなりそうな気がします。単純なEntityとテーブルマッピングにしておいて、多少Service Layerがfatになっても良いのでロジックはあまりEntity側に入れない、代わりにService Layerに対してAPIとしてのテスト自動化を進めて、後々のリファクタリングやリグレッションテストを効率化する、という方が良い気がするのです。
OOPとしての筋の良さよりは、品質、メンテナンス性、可読性、そして理解しやすさと、「誤解のしにくさ」が何よりも大事かと思います。
そうした点で、徒に Domain Model とかレイヤー分けとかモデリングの「絶対的な正しさ」にこだわって凝ったアーキテクチャ設計にしてしまうと、後から入ってきたり突発的にお手伝いに入った開発者がすぐに活躍できない状況になってしまいそうです。
OOPが活用されないと可読性や再利用性が・・・と言われそうですが、これをテストの自動化とリファクタリングでカバーするイメージです。
でないと、Domainがドーノとか、あるべき設計は~というのを議論したり勉強するのだけで何十時間も、何年も費やすことになってしまい、「ぼちぼち妥当で可読性と理解しやすさ、誤解のしにくさ」を備えたコードを作る機会を逸してしまいそうです・・・。

とりあえず今回の調査はここまで。

※前にこんなの書いてた : 日記/2012/12/31/SQLをうまく扱うプログラム設計メモ



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2014-05-06 23:07:54
md5:88f6886831d89dc21786ddd69237ec4d
sha1:a7c3d7cfe47a3e57fa54fa4d76c4b4daa4d90d06
コメント
コメントを投稿するにはログインして下さい。