コードで学ぶドメイン駆動設計入門

15,292 views

Published on

ソースはこちらを参照してください。https://github.com/tricreo/schema-generator

Published in: Technology, Business
0 Comments
41 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
15,292
On SlideShare
0
From Embeds
0
Number of Embeds
4,917
Actions
Shares
0
Downloads
174
Comments
0
Likes
41
Embeds 0
No embeds

No notes for slide
  • はじめまして、かとうです。本日は休日のところ足を運んでいただきありがとうございます。コードで学ぶDDD入門で話したいと思います。DDDの設計思想でソフトウェアを作っている方ってどれぐらいいますか?\nなかなか、設計思想の理解が大変というのがありますので、そこを私の少ない経験からですが、わかるように説明したいと思います。よろしくお願いします。\n
  • まず、自己紹介です。\n
  • Javaで、Seasarで、DDDで、Scalaでって感じです。\n
  • \n
  • 2004年発刊で、海外では非常に評判の高い書籍です。出版前からアナリシスパターンで有名なMartin Fowlerにより「期待できる内容だ」とか。デザインパターンのGoFのメンバーの人も「4、5回は読み直した」。Spring FrameworkのRod Johnson「これからは、リッチなドメインモデルだ」とか。\nということで中身は、4部17章、約515ページぐらい。 設計パターンは41パターンもあります。ヒーという感じですが、いきなり全部無理なんで基本からという感じですね。英文も読みやすいとはいえません。私は英語苦手なんできつい感じです。 読めたとしても設計思想がテーマなんで、理解するものまた難しいというハードルが結構あります。 完全に理解するには結構難しいです。私も難しいですが、日々なんとかしながら、50人規模ぐらいの現場でDDDを取り入れるために、設計と実装の両面でコンサルティングとかやっています。\nQuicklyなどの日本語で読める資料などはいくつかあります。サンプルコードもあります。そういうところからせめていくとよいでしょう。\n
  • さて、今回はコードから学ぶわけですが、その前に導入編にいきましょう。\n
  • DDD本のPart2のモデル駆動設計の基礎に相当する部分からいきなり解説します。Part1には非常に重要なユビキタス言語というソフトウェアの利害関係者で共通の言語を取り上げているのですが、今回はいきなりここから始めます。モデル駆動設計の考え方が分かれば、Part1も読みやすいし、Part3以降も読みやすいと思います。\n
  • まず、ドメインとはなんでしょう。\n
  • 辞書で引くとこんな感じです。領土問題は最近の熱い話題ですね。それと、領域といえば、、、\n
  • DDDでは、ドメインとは、簡単にいうと問題解決の領域です。業務アプリでは、対象の業務そのものがドメインです。Quicklyでは空港の管制塔のドメインの事例が紹介されていましたが、ドメインには飛行機や航路などが含まれます。\n
  • まず、ドメインの話に入っていく前に、レイヤードアーキテクチャを把握しましょう。これは結構大事です。\n
  • DDDにはまずレイヤードアーキテクチャの話がでてきます。アプリケーションでは、大部分はドメインとは直接関係しません。しかし、ドメインと関係するコードが他の層とまざるとコードを読んで検討することが難しくなります。ドメインのことだけに集中できなくなるわけです。\n
  • たとえば、簡単にこの3つの層を混同してしまうと、このようになります。実際の現場ではこういうことは結構あるんではないでしょうか?\n左側だと、ビジネスロジックを変更→UIやデータベースアクセスに変更の影響が出る可能性が高く\n右側だと、UIやデータベースアクセスを変更→ビジネスロジックに変更の影響が出る可能性が高い。\nつまり、変更の影響がレイヤーを超えて波及してしまうため、比較的変更コストが大きくなりやすいのです。\nこのようにレイヤーを混同してしまうと、オブジェクト間の結合度もあがってしまうため、変更だけではくテストもしにくくなるでしょう。カオスといってもいいですね。\n
  • いくつかの層とは、DDDではこの4つに分類されます。レイヤーを混同すると、せっかく作ったドメインオブジェクトを役に立ちません。しっかり守りましょう。\n
  • 一つ目はUI層ですが、一般的な業務アプリでは主眼に置かれる部分です。UIは確かに重要なのですが、もっと重要なのがドメインであり、UIの都合によってドメインが歪んだ設計にならないようにしなければならないとしています。ここでは、具体例を示しませんが、ソフトウェアの構造がUIに引きづられた設計がSmart UIパターンです。賢いUIという名のアンチパターンです。\n
  • ドメイン層のオブジェクトを使って、ソフトウエアがすべき処理を実現する層。ドメインに殆ど委譲するので、このぐらいことしかしないわけです。\n
  • たとえば、銀行の口座管理システムであれば、顧客や銀行口座などがドメイン層に位置する概念です。ルールや状態はワークフローなどが該当します。一番肝心なところです。\n
  • OR MapperなどのDaoやエンティティ(後で説明するエンティティとは意味が違うので要注意です)や、Java APIを使いやすくラップしたようなユーティリティクラスなどもインフラ層に該当します。\n
  • 設計思想はどうしても抽象度が高いので、実際の具体的なコードだとどうなるの?って話が知りたいと思うので、あくまで一例ということで今回サンプルをつくってきました。\n
  • 今回説明する事例のアプリについて簡単に説明します。簡単に言えばDBに対してSQLを発行してスキーマを作るツールです。顧客管理業務などの実際の業務を対象したサンプルだったらよかったのですが、ちょっとでかくなりすぎるので、DBをスーキマを作成する業務ということで考えてみました。あまり良い例ではないかもしれませんが、全体的な概念を抑えるようにしていただければよいかなと思っています。\n仕様としては、コンソールアプリで、作成するスキーマの設定はプロパティファイルに書きます。設定ファイル上にはこの3つ概念があります。実際の設定ファイルはこれです。\n
  • それでは、本題のドメイン層の中のオブジェクトについて説明します。\n
  • \n
  • 業務を表すモデルオブジェクトです。モデルといえば、MVCのMです。業務ロジックを扱うところですね。EntityとValueObject,Serviceがあります。これは単なるJavaBeansなどの属性の入れものではなく、ちゃんとした業務を担うオブジェクトです。これらのオブジェクトは、ユビキタス言語(ソフトウェアの利害関係者で共通な言語)と対応づくものになります。たとえば、口座管理なら、顧客、口座です。\n
  • Entityです。\n
  • Entityは、見分けることが目的のオブジェクトです。ORMのEntityではないので、その概念は一旦忘れてください。ドメインの世界のEntityです。つまり、見分ける必要がある概念をEntityにマッピングします。たとえば、顧客とか、口座とか。見分けることは識別といいます。見分けるためには識別子が必要です。顧客でも同姓同名がいる場合は識別子で識別しなければ、顧客を見誤ります。これ結構重要な概念です。あと、特徴としてはequalsなどの等価判定では識別子しか見ません。hashCodeもそうです。\n\n
  • 本質的には、getFullNameメソッドの不具合です。これを解消するためのコストを考えてみてください。この程度の規模ならなんてことないですが、でかいソフトウェアなら誰が不用意な更新をするかわかりません。この不具合を解消するのが根本対策ですが、そもそも不具合の温床を作らない工夫が必要ではないでしょうか?ゆえに、MStringなどの共有する値オブジェクトは不変であるべきです。\n
  • JavaのStringはImmutableです。こんな感じです。concatで連結したのに、firstNameが変わらないなと思ったことないですか?それは正解なんです。壊しちゃいけないんです。\n
  • こんな感じになります。属性は問いません。識別できる識別子と等価判定ができればOKです。\n
  • Schema-GeneratorのこれらのクラスがEntityです。まず、Entityインターフェイス。\n
  • 5歳のころとは、身長も年齢も変わった今の自分であるが、私であることは変りない。結婚すると名前が変わることもあるが、それでも私である。そういう風に見分けることができるのがエンティティの概念です。そのためには識別子と識別子による等価判定が必須。\n
  • \n
  • 上から順番に説明すると、、こんな感じです。JavaのStringはImmutable。Objective-CにはMutableStringもあります。VOは基本Immutableだと考えてください。値は共有されることが目的なので、同一インスタンスで値がころころ変わってしまうのは、一貫性がなく危険です。不具合が発生すると、原因究明に時間がかかるパターンですね。あずかり知らぬところで、意図しない状態の更新が行われることは防がなければなりません。そのためにはオブジェクトの状態を不変(Immutable)にすることが推奨されています。\n
  • \n
  • Schema-GeneratorのこれらのクラスがEntityです。まず、Entityインターフェイス。ActionContextは実はImmutableではなくMutableです。\n
  • EffectiveJaveに書かれているノウハウで、DDDに直接関係ないですが、VOは基本Immutableなので、前提としておさえておきましょう。\nsetterがあってはなんにもならないのは当然。\nfinalフィールドは、誤ってsetter書いても、再代入をコンパイラで検出できます。\nフィールドをprivateにするのは、外部からのアクセスを禁止するためです。public final フィールドでもよいが、リリースした後に内部実装を変更できないのであまり推奨できないです。\n派生クラスでsetterの追加と、getterのオーバーライドによって不変性を破壊することを防ぐために、final classとしましょう。\nこれについては、実装クラスのみの場合は、Mockライブラリとの相性でfinalにできない場合があります。こういう場合はMockは使えないですね。まぁ、トレードオフだと思います。\n次の、可変オブジェクトの属性、つまりCollectionやMapなどのように同一インスタンスでどんどん状態を変更できるオブジェクトの属性ですね。その属性の参照を外部に渡した場合はもちろん状態を変更できます。これによって属性を保持しているオブジェクトの不変性が壊れてしまいます。これを防ぐにはcloneなどで防御的にコピーしたインスタンスを受渡します。コード上ではCloneUtilってのを使っています。あとで紹介します。\n
  • 識別子の概念を考えたり、実際に識別子を生成したり、等価判定の仕組みを実装したりは、コストがかかります。何でもかんでもEntityだといろいろ大変です。アイデンティティを持たない値の時はVOにしましょう。VOはEntityから参照をたどれるようになっていればよい。\n
  • 次はサービスです。\n
  • 消去法的にEntityやVOに該当しない場合にサービスとなります。非常に都合がいいオブジェクトです。\n
  • データソースに接続するオブジェクトを誰にしようかと思ったんですが、DataSource自身が接続するの?という違和感があったので、このようなサービスを定義しました。\n
  • 都合がいいだけに乱用するとトランザクションスクリプトに傾倒するので要注意です。\n
  • 次はライフサイクルです。\n
  • VMのメモリ上にいつも存在するとは限らない。ファイルやDBにある場合も。\n原書では、Carに対するTireの関係がAggregateということで紹介されています。\n\n
  • \n
  • DDDに限らず、デザインパターンにも登場する概念ですが、オブジェクトを作るために初期化のプロセスが複雑になると、そのオブジェクトを使うクライアント側の手間がかかります。DIコンテナが使われるのもそのためです。\n
  • コードとして単純なファクトリです。馴染み深いと思います。EntityでもValueObjectでもFactoryの責務はありえます。StringBuilderに近いイメージです。この例ではファクトリはなくても構わないと思います。これ以上初期化が複雑なら必要だと思います。\nただ、Immutableでコンストラクタの引数が多い大きめのVOは、Factoryではなく、Builderパターンというものを導入するとよいと思います。\n
  • 原理主義ではなく、複雑なところだけ作ればよいのです。\n- ポリモーフィズムが不要な時\n- クライアントが実装の詳細を必要とする時\n- 組み立てがシンプルな時\n- publicなコンストラクタが不変条件を満たすアトミックな操作である時\n場合によって、ファクトリメソッドでもありです。\nコンストラクタの引数の数が多いオブジェクトはファクトリだとしんどいかもしれません。そんな場合はBuilderを使うといいと思います。\nちなみにScalaではコンパニオンオブジェクトがあります。同名のobjectを定義するとファクトリのように振る舞います。ListはList.applyの構文糖衣です。\n
  • \n
  • \n
  • \n
  • Repositoryの責務は、ドメイン層とインフラ層の変換、Dxoです。まぁ、Daoを直接使いたいところでしょうけど、それをやってしまうとレイヤーが崩れてしまうので、要注意です。綺麗に分けたいなら、きちっとレイヤー守らないとカオスになってしまうでしょう。DxoはDSLで解決すればいいと思います。\n
  • 弊社の宮本が担当しているbasicunitsを近日中に公開する予定です。Mutableなjava.util.Dateを置き換えるようなVOライブラリです。ALなんで好きに使ってください。\n
  • \n
  • コードで学ぶドメイン駆動設計入門

    1. 1. • • seasar, java-ja 18 •• 38 • Scala Advent Calendar• Java 2010/12/31 !• S2Chronos, S2Config • @j5ik2o
    2. 2. DDD
    3. 3.  
    4. 4. •••
    5. 5. domain =
    6. 6. •••
    7. 7. ••• http://www.flickr.com/photos/yoshimov/228695466/
    8. 8. • UI••• http://www.flickr.com/photos/mithril/3588952396/
    9. 9. UI• • •• Web Action, Page, JSP, JS http://www.flickr.com/photos/ph0t0s/169353882/
    10. 10. • • • http://www.flickr.com/photos/jordanleepics/359596349/
    11. 11. ••• http://www.flickr.com/photos/lwr/2405124325/
    12. 12. •• http://www.flickr.com/photos/96528863@N00/3257210887/
    13. 13. DDD
    14. 14. • SQL Schema Generator• • ( ) • ( ) • (SQL )
    15. 15. • • Entity, ValueObject, Service • • • •
    16. 16. Entity
    17. 17. • •••• (Mutable) (Immutable)
    18. 18. public static void main(String[] args) { MString firstName = new MString("Junichi"); MString lastName = new MString("Kato"); MString fullName = getFullName(firstName, lastName); System.out.println("fullName = "+fullName); System.out.println("firstName = "+firstName);}private static MString getFullName(MString firstName, MString lastName){ MString result = firstName; result.set(firstName + lastName); // return result;}
    19. 19. public static void main(String[] args) { String firstName = "Junichi"; String lastName = "Kato"; String fullName = getFullName(firstName, lastName); System.out.println("fullName = "+fullName); System.out.println("firstName = "+firstName);}private static String getFullName(String firstName, String lastName){ String result = firstName.concat(flastName); // return result;}
    20. 20. (E)ID ( )
    21. 21. • Entity• DataSource• ActionsImpl
    22. 22. •• ( )•
    23. 23. ValueObject
    24. 24. • •• VO = String, BigDecimal•• Immutable
    25. 25. (E)ID ( ) (VO) (VO) (VO)
    26. 26. • ValueObject• EchoActionImpl• SqlActionImpl• ActionContext
    27. 27. Immutable• • final class • Collection Map• final clone• private getter
    28. 28. • Entity• VO Entity• Collection<VO> VO
    29. 29. Service
    30. 30. • Entity VO Service•
    31. 31. • DataSourceConnectServiceImpl
    32. 32. • Service• Service
    33. 33. • Factory, Repository, Aggregate• • → → • Factory • Repository • Aggregate
    34. 34. Factory
    35. 35. ••
    36. 36. • ActionsFactoryImpl• ActionFactoryImpl
    37. 37. •• DI• VO Factory Builder Address address = new Address.Builder(Pref.TOKYO).withCity(“ ”).withAddress(” 5-6-12”).withBuildingName(“ 7F”).build();• Scala class List(...) { } object List{ def apply(...){ new List(...) } } val list = List(1,2,3)
    38. 38. Repository
    39. 39. • Entity• Entity DB•
    40. 40. • ActionsRepositoryInProperties• DataSourceRepositoryInProperties
    41. 41. • Dxo(Domain eXchange Object)•• Dxo DSL • Scala DSL ( Д ) • Java Dxo • jiemamy object-manipulator
    42. 42. •• Java EE /DDD• Quickly•• baseunits

    ×