「DDD Alliance! ドメイン駆動設計のためのオブジェクト指向入門」に参加してきた #DDDAlliance

2015年1月21日に開催された、「DDD Alliance! ドメイン駆動設計のためのオブジェクト指向入門」に参加してきました。

ddd-alliance.connpass.com

以下から現地で書いたメモ

ドメインモデルとは

ドメインモデル = 関心事を集めたもの。サブセットと呼んでもいい

ドメイン駆動設計

ドメインを分析する人がコードを書く コードを書く人がドメインを分析する 毎日分析し、毎日コードを書く

コードを書くためにはクラスを見つけなければいけない 問題領域を分析することでクラスが見つかる 分析者 = 実装者 とならなければならない

モデルを見つけることとクラスを見つけることは一緒。人を別にしてはいけない。

ソフトウェアを立ち上げる時はフェーズと人を分けるけど、 保守フェーズでは分析と実装者が一緒。立ち上げ時もそれをやればいい。

ドキュメントを書いてからコードを書くという方法は大概無駄になる。

ドメインを学び学んだことをコードで表現することが大前提

ドメイン

ドメイン層はどこにも依存しない(プレゼン、アプリケーション、データベース) Springで実装

ドメイン層はPOJOフレームワークは導入しない。

ドメイン層のオブジェクト ドメインにGetter/Setterは使わない 業務の関心事の表現 知りたいこと 誰が、いつ、なぜ。。。 業務ルール 制約、閾値、手順。。。 業務ルールが破られた時の業務ルール

業務サービスの実行役 判断の結果 計算の結果 加工の結果 (基本データ型は返さない)

独自の型を作ることがドメイン指向設計

オブジェクト指向のメリット: コードの整理

クラス単位に関連するコードを整理する どこに何が書いてあるか特定しやすくなる 変更が飛び火しない - データと機能を分けると飛び火しやすい 変更した箇所のテストがOKなら全体もOK

アンチパターン:変更コストが高くなる設計

ふるまいを持たない、いわゆるデータクラス

データクラスがレイヤーを跨いで使われている

どこで使われているか特定しにくくなる

変更コストが下がる設計

役に立つクラスを作る(判断・加工・計算ロジックを持たせる) クライアントは使うだけ

オブジェクト指向ドメイン層の構造

主要な関心事をアグリゲートに組む

オブジェクト指向アーキテクチャ

構造

オブジェクトの集合体

強く関連する部品は集約する

アグリゲート同士は分散性を高め必要最小限につなげる

「変更」を前提にしたネットワーク構造

メタファ:LANアグリゲートとインターネット(全体)

工法

「変更」を前提にする

インクリメンタルに開発

少しずつ成長する全体

毎日変化させていく

オブジェクトの設計

独自の型を設計

オブジェクトのコラボレーションを設計

実現手段の隠蔽

独自の型を設計する

ドメイン層は独自の型だらけにする

独自の方=利用者の関心事

基本データ型=コンピュータの関心事

オブジェクトのコラボレーションの設計

契約による設計、責任駆動設計、擬人化。。。

結局は「オブジェクト」と「オブジェクト」が協力して設計することが大事

コラボレーションの設計原則

オブジェクト同士の契約(顧客と供給者の関係)を意識する

相手のことを深く知りすぎてはいけない

書籍:契約による設計

「契約による設計」

契約ってなんだ?技術者にはいいメタファじゃないよね

契約はビジネスルールの関心事。業務ルールの基本。

業務知識を学べば業務ロジックも契約による設計も学べて美味しい

オブジェクト指向:実現手段の隠蔽

Java標準の(コンピュータよりの)メソッドを徹底的に隠す

業務の関心事の表現手段として「不適切」

int string LocalDate => 値オブジェクトで包む

for while List => ファーストコレクションで包む

列挙型でswitchを減らす

事例の研究

「転職支援サービス」を例に

転職アドバイザ

求職者

求人

応募

スカウト

職務依頼

ドメイン層の隔離

初級

フレームワークと規約 貧血ドメインモデル

中級

ドメインロジックを(プレゼン層、アプリケーション層などから)ドメイン層に移動する

浅いリファクタリング

上級

知識豊富になったドメイン層の見通しを良くする

深いリファクタリング

主要な関心事(コア)の隠蔽

主要な関心事(コア)の抽象化

各層の実装

プレゼンテーション層

標準的なSplingの機能を使う

サービスをインジェクションする

サービス層

渡されたIDに対してデータをリポジトリから取得する

ドメインの型を受け取り、ドメインの型を返す

データソース層

SQL Mapperのインジェクション

SQLとオブジェクトをマッピング

ドメインの型を返す

メトリクス

ざっくり20ヶ月、100人月くらい

アグリゲートに対して10くらいのドメイン(平均なのであくまでイメージ)

ドメイン層のクラスの行数はだいたい100行くらいで収まっている

30行〜60行がほとんど

クラス数が1000~2000あっても、関心事が集約されているのでたいして困らない

ドメインオブジェクト「善い部品」

ドメイン駆動設計のEntityパターン:参照オブジェクト

Entityと呼ぶのではなく参照オブジェクトと呼んだ方がいい

主要な関心事への参照点

顧客との会話の語彙の起点となる

JPAの@Entity

ただのデータの入れ物クラス

テーブルと対応する

ドメイン駆動のEntityとは無関係(むしろ逆に位置する)

値オブジェクト

業務の関心事と基本語彙(日付、期間、数量、金額、単位...)

目的に特化して制約をかける

暗黙の業務ルールをコードで表現する

コードを読んで業務ルールがわかるようにする

ロジックを集約する

コードの重複がなくなり変更が楽になる

値オブジェクトの実装パターン

完全コンストラクタ

不変なオブジェクト

何もしないgetterはNG

例えば、求人者の転職回数というのは今後関心事となることが予想される。そういったものを値オブジェクト化するのは有効。

値オブジェクトの効果

コードにドメインの言葉が増える => コードが仕様を語れる

一覧オブジェクト(ファースクラストコレクション)

ListやSetをラップ

コレクションの操作はコードがごちゃごちゃしやすく、変更の副作用が多い

クラスとして独立させ、そのクラスに閉じ込める

一覧オブジェクト vs SQL問い合わせ

SQLの抽出条件を単純にし、微妙な抽出を一覧オブジェクトにやらせる選択肢もある

どちらのやり方が関心事をうまく表せられるか

メモリとトレードオフ

区分オブジェクト

場合ごとのルールを理解し表現する道具

列挙を有効活用しifやswitchを削減する(ポリモフィズム)

区分オブジェクトの効果

関心事の明示的なコード表現

複雑なif文の削除

どこに何があるかわかりやすくなる

区分の追加や削除時の変更の副作用が少ない

How より What でコードを記述する

業務の関心事を明示的に表現する

より業務に寄せたメソッド名にする

深い洞察に向かうリファクタリング

お客さんの要望からやりたいこと、関心事を洞察し、コードに反映させるようにリファクタリングをしていく

関心事をより強調されるようにドメインオブジェクトを捻出していく

オブジェクト指向設計の学び方

本やスライドで用語を学ぶ

実際にコードを書く

元の本やスライドを読みなおす

変更作業が学びの絶好のチャンス

既存コードを変更しながらコードを読んだ範囲、変更箇所の数、テストの範囲を意識

オブジェクト指向設計に書き換えてから変更してみる

変更の際にリファクタリング

既存のコードの設計と比較して成功を実感する

ドメイン駆動設計とは

ドメインを学び、学んだことをコードで表現すること

QA

Q: リポジトリトランザクションやロックするの難しくない?
A: 最近はトランザクションなしでの開発にチャレンジしている(現場はトランザクション使う)

Q: リポジトリから特定の値オブジェクトだけを取るのはあり?
A: 業務として必要であるのであれば全然あり。(ただし業務として成立している場合)

Q: コンストラクタは隠した方が良いか?
A: 隠したほうが良い。ファクトリメソッドで生成する考え方は推奨。

Q: DDDによって開発スピードが落ちないか?
A: ビジネスががらっと変わることはなく、既存のサービスをどんどん成長させていくケースがほとんどで、
ビジネスと開発に大きなスピードのギャップは生まれない。
開発スピードがでないのであれば、それを判断材料にオブジェクト指向を習得していくべき。

Q: ドメインエキスパートが不在、もしくはまだビジネス未成熟の場合どうすれば?
A: ドメインエキスパートなんてものは存在しない!ビジネスの全てを知っている人はいないので、技術者が積極的にキャッチしにいくべき。ドメインの知識をためていく。