告知サイト
http://connpass.com/event/5072/
以下の書籍をターゲットとした読書会なのです。
![]() | エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) (2011/04/09) エリック・エヴァンス 商品詳細を見る |
場所はいつもの矢向、横浜地区センターです。
参加者は11人です。初参加は1~2名かな。
今回はじゅんいち☆かとうさんによるDDDの解説があるということで、申し込み開始後、すぐ満席になりました~
事の発端は以下のツイート参照。
@j5ik2o DDD本の読書会主催してて案件がド直球なのでお話していただきたいような・・・(参加者が通常6名ぐらいの会っすけど #DDDSheep
— なおぴ! (@naopi) 2014, 1月 23
この後、二つ返事で引き受けてくださったじゅんいち☆かとうさん!
イヤッッホォォォオオォオウ!
主催の@naopiさんもグッジョブ!
この日は前日の京浜東北線の脱線事故の影響で、蒲田~川崎間がまさかの終日運休・・・
んでも私は京急線の最寄駅が徒歩8分くらいのトコにあるので、京急線を使って無事辿り着くことができました。
■じゅんいち☆かとうさんによるDDD解説
Scala with DDD from じゅんいち かとう
今日のスペシャルコンテンツ!
以下、個人メモをダラダラ書き起こしてみる。
---
■自己紹介
・DDDの出会いは4年前くらい。原著を辞書引きながら読んでいた。
・@t_wada さんに訳者の和智さんを紹介してもらい、DDD本の第二部のレビューを担当した。
・書籍の翻訳者謝辞に「じゅんいち☆かとう」の名前が出ている。
・50人規模のプロジェクトで、XPとDDDで1チーム4名、ペアプロでユビキタス言語を使いドメインモデルを作ってやっていた。
・み○ほ証券のホームトレードシステムの開発でDDDをやり始めた。
・ドワンゴに転職して、スマホ向けシステムでScalaとDDDを使い、基盤周りを開発した。
■DDDについて
・複雑な業務系のドメインはDDDと相性がいい。でもアジャイルじゃないとできない。
・ウォーターフォールは向いていない。
・DDDを始めるには前提となる知識が必要で、事前に読むべき書籍がたくさんある。
・ただ、DDDは設計論の頂点にあるからといって、下から階段を登って学んでいくと一生が終わってしまうw
・なので、今から初歩的な学習をやるのではなく、本質的な所から入って、必要なものを上から下がって知識をインストールしていく方が、今からDDDを実践するには向いている。
・難しいものと向き合うときに役に立つのがDDD本。
・DDDは設計の本なので、実装についてはあんまり書いてない。
・発刊して10年以上経っているが、陳腐化していない。未だに設計の議論は行われている。
■Webアプリの設計時に何を重視するか?(P.7/65)
・設計には色々なアプローチとコンテキストがある。
・開発してすぐ捨てるようなシステムには、DDDは向かない。
・何を重視するかを考えるとき、「テーブル重視型」と「UI重視型」の2種類がある。
・UI重視型が悪かというと、そうではない。
・プロトタイプを作ってみないと分からない、といった場合は、Smart UIアンチパターン(※1)が使われることが多い。
(※1) 第4章のP.74 "利口なUI「アンチパターン」"を参照。
・でも、それを続けると死人が出る(笑 ので、使い続けるならDDDを前提にしたほうがいい。
・DDDはモデルを前提にしている。それは、複雑な問題をオブジェクト指向で解決したいから。
・DDDの基盤はオブジェクト指向。
■The Hexagonal Architecture (P.16/65)
・真ん中にはドメインモデルがあり、問題の領域の知識を定義している。
・例えばモンハンだとプレイヤーとかアイテムが該当する。
・ERPだと商品とか売上とかが該当する。
・問題の領域の言葉を定義して、実装しているのが真ん中の部分。
・左上に、他のシステムからの入力インタフェースがある。
・コアにはドメインモデルがあり、ここを解決しないとソフトウェアの価値はない。
・実装コード上にコアは見出しづらい。
・ドメインモデルは、現実のモデルとかサービスで使っている言葉と合わせましょう。
■モンハンの例(P.19/65)
・ドメインオブジェクトとして、MonsterとかHunterとかがある。
・それをちゃんとモデル化しましょう。振る舞いを持つオブジェクトとして定義しましょう。
・モデルはただのデータじゃない。役に立つシナリオというものがある。
・モデル自体の振る舞いがそこで行われる。
・例えば、プレイヤーがMonsterを倒してItemを拾うシーンを考える。
・その際、Webアプリケーションだと落し物テーブルを作りたがる。
・Hunterが落し物(LOST_MATTER)を拾ったから、関連が必要だよね、ということで、間を繋ぐテーブルを作りたがる。
・この設計はドメインの知識を表していない。この関連ってドメインにどう紐づくのかわからない。
・Hunterが落し物(LostMatter)を拾っているので、HUNTERとLOST_MATTERという関係でしかないはず。
・割とこれは、ゲームをする人のメンタルモデルと紐付ける考え方である。
・関連テーブルの考え方で話すと通じない。
■ドメインの語彙=ユビキタス言語(P.25/65)
・Hunterとかのユビキタス言語を決めたら、ドメインモデルを定義し、すべての人が意思疎通しやすくなるようにしましょう。
・ユビキタス言語は「シナリオ」⇒「モデル」⇒「実装」のサイクル。
・このサイクルの話は書籍に書いていないが、著者のエリックさんがそう話している。
・まず、モデルをどういうふうに使うかの「シナリオ」がある。
・例えば、プレイヤーがパーティーを組んでモンスターと戦うようなシナリオとか。
・シナリオは、ユーザーストーリから引っぺがしてくる。
・最初は「ユーザーストーリー = シナリオ」として広げていく。
・次に、ホワイトボードの前に丸を書いて、線を引っ張って、1:1なのか1:nなのかを考えながら「モデル」を作る。
・どういう属性や振る舞いがあるのかはまだ考えず、まず枠だけ書いて、それで「実装」に落としちゃう。
・そこから属性や振る舞いをチームで議論して、また「実装」に落とす。
・大きいシステムだとUMLを書いたほうがいいかもしれないが、小さいシステムだと、ホワイトボードで書いて写真とって共有するくらいでもいい。
・アジャイルが向いているのは、そういう「シナリオ」⇒「モデル」⇒「実装」のサイクルがあるから。
・イテレーションの最初のフェーズでは、このサイクルを回す。
・それでユニットテストを書いていく。DBはモックとしている。
・リポジトリもモックとかオンメモリリポジトリを使っている。そうやってモデルが通用するのかを固めていく。
・そのあとに、ドメイン層にも手を入れていく。
■DDDによるレイヤー化(P.28/65)
・スプリントを何回か回して形ができてきたら、UI層とインフラストラクチャ層から挟み込む。
・StrutsやSpringといったフレームワークからは完全に切り離して考える。そうしないとF/Wと喧嘩しちゃう。
・ドメイン駆動は別のモジュールとして切り出しているので、Webでもバッチでも呼べるようにアプリとして依存しないように分離させて書く。
・新しいフレームワークが出てきてもドメイン層は中立している。
・ActiveRecordとドメイン層は分離していたほうが良いが、トレードオフがあるのでできない場合があるので、ケースバイケースで対応が必要。ただ、分けたほうが安全。
・JPAはJDBCに依存しているので、いきなりJPAを前提にしてエンティティにアノテーションを付けちゃうと、Memcacheを使う時にに問題になったりすることがある。
・DDDをサポートしている既存のフレームワークは無いので、分けて考えたほうが安全である。
・モデルはなるべくドメイン層だけにしたい。テーブルとドメインモデルは違う、ということを理解する必要がある。
・今までのMVCの考えだと、Mは「UI層、ドメイン層、インフラストラクチャ層」の3つに分かれる。
・今はは3つがごちゃ混ぜになっているが、DDDの視点でみると、3つのパートは分離すべき。
・上の層が下の層を知っている必要はあるが、逆はダメ。
・依存関係の話が第2部の最初に出てくるが、相互依存をシビアに管理しないといけないので、依存は一方向とすべき。
■モデルと実装(P.29/65)
・書籍の5章に登場するエンティティ、値オブジェクト、サービスなどは全部理解している必要がある。
・エンティティは特殊なオブジェクトで、ideniityを持ってないとけない。
・オブジェクトを比較した時に、idenitityの値が同じかどうかで同一性を判定しないといけない。
・例えば人の名前は変わるけど、identityは変更してはいけない。
・なのでidentityはfinalで不変にし、一生変えちゃいけない。再利用も基本的には許さない。永久欠番とかにする。
・equalsメソッドは、デフォルトだとハッシュコードの値で比較しちゃうが、DDDではidentityで比較するようにしなくちゃいけない。
・Userクラスのインスタンスであるyamashiroとkatoさんがいて、インスタンスが持つ3つの属性の値が一緒だったらイコールと判定するのは、エンティティじゃないモノの考え方である。
・P.34のコードは、リストの各要素がアンダースコアに順次入って、existsで存在を評価するコード。
・ここで後ろの名前をYamashiroからHogeshiroに変更してしまうと、今度はexistsで不一致と判定して、戻り値がfalseになる。この操作は危険。
・ちゃんとしたエンティティの実装は、identityで同一性を判定し、他の属性はぜんぶ無視する。
・エンティティには属性がないと表現は乏しいが、idだけを保持してidentityを証明したい、という考え方が大事。
・Userクラスの属性であるfirstNameとlastNameも、文字列ではなくオブジェクトにしてしまった方が本当は良い。
・Userのidは変えられないようにしないといけない。
・人の名前は流転していくもの。年齢とか住所とか、その時々で変わるものをあてにしてはいけない。
・HunterIDをInt型にすると、他の整数を代入されることがあるので、Int型ではなくクラスにした方が良い
■値オブジェクト(P.39/65)
・識別はしない。
・書籍ではクレヨンの例がでてきた。
・モンハンではアイテムとかが値オブジェクトになりやすい。
・DDDだと、値そのものも振る舞いを持つ、という考え方がある。
・値オブジェクトに振る舞いがあるなら、持たせた方がいい。
・例えば回復アイテムは、指定したハンターを回復させる、といった振る舞いを持たせる。
・アイテムはハンターに使われるので、メソッド名は受動態にしている。
■ドメインモデルはユビキタス言語と対応づくこと(P.42/65)
・設計に躓くと、「ここにフラグが必要だよね」といった設計になるが、そうじゃない。
・ドメインモデルはユビキタス言語と対応づけること。
・「なんとかフラグ」という言葉が出てくるのが一番ヤバい。
・本当に「フラグ」のような概念があるんですか?と問わねばならない。
・ユビキタス言語が変わると、凄いリファクタリングが発生する。概念が変わると実装が変わる。
・なのでリリース直後とかには実施できないので、バックログに積むなり工夫する必要がある。
■ライフサイクルの管理(P.44/65)
・よく誤解するが、ライフサイクル管理はドメインの知識と関係していない。
・集約はよく理解しておかないといけない。
・リポジトリはドメインの知識ではなく、I/Oだけを担当する。
■よくある勘違い(P.47/65)
・User#saveというコードは、Userというエンティティにsaveというユビキタス言語があるんですか?と問わねばならない。
・無いはず。それはリポジトリの責務のはず。
・ActiveRecordはドメインモデルとせず、インフラストラクチャ層にすべき。
■Repository ≠ DAO (P.48/65)
・リポジトリはDAOじゃない。もう一段高い抽象化されたもの。DAOはリポジトリの内部で使われる実装のこと。
・グローバルな識別子を持っているのが集約のルートになる。
・集約の単位がDBだとトランザクション境界になる。一般に、トランザクションの境界は集約。
・DDDをやるとなると、既存のフレームワークの便利なものをリポジトリの中で使わないといけない大変さがある。
・自分たちでリポジトリのフレームワークは作った方がいい。
■DDDではフルスタックF/Wは使いにくい(P.62/65)
・RailsでDDDをやろうとすると、レールから外れないとできないことがある。そのため難しい。。。
・手間をなるべくかけないなら、DDDのフレームワークを自分たちで作った方がいい。
・一番面倒なのはリポジトリ。
■Q&A
Q1.
・DDDで使いやすいF/Wはあるか?
A1.
・無いと思っている。
・フルスタックF/WがDDDを前提にしていないと難しい。F/Wはそれぞれの世界観を持っているので。
・極論すればフルスタックF/Wはいらない。ドメインやORMは切り離さないといけない。
Q2.
・DDDはORMとの相性が悪いとのことだが、JPAはDDDと相性が良さそうに感じる。実際どうなのか?
A2.
・エンティティは基本的に永続化層と紐付かない。
・永続化の手段に応じて別々の手段を定義してはいけない。
・DDDでは、JPAはDBならいいかもしれないが、オンメモリとかでは制約になるかもしれない。
・JPAはカラム情報が属性に全部存在しないといけない。
・でもDDDだと、Hunterクラスの属性であるfirstNameとlastNameは、Nameクラスとして別のオブジェクトにしたりする。
・その場合はHunterクラスの構造が階層化されるので、単純にマッピングできない。
・JPAを使うと決めたら、属性を羅列する必要があり、構造が1次元になるのでプログラマの理解度が落ちる。
・概念的な集合にしたほうがフィールドの数を減らせるが、DDDでは表現できなかったりする。
・なのでDDDでモデリングして、JPAを吸収する仕組みをインフラストラクチャ層に作ったりしないといけない。
Q3.
Webアプリとかのトランザクション制御はどう考えればいいか?
A3.
・トランザクション境界は集約ルートになる。集約単位でI/Oしなさい。
・画面の1ボタンクリックを1トランザクションにしたい場合は、トランザクションを入れ子にして、論理的なトランザクションを貼らないといけないかもしれない。
・アプリケーション側でトランザクションを定義しないと現実的ではない。
・トランザクション制御のイメージはP.70の図4.1が参考になる。
・アプリケーション層がbeginTransaction()を呼び出し、インフラストラクチャ層の作業ユニットマネージャに委譲する。
Q4.
・飛行機と宿を両方予約するようなシステムを設計する必要がある場合は、トランザクションをどう設計すればいいか?
A4.
・飛行機と宿を両方とも、1つの集約モデルに含める。
・どちらかが失敗したら片方もロールバックされるようにできるし、設計の意図も伝わりやすくなる。
■7章「言語を使用する:応用例」
じゅんいち☆かとうさんの講演が終わったら、いつもどおり書籍に沿って黙読&ディスカッションタイムです。
この日は7章「言語を使用する:応用例」が範囲でした。
以下、ちょびっとメモ。
■エンティティとテーブル
・「荷役イベント」はエンティティの一種で、ドメインイベントと言う。
・「配送記録」はテーブルにならない。
・「荷役イベント」は貨物ID、完了時刻、タイプの3つのキーの組み合わせを主キーとするか、サロゲートキーを用意するかの2パターンある。主キーとは別に、ientityは存在させる。
■腐敗防止層
・P.184の「配分チェックサービス」はの腐敗防止層としての役割を果たす。
・販売管理システムは予約アプリケーションとは別のドメインにある。
・レガシーシステムのドメインは、予約アプリケーションのドメインに影響を与えたくない。
・販売管理システムがレガシーシステムだったとしても、腐敗防止層として配分チェックサービスを置くことで分離できる。
・ただし腐敗防止層は使い過ぎないようにすべき。
・腐敗防止層を一度でも作っちゃうと、リファクタリングするタイミングを設けないといけない。
・変換コンバータを作って、DDDの層とレガシーの層を分ける。ただし変換ロジックを実装するのはすごく大変。。。
■リポジトリ
・リポジトリを使うのはアプリケーション層である。
・ドメインモデルが永続化とかライフサイクル管理を担っちゃいけない。リポジトリはアプリケーション層が使う。
・なんか値を取ってきたいからとりあえずリポジトリ使えばいいじゃん、とい考えはダメ。
---
あと、説明の過程で「Scala Pet Store API」なるものの紹介もありました。
.@j5ik2oさん作のScala Pet Store http://t.co/7XuX0p9my2 JavaPetStore(http://t.co/ifJLX7bQ4y)のDDD with Scala版 #DDDSheep
— satokittyd (@satokittyd) February 23, 2014
読書会の終了後は、じゅんいち☆かとうさん+8名でいつもの中華屋さんにご飯を食べに行きました。
DDDの話も熱かったが、糖質制限ダイエットのトークも同じくらい熱かった!w
★感想:
じゅんいち☆かとうさんに感謝!
実務でDDDを使ってるエンジニアのお話をじかに聴けて、とても勉強になりました。
本を読んでるだけだと、ふーん、へー、とかでなんとなく素通りしてしまうんですが、経験に基づく説明を聞いてると、あーそういうことだったのか!となる事しきり。
これは本だけでは得られない部分ですね。
こーゆうイベント、今後の読書会でもやれるといいですね~
じゅんいち☆かとうさん、主催の @naopi さん、参加者のみなさんありがとうございました~