fc2ブログ

makopi23のブログ

makopi23が日々の生活で感じたことを気ままに綴るブログです。

「DDD Alliance! ドメインオブジェクトの見つけ方・作り方・育て方」に参加しました

2016/5/25(水) 「DDD Alliance! ドメインオブジェクトの見つけ方・作り方・育て方」に参加してきました。

connpass
http://ddd-alliance.connpass.com/event/31834/

場所は銀座の株式会社ドワンゴさんです。
参加者は100人くらいでしょうか。毎度の満席です。

これまでDDDAllianceの講演は、(無料のは)ほとんど皆勤で参加してます。
いつも学びが多く、今回も楽しみでした。

以下、スライドに無い口頭説明部分を中心に、メモ。


講演


ドメインオブジェクトの見つけ方・作り方・育て方 from 亨 増田



最初に
  • 今日は、自分たちがどうやってDDDをやってきたか、をできるだけ生でお伝えできればなぁ。
  • ドメインオブジェクトの設計の話は
    • 「オブジェクト指向エクササイズ 9つのルール」とか
    • 「リファクタリング」とか
    • バートランド・メイヤーのモジュール性の話とか
  • これらを私自身、勉強してきたし、現場でここらへんが基礎となって生きてきている。
  • 今日は具体的に「どういうことなの」と繋げていければ。


4冊に共通する価値観 (P.5)
  • 4冊の本に共通する価値観がある。
  • それは、「複雑なものは変化するし、それに立ち向かうには自分たちも変化しなければならない」ということ。
  • 完全に予測することは不可能。価値観、実践のノウハウ集としてドメイン駆動設計を活かしている。
  • オブジェクト指向とアジャイルは、片方だけでは成立しないと考えている。
  • 変更容易性を重視するなら、作るならオブジェクト指向だし、プロセスならアジャイルなのでは。
  • 同じ価値観がデザイン側とプロセス側の側面で2つあるが、同じ活動なんだろうなと思う。


オブジェクト指向の開発スタイル (P.6)
  • Interactive
    • XPとかをケント・ベックは重視している。
    • コミュニケートすることの重要性を基本に据えている。
    • 会話的にやりとりしていくことを重視している。
  • Synergy
    • Iterative、Incremental、Interactiveのシナジー効果が、複雑さに立ち向かうための狙いなのでは。
    • 相乗効果ということ。


設計原則 (P.8)
  • 設計を考えるとき、複雑さに立ち向かう根本は「モジュール性」にある。小さいものを組み合わせて構成する。
  • オブジェクト指向が考えているモジュール性と、機能分割のモジュール性は考え方が根本的に違う。凝集性も同じ。
  • なので、どちらの文脈なのか合わせないと噛み合わない。


モジュール性の基準 (P.9)
  • バートランド・メイヤーが、オブジェクト指向の狙いなどを分厚い本の中で書いてる。
  • 3章か4章でモジュール性を取り上げて重要視している。


モジュール性の評価基準 (P.10)
  • バートランド・メイヤーのモジュール性の評価基準は5つの評価軸がある。

  • 1.分解しやすい
    • まず、分解の話が最初にある。
  • 2.組み合わせやすい
    • 分解したモジュールを組み立て直すときに、他の組み立て方もできるのが良い。
    • オブジェクト指向と機能分解の大きな分かれ目がここ。
  • 3.わかりやすい
    • モジュールを見ただけで、こんなことやろうとしているんだな、とわかる。
    • 呼び出す側とかまで読まないと自分自身を理解できないというのはモジュール性として劣る。
  • 4.連続性
    • 最も重要なモジュール性の尺度。
    • 仕様の変更に対して、モジュール側の変更がばらつくのは悪いモジュール性。
    • ある1つの要求を、プログラムあちこちを直さないと保証できないのは悪いモジュール。
    • ばらつく、という言葉に意味が合って、ある時は1対1に対応しているように感じて、ある仕様に関してはあちこちに影響が及んでしまうような、共通性がないものは、不連続である。
    • ドメイン駆動設計はここをすごく意識している。
    • 同じ振幅でプログラムにも影響するのがよい連続性。
  • 5.保護性
    • 影響が波及しないように作ってあるのがよい保護性。変更が楽で安全なら、良いモジュール。


モジュール性:2つのアプローチ (P.11)
  • 1.機能分割
    • Javaはクラスを作れば、クラス単位でモジュールになる。
    • Javaで「機能クラス」と「データクラス」に分けるという考えはよくある。
  • 2.オブジェクト指向
    • 抽象データ型で作っていくのがメイヤーの考え方。こちらがキーになる。


機能分割:機能クラスとデータクラス (P.12)
  • 機能クラス、データクラス、という考え方は、C言語とかCOBOLとかを踏まえて自然的に出てきた考え方。
  • なので、getter/setterがある、エンティティビーンとセッションビーンに分ける、というスタイルが広がったのは合理的。
  • getter/setterの入れ物を宣言するのが@Entityだよ、とマニュアルに書いてあって、これが正しいという確信犯。
  • この世界でJavaを使っていたら、今日の話は違和感があるはず。


オブジェクト指向:抽象データ型 (P.13)
  • 「外から見た時、どう見えるか」、「内部でどう使うか」が一致していない作り方。


抽象データ型の例 (P.14)
  • String
    • 内部のデータ表現はcharの配列。
    • 外部からは、substringしろ、などの命令に対する結果だけを返す。
    • 内部がどういう処理ロジックで書かれているか関係なく、こうしたいんだ、といえば結果を返してくれる。
  • BigDecimal
    • 内部はBigInteger型のintValと、桁数を表すint scaleで表現される。intValはintの配列。
    • 小数の桁数は21億桁分まで持てる。
    • でも、使うときはだれもそんなこと意識していない。
    • 人間が数字を使いたい時の機能が公開メソッドとして定義されている。
  • LocalDate
    • 外から見た時は「何ヶ月前の日付を返せ」、とか、日付について人間が興味が持っていることをオブジェクトとして提供している。
    • 内部的にどうデータ持ってるかは関係ない。
  • ArrayList
    • 内部的にaddしたときに配列の要素が足りなかったら配列をもう1回作るんだけど、性能が悪くなるから複雑な処理で回避してたりするんだけど、外からはそんなことは見えない。

  • 抽象データ型は、「内部的に持っている実装と、外部的な仕様の宣言を分ける」という考え方。
  • ドメイン駆動開発もこの考え方が大事。
  • 内部のデータ表現は、ドメインオブジェクトが内部に持つ。
  • やりたいことだけシンプルに書いたメソッドを外に公開する。


モジュール性の比較 (P.15)
  • バートランド・メイヤーの比較表に、増田さんのバイアスをかけた表。
  • 「機能分割」は、分解しやすいかもしれないが、他の指標がどんどん悪化していく。
  • 「抽象データ型」は、関心ごとにモジュール化する。わかりやすさや連続性に結びついてくる。これがオブジェクト指向の良さの活かし方。


オブジェクト指向のモジュール性 (P.16)
  • この理屈を理解するのが大変。
  • オブジェクト指向らしいコードを書いてみて、変更が楽、という実体験をするのが良い。体で覚える。
  • DDD本の10章にある6パターンは、「オブジェクト指向エクササイズ 9つのルール」の別の切り口。非常に実践的で役に立つ。
  • お客さんから中盤で仕様変更が来ても、全部無駄になる、ということがない。
  • 機能分解でやってるよりも、オブジェクト指向でやってるほうがメリットが具体的なのでは。


オブジェクト指向エクササイズ (P.18)


ドメイン駆動 しなやかな設計 (P.20)
  • 「オブジェクト指向エクササイズ 9つのルール」とかに比べると抽象的だが、今日は具体的に説明する。


ドメイン駆動設計 (P.24)
  • 1~3章が基本。
  • ドメインを学んで、言葉で会話して、コードに落としてみるのが1~3章。


実際にどうやっているか (P.25)
  • asoviewさんの事例を紹介する。日本最大のレジャーサイト。
  • 初日の打ち合わせで、「レジャー」とか「プラン」などの言葉を彼らが使っていて、最初、ん?と思った。
  • 最初、ヘルプを見ると、よくある質問が書かれたFAQがあって、「会員登録」とか、いっぱい言葉が出てきていた。
  • あと、利用規約のページを見た。
  • asoviewの利用規約は良く作られていて、ドメインそのもので、キーワードが書いてあった。
  • 用語の定義があった。クラスの候補で、どんな振る舞いをしたらいいのかが示唆されていた。
  • ヘルプ利用規約で、ドメインの知識をすごく学ぶことができた。
  • asoviewさんは、ビジネスとシステム開発が近い。
  • 社長の天野さんは、「システム開発はお客さんのためだ」と言い切っている。
  • ドメイン駆動設計にチャレンジしたいのならasoviewさんはおすすめ。
  • 経営者が「システムが価値を生み出して欲しい」とストレートに要求してくる。


全体図+用語の洗い出し (P.27)
  • ヘルプ利用規約を読んだだけでなく、ホワイトボードに書き出した。
  • 時間的には、30分よりもうちょっとかけたくらい。


言葉を関連付けた初期のクラスのラフスケッチ (P.28)
  • その手書きを整理して初期のクラスのラフスケッチを作った。
  • このへんでドメインエキスパートと話しながら、言葉を使いながら、その言葉がぴったりくるのか、怪訝な顔されるのかを見る。
  • 会話をしながら、言葉の関連付けとか関連線の妥当性をチェックしていくのが、ドメインを学ぶということ。


コードに書いてみる (P.29)
  • 次に、コードに書いてみた。
  • 初日にクラス図をすぐ書いてみた。


とりあえずのひな型:8点セット (P.30)
  • まずPlanがあって、詳細クラス(PlanDetail)を作る。
  • 予約する行為があるので、プランの検索条件(PlanCriteria)がオブジェクトで、それでプランに一覧(Plants)を作って、と。
  • DBに予約するとかするから、リポジトリ(PlanRepository)を作って。
  • あまり考えずに、雛形のコードはぱっと書く。どんどん後で変更していけばいい。
  • 変えていくという前提で、雛形のクラスは作って始めたほうがクイックスタートできる。
  • 大体アプリ作るときは、いきなりこの程度はコードを書き始めて、頭がごちゃごちゃしてきたら、スケッチしてみて、コードを書き直す。
  • コードドリブン。


これらについて「名前」を探す (P.32)
  • 「ひと、もの、こと」という関心があって、それに対し識別の情報とか数量とか説明とか状態とか、性状(aspect)を表現する。
  • 契約とか規則というパターンがあって、「ひと、もの、こと」の関係を捉えることでビジネスルールが詳細化される。
  • 約束が履行された、されなかったはビジネスの根幹なので、記録と参照がある。
  • あと通知しあって送信しあう、とか。
  • こういうマップでオブジェクトを見つけに行ったりとか、分野ごとによく使うクラスの構造パターンが経験値としていくつかあって、それを引き出しにオブジェクトを見つけに行く。
  • とくに人。「もの」とか「こと」と違って、意志がある。特別な関心事。


関心事:3つの対象/6つの関係 (P.33)
  • 6つの関係(ヒト⇔モノ、モノ⇔コト、コト⇔ヒト、ヒト⇔ヒト、モノ⇔モノ、コト⇔コト)を書く。
  • 「この関係が抜けてるからうまくいかないんじゃないか?」のように見る。


性状(aspect):知りたいこと (P.35)
  • 知りたいこと、関心ごとを表す値。
  • 1個1個が文字列型とか数値型なので、それを包んだオブジェクトをつくる。


whyの設計パターン (P.36)
  • 契約
    • 複数の事業主体が約束する。コミットメントする。双方向の約束事。
    • そういうオブジェクトを捉えてモデリングする。
  • 内部的な約束だと、「いつまでに何をする」として、それまでにアクションして、やった、中断した、などがずらっと並んで、それをトラッキングするのがドメインをうまくドライブしていくこと。
  • 重要なのはポリシーとか方針、といった規則。
  • ポリシーはルールの集合体。ステータスがどうなったとき、とか、ルールがだんだん複雑になってくる。
  • 過去の経験を引っ張り出しながらクラスを使っていく。最初は生の言葉を使って、あとから名前を変えていく。
  • 最初からいい名前を見つけようとすると大変なので、最初は借りの名前で設計していく。
  • ドメインの一般化されたパターンであれば、どっかに当てはまるんだなぁ。


ドメインオブジェクトの見つけ方:まとめ (P.38)
  • 語彙を増やす
  • 関連付ける
  • 初日からコードを書く
    • クラスの雛形を書く。中身は書かないので最初はスカスカ。


ドメイン知識を記述する (P.40)
  • ドメインの知識を記述したとき、データもモデルも一体化する。
  • 中身の実態は、突き詰めればプログラミング言語で扱える基本データ型に対するロジックを書くことになる。
  • これをどういう単位でモジュール化するかが大事。


オブジェクト指向エクササイズ (P.41)
  • ここで登場するのがオブジェクト指向エクササイズ。
  • 3.すべての基本データ型をラップする
    • 1つのstringを取り出して、それをラップする。すべての基本データ型をラップする。
    • この3.と7.「1つのクラスにインスタンス変数は2つまで」の両方を実践したとき、相当小さいオブジェクトがたくさんできる。
    • ことごとくオブジェクトになる。それがオブジェクト思考のモジュール性を活かす良いアプローチなんだ。
    • ここまでやったほうが、今まで見えなかったオブジェクト指向の良さが見えてきたのでおすすめ。


getterを使わない (P.42)
  • 9.「getter/setterを使わない」で、特にgetterを使わないのが、オブジェクトをどこに置くか、というのに関わる示唆に富んだルール。
    • getterを使わないということは、内部に持ってるstringを他のオブジェクトが入手して加工計算できないということ。
    • 他のオブジェクトではなく、自分自身に計算とかをやらせるのが基本。


主役は値オブジェクト (P.43)
  • 加工とかはそのオブジェク自身に担当させる。
  • 名前をうまくつけることで、ロジックを引き寄せる。
  • 「あ、これはあそこにおいといたほうがいいな」と、名前とロジックが洗練される。
  • たくさんオブジェクト作ると雑然とすると思いきや、重複が激減するようになった。
  • ちょっとした式はどこでも書けちゃうのでちらばりやすいが、オブジェクトに持たせると、変更になったとき、そのオブジェクトだけ直せばいい。

ドメイン知識の置き場所 (P.44)
  • 文字列を持ったところに判断ロジックや加工ロジックが同居する。
  • そのロジックを外から呼び出す。

ドメイン駆動 しなやかな設計 (P.45)
  • 「意図の明確なインタフェース」というパターン名は、判断とかするメソッドはわかりやすくしようね、ということ。
  • 3.の「表明」バートランド・はメイヤーの契約設計。
  • 6.の「閉じた操作」は、メソッドが返す型、受け取る型を自分と同じ型にする。


ドメインオブジェクトの作り方 (P.46)
  • 重要なのは、公開メソッドを少数に限定して、メソッド名で意図を明確にすること。
  • 整数は21億扱えるけど、そんなに使わない。なので1000に制限したオブジェクトを新しくつくる。
  • やりたいことだけやるメソッドにする。範囲を限定する。
  • 数字や日付は、不正な計算とか例外が発生しやすい処理ができてしまうので、事前条件とか事後条件をいかに明確にするかが大事。
  • まずはnullはもどさない、渡さないことにすることで、if文がいらなくなる。
  • 渡しても役に立たないんだから、それをルールにすることで安定する、というのがメイヤーの考え方。
  • チームでルールにしよう。
  • 約束をいかに明確にしておけるか。
  • 表明を使ったり例外を使ったりしてやったりするが、明確にしましょう。


値オブジェクトの設計 (P.48)
  • インスタンス変数は生成時に設定する。
  • setterを置かずに不変にする。
  • getterを持たない。自分で加工してから渡す。


コード例: UserIdentify (P.51)
  • ユーザを識別するUserIdentityクラス。
  • メールアドレスでユーザを識別しよう。
  • stringでアドレスを持たせる。
  • 完全コンストラクタにするため、アノテーションでnullを渡せないようにする。


コード例: DateOfBirth (P.52)
  • 日付をLocalDateで見るようにする。メインで持ちたいデータはこれ。
  • 入力値を保持する変数sourceと、有効だったのかを保持するisValidというメソッドとフラグvalidを持つ。
  • LocalDateはなんでも入ってしまうので、型で制限する。
  • 日付でデータを保持するが、本当に注目すべきは年齢だったりする。
  • 年齢の処理もDateOfBirthに持たせるとごちゃごちゃするので、Ageクラスに任せる。


形式的な隔離 (P.55)
  • パッケージはレイヤー化アーキテクチャどおり4階層(application, domain, infrastructure, presentation)で分けてる。
  • すべてがドメインオブジェクトを使うようになっている。ドメイン層ではモデルが機能を使うようになってる。


形式的な隔離:お約束 (P.57)
  • 雛形があって、Spring Bootだと、コントローラは@controllerとか、アノテーションで書いておくと、書くべきロジックがなくなってスカスカになる。
  • お約束のパターンでさくっと書ける。
  • ドメインはバリデーションのアノテーションを使って、あとはクラス単位にはほとんどアノテーション使わない。
  • 上の4つ(コントローラ、アプリケーション、データソース、データマッパー)は、ControllerでSpringが面倒みてくれるので、ほとんどコード書かなくて良いはず。
  • でも上の4つにifとかロジックが入ってくると、ドメインロジックが流出している。
  • ドメイン層以外はできるだけシンプルに、お約束だけで済ませるべき。


オブジェクトの世界/フラットな世界 (P.59)
  • ドメインの世界はグラフ構造。末端の値オブジェクトが実際の基本データを持ってる。
  • テーブルとか画面とかjsonはグラフを嫌う。なので基本データ型を並べるリスト構造となる。


ドメインロジックをほんとうに隔離する (P.60)
  • ドメイン層をオブジェクト指向でやる、というのは左のグラフ型を作るということ。
  • 右のフラット構造に左のグラフ構造が引っ張られると、制約が出てくるし道を外す。
  • 右はフレームワークやマッパーにまかせて頑張る。左が右に引っ張られてはならない。
  • jsonに合わせたコードを作るほうが楽だが、それだと右に引っ張られた状態になってしまう。ここはこだわらないといけない。
  • 左と右を分離できるなら、そのあいだのインタフェースを多少頑張るのは仕方ない。


フレームワークの都合を排除する (P.61)
  • MyBatisは、nullはマッピングされないので注意。sqlでnullで帰ってきたものはマッピングされない。
  • MyBatisとSpringは同じ挙動になるよう設定している。

ドメインオブジェクトの育て方のシナリオ (P.65)
  • 最初は雛形を作るので、スカスカな状態から始まる。
  • それじゃアプリにならないので、ロジックを追加していく。
  • 雛形に置き場所を吟味しながら書いていくのが理想的だが、1週間のスプリントで2.を吟味してやってくと間に合わない。
  • なので、納期があるので、てっとりばやく書いちゃう。
  • コードがごちゃごちゃして整理するタイミングが、ドメインが育つポイント。
  • getterは現実として書いちゃう。そこを中心にメソッドに抽出して名前を付ける。


ロジックを整理すべき兆候 (P.66)
  • ロジックを整理すべき兆候は、本に出てくる「リファクタリングの嫌な臭い」の4つ。
  • 1.コードの重複
  • 2.長いメソッド
  • 3.長いクラス
    • オブジェクト指向エクササイズ的には、50行を超えたらアウト。
    • valueオブジェクトに分けて行くと小さくなる。6行とかにならない。5行以内。
  • 4.引数の多さ
    • 引数は2個以上渡す必要があるなら、ドメインの置き場所がずれているんじゃないか、と思ったほうがいい。


メソッドの抽出 (P.68)
  • 空行があったら、その単位でメソッドに抽出できるかを考える。
  • 改行を入れるのは楽だが、メソッドに抽出するとなると名前を考えないといけないので大変だが、ここを頑張ることで、新しい発見に繋がる。
  • いい名前が見つからない場合は、改善したくなる。そこでドメインの知識と語彙がだんだん増えてくる。
  • いい名前が見つからないでも抽出することで、学びが得られる。
  • いい名前が見つからないからといって残したままにすると、何も学びは得られない。
  • 丁寧に地道にやっていくと、ロジックの移動がごく自然にできるようになってくる。


クラスの抽出とメソッドの移動 (P.69)
  • インスタンス変数は2つまで。増えたら分けましょう。
  • グループを見つけたらクラスに抜いて、そこに加工などのロジックを移動させる。
  • クラスに抜き出したあとにメソッド名を考えるほうが、名前が見つからない状態で名前を考えるより楽。


一時変数を値オブジェクトに (P.70)
  • ここまでやるのは大変かもしれない。
  • 式を見つけたら、ぽんっとオブジェクトにして、それに何かやらせるようにする。
  • そうやると、加速度的にいいオブジェクトが見つかる。


長いメソッドをオブジェクトに (P.71)
  • メソッドをほぐしてオブジェクトを分解しやすくする。


区分オブジェクト (P.79)
  • 料金区分はenumで宣言しておく。
  • if文なしで金額計算ができるようになる。


振る舞いを豊かにするメソッド候補 (P.82)
  • スカスカの状態から振る舞いを豊かにするメソッド候補。
  • Javaの標準ライブラリにあるようなメソッドもある。
  • 型としては、こういう挙動を持つべき、というhaskellの思想を参考にしている。


第10章 しなやかな設計:6つのパターン (P.84)
  • 正直言ってわかりにくい。抽象度が高い。
  • 6つのパターンは、チェックリストとしてはよく使える。
  • 「概念の輪郭」が、業務の関心事と一致しているかいつも関心を持って欲しい。
  • 「独立したクラス」は、いろんなjarをかき集めないと動かない、というのはダメということ。


ドメイン駆動設計への道 (P.92)
  • DDDができない理由をおっしゃる方がいるが、ドメイン駆動設計へ進む気があるなら、XP本のケントベックの言葉を伝えたい。
    • どんな状況でも改善できる
    • どんなときでも「あなた」から改善を始められる
    • どんなときでも「今日」から改善を始められる
  • どんなときでもドメインの言葉で話すことはできる。だれも止めることはない。
  • どんな時でもドメインの言葉をコードに反映できる。
  • コードを書くことが仕事である以上、コードにドメインを反映できるチャンスがある。
  • 明日からでもできる。


Q&A



Q&A 1


Q.
ドメインオブジェクトを見つけたタイミングでクラスにしていくのに2点課題があると思っている。
1.ドメインオブジェクトは最初洗練されてないので変えたくなる。後でやるとリファクタリングが大変。
2.クラスが多い時に、レビューが大変。GitHubのプルリクで何十ファイルもあると嫌がられる。

A.
2.で、レビュー体制は障害になるかもしれない。
レビューって何のためにしてるのかな。
クラスの置物を考えているだけでも、その人がどのくらいドメイン考えているか、を見られる。

1.で、リファクタリングは問題に感じたことは無い。InteliJ IDEAは最高。IDEツールにより可能になったことは素晴らしい。
どんどん実験してどんどん修正できるようになったのはIDEツールが助けてくれるようになったのがでかい。

Q&A 2


Q.
細切れにする、がテーマだったと思うが、プリミティブなものがたくさんでてくるのか。
コンテキストを超えて使いまわしたいときはコピーするのか?

A.
共通モジュールにするとメンテが大変。
なので、そのコンテキストの中で成長路線に載せたほうが楽。共通モジュールにすると費用対効果的に今では大変。

Q&A 3


Q.
モジュールの依存性があると、テストが大変だが、どうすれば。

A.
そんなに依存関係が深くなるなら、テストの前に、そこを改善すべきでは。
ドメインコードに対するテストコードは書いてない。コンパイラがOKだしたらOKとしている。
サービスレベルとかではテストする。E2Eのテストも書く。
ドメインオブジェクトを切ったテストは価値をあまり感じていない。
DDDはリファクタリンクしまくるので、テストコードを直すのも大変。
テストを否定しているわけではないが、私はそうやっている。


感想


増田さんのDDDの講演は何回も聴講してきたが、毎回新しい話があって、毎回新しい発見がある。
それだけ引き出しが多くて、経験に裏打ちされたネタが満載ということなのでしょう。

今回一番興味深かったのは、P.30の「とりあえずのひな型:8点セット」ですね。
これって、今回初公開ネタなんでしょか?
この8点セットの詳細が聞きたいなぁ、と思いました。

毎度ネタ満載で、一度スライドを読むだけでは消化しきれないので、も一回復習しよう・・・

増田さん、関係者の皆様、ありがとうございました。

-->