#navi_header|技術| 会社で、Ruby on RailsのバックグラウンドがあるけどJavaはそれほど深くない人向けに、Javaでのオブジェクト指向設計とか、パッケージやクラスの分け方、置き方を説明した方が良いかなーという場面が出てきました。 が、いざ棚卸ししてみますと、特にドメインモデル周辺について自分も良く理解してない点が多々有りまして。なんかざっと記事を漁ってみまして、それのログというかメモ書きになります。 全体的に「如是我聞」な内容・・・どころか、そのまんま載せるのに力不足でもしかしたら曲解・誤解して書いてる内容があるかもしれません。とにかく参考資料(記事のURLや、PoEAA本のP数)については載せましたので、不審に感じたら元資料をあたってみてください。 #more|| #outline|| ---- * 開始点 : service layerとかdomainの分け方 うろ覚えになってたのが、「なんかservice layerとかdomain modelとかbusiness logicとかに分けるんだよなー」レベルでしたので、そもそも service layer ってなんだろうと適当にググりました。 - Service-oriented architecture - Wikipedia, the free encyclopedia -- http://en.wikipedia.org/wiki/Service-oriented_architecture - Chapter 1: Service Oriented Architecture (SOA) -- http://msdn.microsoft.com/en-us/library/bb833022.aspx ・・・SOAは飛びすぎた・・・。 - 2.4. アプリケーションのレイヤ化 — TERASOLUNA Global Framework Development Guideline 1.0.0.publicreview documentation -- http://terasolunaorg.github.io/guideline/public_review/Overview/ApplicationLayering.html TERASOLUNA というフレームワーク用の解説になってますが、うろ覚えレベルの知識でも、「ああ、そうそう、こんなレイヤー分けだったわ。」的な図が載ってて分かりやすい。 * キーワード:「ドメイン」 「ドメイン」というのが分かりづらいのですが、そのアプリの解決すべき問題を表現するものらしいです。 ここで分かりづらいのが、「ドメインロジック(domain logic)」とか「ドメインモデル(domain model)」みたいな用語が乱れ飛んでる点でした。 - ドメインモデル - Wikipedia -- http://ja.wikipedia.org/wiki/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%A2%E3%83%87%E3%83%AB この記載を参考にすれば、システムに関わる実体とその関係を説明する、概念レベルの模型化、ということで、そんなに違和感は感じません。 ところが、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 -- http://d.hatena.ne.jp/asakichy/20110525/1306276845 「エンティティ」というのは、ドメインの表現により強く関連し、ID値を持っていて、他のデータと区別する必要があるオブジェクトです。ユーザや記事、グループなど、一般的なCRUD操作が必要となるデータ表現全般に当てはまるオブジェクトです。 - ドメイン駆動設計・アプリケーション構築編・エンティティ - Strategic Choice -- http://d.hatena.ne.jp/asakichy/20110524/1306190461 この辺の記事を読んでみると、例えば「ユーザ」の1人を表現するのが「エンティティ」で、その性別を表現するラベル付の値は「値オブジェクト」として考えて差し支えなさそうです。 - .NETでドメイン駆動開発~ValueObject 前編~ -- http://www.infoq.com/jp/articles/DDD_ValueObject - .NETでドメイン駆動開発~ValueObject後編~ -- http://www.infoq.com/jp/articles/DDD_ValueObject_0325 -- 「Entityのプロパティに使用する。」「Entityのプロパティに使用する属性用のclassのこと。」辺りの記述が参考になりそう。 "Service Layer"の誤用の話題に戻すと、本来のDomain Modelとの関係から離れてしまって、単なるValue ObjectやEntityオブジェクトを"Domain Model"と呼んでしまい、ドメインに関連するロジックがすべて Service Layer で実装されてしまう設計が問題視されたようです。 * キーワード:「ドメインモデル貧血症」(AnemicDomainModel) 前述のような、Service Layerに処理が書かれすぎてしまい、肝心の Domain Model が Domain Model の本来の機能を果たしていない、という状況について Martin Fowler たちが「ドメインモデル貧血症」というアンチパターンを命名しました。 - AnemicDomainModel -- http://www.martinfowler.com/bliki/AnemicDomainModel.html - Martin Fowler's Bliki in Japanese - ドメインモデル貧血症 -- http://capsctrl.que.jp/kdmsnr/wiki/bliki/?AnemicDomainModel Javaとは離れてしまいますが、MSDNマガジンで「ドメインモデル貧血症」、「リッチなDomain Model」、「値オブジェクト」について分かりやすい記事が公開されています。 - データ ポイント - ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 2 部) -- http://msdn.microsoft.com/ja-jp/magazine/dn385704.aspx -- 引用:「値オブジェクトの基本は、ID キーのないクラスです。」 * ドメインモデル貧血症への対処について まだ調査が進んでいないのですが、そもそも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 -- http://d.hatena.ne.jp/higayasuo/20080519/1211183826 -- 「ドメインで扱う概念の中には、機能で閉じていて、もの(オブジェクト)として扱うのが不自然なものがある」 上手くDomain Modelを組み立てられないような処理が出てきたので、徐々にService Layerが太っていった可能性もあり、そうした場合の解としてはやはり、Service に組み込む、というアプローチがあるようです。 - [ 技術講座 ] Domain-Driven Designのエッセンス 第2回 -- http://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap2.html#Services * キーワード:「データ転送オブジェクト」(Data Transfer Object) 実際にアプリケーションを設計する際は、プレゼンテーション層のことも忘れてはいけません。 ここで、Domain Model辺りを検討していると、「はたして、プレゼンテーション層がダイレクトにDomain Modelに触るのは適切だろうか?」という疑問が出てきます。 ましてや Service Layer を導入すると、そもそも Service Layer は境界線として機能するため、プレゼンテーション層が Service Layer を介して Domain Model のオブジェクトを取得できてアクセスできてしまうのはどうも一貫性に欠けている気もします。 そういう場合に、DTOに変換することで、データ転送のためだけの、シリアライズ可能なオブジェクトとして扱うパターンが「データ転送オブジェクト」(DTO)のようです。(PoEAA, p401) シリアライズが出てくるのは、ネットワークを介した分散システムを想定しているからのようです。 MSDNマガジンの記事で、調度良いのがありました: - Cutting Edge: データ転送オブジェクトの長所と短所 -- http://msdn.microsoft.com/ja-jp/magazine/ee236638.aspx * RailsのActiveRecordとDomain Modelについて Railsの場合、ActiveRecordが"model"の役目も負っていることにより、なんでもかんでもmodelに入れたり、かなり設計技法や概念的な部分で開発者が困惑し、誤用したり乱用するケースがあるようです。 - デザインパターンから見たActive Record (2) | TECHSCORE(テックスコア) -- http://www.techscore.com/tech/Ruby/Rails/other/designpattern/2/ - O/RマッパーやActiveRecordによるMVCの誤解 - Togetterまとめ -- http://togetter.com/li/114000 -- ActiveRecordでの誤用、それにより引き起こされる誤解についての解説 - ActiveRecord のモデルを整理する7つのパターン - tkawachi Blog -- http://tkawachi.github.io/blog/2013/09/21/active-model-7-pattern/ - Rails の ActiveRecord モデルテストの書き方ガイドライン - passingloopの日記 -- http://d.hatena.ne.jp/passingloop/20111125/how_to_write_rails_activerecord_model_tests - Crossing borders: Exploring Active Record -- http://www.ibm.com/developerworks/library/j-cb03076/ さらにこれが、MVCパターンの"Model"の部分と絡みあってしまい、混乱に拍車をかけてる感じが・・・。 - Life is beautiful: Ruby on Railsの「えせMVC」の弊害 -- http://satoshi.blogs.com/life/2009/10/rails_mvc.html - そのMVCだと思っているものはMVCでないかもしれない - ふくちはるき×blog -- http://fukuchiharuki.blogspot.jp/2013/05/mvcmvc.html * "fat model", "fat controller" への対処 このへんの議論については、ひがやすをさんの意見が一番バランスが良い気がします。 - えせMVCについてそろそろ一言言っておくか - ひがやすを blog -- http://d.hatena.ne.jp/higayasuo/20091013/1255408723 そもそも、どこに何を置こうと、制約や当初の前提が崩れることはどうしても出てきてしまうので、そこはソフトウェアテストの自動化を適用してService Layerに対するテストを自動化した上で、リファクタリングで対処する。これが現実的なアプローチに思います。 * EJBはどう考えようか? ぶっちゃけEJBを使う予定は当分ないので、ググって見つかった上位記事だけを数点メモ。 - 第1回 EJBのすべてを知る | Think IT -- http://thinkit.co.jp/free/article/0709/14/1/page/0/2 - 技術トピックス|業務アプリもJ2EE一辺倒からWeb 2.0的な軽量技術が現実解に(1/4) | ウルシステムズ株式会社 | UL Systems, Inc. -- https://www.ulsystems.co.jp/technology-topic019-01.html - java - What use are EJBs - Stack Overflow -- http://stackoverflow.com/questions/5579890/what-use-are-ejbs * 参考資料 Martin Fowler氏による"Service Layer"パターンのPoEAA記事(PoEAA本とほぼそのままのよう) - Service Layer | Framework Design Guidelines: Domain Logic Patterns | InformIT -- http://www.informit.com/articles/article.aspx?p=1398617&seqNum=4 DDDについての日本語解説記事 - [ 技術講座 ] Domain-Driven Designのエッセンス -目次- -- https://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/index.html - ドメイン駆動設計・俯瞰編・アプリケーション構築 - Strategic Choice -- http://d.hatena.ne.jp/asakichy/20110425/1303683158 MSDNマガジンより。.NETテクノロジーをベースとした話になっていますが、クラス分けやレイヤー分けの考え方、アドバイスについてはJavaでも十分展開可能な内容となっており、参考になります。 - データ駆動開発からドメイン駆動開発へ (InfoQによるMSDNマガジン連載記事の紹介) -- http://www.infoq.com/jp/news/2013/10/data-driven-to-ddd - データ ポイント - ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント -- http://msdn.microsoft.com/ja-jp/magazine/dn342868.aspx - データ ポイント - ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 2 部) -- http://msdn.microsoft.com/ja-jp/magazine/dn385704.aspx - データ ポイント - ドメイン駆動設計のコーディング: データを重視する開発者のためのヒント (第 3 部) -- http://msdn.microsoft.com/ja-jp/magazine/dn451438.aspx - Data Points - Coding for Domain-Driven Design: Tips for Data-Focused Devs -- http://msdn.microsoft.com/en-us/magazine/dn342868.aspx -- 英語での第一回目?記事 SpringでのService Layer, Domain Model の実装例解説 - Spring – Designing the domain model and the service layer | Java Code Geeks -- http://www.javacodegeeks.com/2012/09/spring-designing-domain-model-and.html PHPでのService Layer, Domain Model の実装例解説 - An Introduction to Services -- http://www.sitepoint.com/an-introduction-to-services/ その他の議論、解説: - Part 1 – Designing the domain model and the service layer | vrtoonjava -- http://vrtoonjava.wordpress.com/2012/06/17/part-1-designing-the-domain-model-and-the-service-layer/ - ASP.NET MVC Business Logic in Domain Model vs Service Layer - Stack Overflow -- http://stackoverflow.com/questions/14657656/asp-net-mvc-business-logic-in-domain-model-vs-service-layer - design patterns - How accurate is "Business logic should be in a service, not in a model"? - Programmers Stack Exchange -- http://programmers.stackexchange.com/questions/218011/how-accurate-is-business-logic-should-be-in-a-service-not-in-a-model * 感想 仏教史とかキリスト教史かじってるせいか、経典や師の言葉の解釈がどんどん枝分かれして、極端に走ったり揺れ戻ったり、互いに批判したり、誤用されたり濫用されたりとか、そのまんまですよ奥様・・・。 「パターン」は技法というか考え方、思想、表現方法であって、「こうでないと間違い」という類のものではないはずです。というか、「パターン」の本来の意図どおりに使われて無くても、結果としてそのアプリケーションなりシステムなりを、妥当な品質と妥当なコストと妥当なメンテナンス性と妥当なソースコード可読性でもって構築できていれば、現実的には問題は無いはずなんです。 ・・・と考えるのは異端なんでしょうか。 (まぁ、まだ勉強途中の開発者がそのアーキテクチャをみて、「ふ~ん、これが○○というパターンの適用例なのか」と刷り込み学習してしまう危険性はありますが。) あくまでも、超個人的な意見としてですが、一番気をつけなければイケないのは ''データ表現方式のレイヤー間でのミスマッチ'' だと思うんですよ。 次点でソフトウェアによるテスト自動化。 Webアプリにせよ、ネイティブアプリにせよ、あるいは他システム間連携のNW分散システムにせよ、ファイル読み書きしかしないバッチ処理にせよ、絶対に、「入力/出力」のデータフォーマットと、内部のビジネスロジックで扱い易いデータフォーマットにミスマッチがあるはずなんです。 もちろんシステムの規模や言語によってそれは変動して、単純なCSVレコードを処理するような場合だとほとんど入力・出力と内部処理の表現のミスマッチがない場合があるでしょうし、逆に、WebアプリだとHTTPでやってくるパラメータ形式と、OOPの言語やライブラリで扱い易いオブジェクト表現にはそうとうなミスマッチが出てくると思います。 もう一つミスマッチが発生しやすいのが、ビジネスロジックと永続化レイヤーの間です。 これも、SQLだったりシリアライズ処理だったりと、それぞれの言語や永続化機構、ライブラリ、フレームワークによりミスマッチの程度は異なってきます。 で、このミスマッチを、無理やり、メタオブジェクトプログラミングとか、リフレクションとかを使って「単純なケースなら自動マッピングOK」としてしまうと、色々崩壊してくると思うんですよ・・・ってこれも私見ですが。 自動マッピングならコード書かなくて済むじゃない・・・と言われると思うんですが、案外、テストさえ自動化できてれば、多少手作業でも素直に、愚直に、自前で変換処理を作ったほうが、後々自動マッピングの罠や制限で苦労しなくて済む場合が多そうに思うんですが・・・どうなんでしょうかね。もちろんそのへんの地雷とか罠とか、自動マッピング処理が適用できる限界点を把握した上で付き合う分には問題ナッシングと思います。 超個人的な意見ですが、あんまりOOPの継承とか派生をこのへんの仕組みに適用しすぎても、却って後々の変更に弱くなりそうな気がします。単純なEntityとテーブルマッピングにしておいて、多少Service Layerがfatになっても良いのでロジックはあまりEntity側に入れない、代わりにService Layerに対してAPIとしてのテスト自動化を進めて、後々のリファクタリングやリグレッションテストを効率化する、という方が良い気がするのです。 OOPとしての筋の良さよりは、品質、メンテナンス性、可読性、 ''そして理解しやすさと、「誤解のしにくさ」'' が何よりも大事かと思います。 そうした点で、徒に Domain Model とかレイヤー分けとかモデリングの「絶対的な正しさ」にこだわって凝ったアーキテクチャ設計にしてしまうと、後から入ってきたり突発的にお手伝いに入った開発者がすぐに活躍できない状況になってしまいそうです。 OOPが活用されないと可読性や再利用性が・・・と言われそうですが、これをテストの自動化とリファクタリングでカバーするイメージです。 でないと、Domainがドーノとか、あるべき設計は~というのを議論したり勉強するのだけで何十時間も、何年も費やすことになってしまい、「ぼちぼち妥当で可読性と理解しやすさ、誤解のしにくさ」を備えたコードを作る機会を逸してしまいそうです・・・。 とりあえず今回の調査はここまで。 ※前にこんなの書いてた : [[1128]] #navi_footer|技術|