Upload
-
View
5.677
Download
0
Embed Size (px)
Citation preview
拙い設計
• コードが重複しまくっている • 条件分岐の密林があちこちにある • どこに何が書いてあるかわからない • 変更した時にどこで何が起きるか推測できない
• やっていることは解読できるが、なぜ、そこでその処理が必要か意味がわからない
• パッケージ名/クラス名/メソッド名/変数名/コメントが嘘だらけ
• でも動いてる
動いていなければ、ろくでもない設計であることは誰でもわかるのに… 4
善い設計
• コードの重複がなくなる
• 条件分岐の密林がなくなる
• どこに何が書いてあるかわかりやすくなる
• 変更した時に影響範囲を限定しやすくなる
• 処理の意図がすぐわかる
• パッケージ名/クラス名/メソッド名/変数名がわかりやすく正確になる
– コメントはむしろノイズ
5 ドメイン駆動設計を実践すると、自然にこうなっていく
オブジェクト指向
インクリメンタルな設計
データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ
「設計不要」でもなく「上流工程で設計する」でもない世界へ
(JPA,Active Record,Entity Framework…)
技術的にはこの2つ越境が、ドメイン駆動設計を実践するブレークポイント
7
二つはコインの裏表
ドメイン駆動設計の学習曲線と 2つのブレークポイント
オブジェクト指向設計
インクリメンタルな設計
パラダイムシフトに3ヵ月から半年くらい
実プロジェクトでいっしょにやりながら パラダイムシフトに半年から数年
8
オブジェクト指向
9
判断/加工/計算の ロジックを整理する技法
ロジックとデータをまとめて 一つのプログラミング単位にする
人間にわかりやすい単位に プログラムを分割して整理する
クラス インスタンス変数
メソッド
パッケージ クラス メソッド 名前
わかったつもりでもなかなかできない オブジェクト指向らしいロジックの整理のしかたを体で覚える
インクリメンタルな設計
10
最初から良い設計は手に入らない
コードを書いて動かしてみれば わかることがたくさんある
経験から学んだことを反映して 設計の改善を続ける
仕様の抜け漏れ 要求の勘違い 拙い設計
毎日、 知識を広げ 理解を深め コードに反映
どうしてもきれいな答えを最初から見つけたくなる 設計を少しずつ改善するスピード感を体で覚える
ドメイン駆動設計の手ごたえ
• 対象領域の業務を学びながらソフトウェア開発する面白さ
• ソフトウェアが育っていく楽しさ
• 対象領域の知識とコードが一致している時のコードのわかりやすさ
• 変更が楽で安全になる気持ちよさ
• 正しい仕事をしている安心感
12
ドメイン駆動設計のハードル
• 発想の転換 – 「動いた」駆動から「善い設計」駆動へ
– 「技術」駆動から「ドメイン」駆動へ
• 「善い設計」の判断基準の変化
• 設計・開発手法の切り替え –オブジェクト指向へ
– インクリメンタルな設計へ
• 実装の制約との戦い
– フレームワークやプログラミング言語の制約 13
ここらへんの 実践経験を紹介します
基本の活動
ドメインの オブジェクト モデル
利用者の 「重要な関心事」を 鋭く説明する
選び抜かれた 重要な関心事を コードで表現する
会話を繰り返して 「要点」を選び抜く
重要な「言葉」の解釈を チームで合意する
1章 2章 3章
16
第1章
ドメインの知識をかみ砕く 第3章
モデルと実装を結びつける
第2章
言葉を使った意図の伝達
リレーションシップ駆動 要件分析(RDRA)
・コンテキストモデル ・業務フローモデル ・イベント/状態モデル
ICONIX
・ドメインオブジェクトの発見 ・ドメインモデルの育て方 ・ロバストネス分析
問題領域を把握する技法
初期のドメインモデルを 2時間以内で描く
モデル間を関連づけて 整合性と網羅性を確保 ユースケースと実装の
ギャップを埋める 20
ドメイン駆動の開発プロセス
業務フロー
モデルの改良
要約、骨格
画面・帳表 ユースケース
属性の追加 モデルの洗練
イベント 状態遷移
データモデル
初期の ラフスケッチ
コンテキスト図
手がかかり
ドメインオブジェクトの設計と実装
ロバストネス分析
必要ならシーケンス図
操作追加
Java ソース
基盤クラス追加
DDL/SQLソース
問題領域の把握
構造化用語集
22
最初にやっていたやり方
• モデルの作成に時間をかけた – モデリングツールの利用(Enterprise Architect) – クラス図 – ユースケース記述 – ロバストネス図 – ER図
• 慣れて来たら、なんでもかんでも図にすることはなくなった – 複雑になるところだけ – しっかり設計したいところだけ
• 設計ドキュメントの作成が一段落してからコードを書いていた 23
汚いコードと格闘しながら いろいろ勉強してみた
25
オブジェクト指向 エクササイズ
インクリメンタルな設計
抽象データ型 契約による設計 シームレスな開発
コードで意図を 表現する
既存のコードの 設計を改善する
ドメインロジックの 設計パターン
言葉たいせつ モデルと実装の一致
通読した本は一冊もない そのかわり何回も何十回も拾い読みしている
• オブジェクト指向がだんだん体になじんできた – 人間の関心事の表現と、ロジックの整理の単位(クラス)が一致してきた
• インクリメンタルな設計の良さがだんだんわかってきた
• ドメイン駆動設計って、もともとそういう本だよね、そういえば – オブジェクト指向ソフトウエアの開発者向けに書いた
– 設計と開発プロセスの議論の基礎としてエクストリームプログラミングを使用する
26
オブジェクト指向
インクリメンタルな設計
データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ
「設計不要」でもなく「上流工程で設計する」でもない世界へ
(JPA,Active Record,Entity Framework…)
ドメイン駆動設計を支える技法
27
二つはコインの裏表
ドメイン駆動設計の学習曲線と 2つのブレークポイント
オブジェクト指向設計
インクリメンタルな設計
パラダイムシフトに3ヵ月から半年くらい
実プロジェクトでいっしょにやりながら パラダイムシフトに半年から数年
28 私自身は、孤独な戦いの中で、ここまでに数年かかった
9つのルール
1. 一つのメソッドでインデントは一段階 2. else 句は使わない 3. すべてのプリミティブ型と文字列をラップする 4. 一行につきドットは1つまで 5. 名前を省略しない 6. すべてのエンティティを小さくする 7. 一つのクラスのインスタンス変数は2つまで 8. ファーストクラスコレクションを使う 9. getter, setter, プロパティを使わない
36
名前をつける
1. 一つのメソッドでインデントは一段階 2. else 句は使わない 3. すべてのプリミティブ型と文字列をラップする 4. 一行につきドットは1つまで 5. 名前を省略しない 6. すべてのエンティティを小さくする 7. 一つのクラスのインスタンス変数は2つまで 8. ファーストクラスコレクションを使う 9. getter, setter, プロパティを使わない
37
パッケージ クラス メソッド 小さい単位で 名前をつける
データとロジックを凝集させる
1. 一つのメソッドでインデントは一段階 2. else 句は使わない 3. すべてのプリミティブ型と文字列をラップする 4. 一行につきドットは1つまで 5. 名前を省略しない 6. すべてのエンティティを小さくする 7. 一つのクラスのインスタンス変数は2つまで 8. ファーストクラスコレクションを使う 9. getter, setter, プロパティを使わない
38
値オブジェクト
ファーストクラス コレクション
データとロジックを別の場所におかない
複数の関心事を持たない
1. 一つのメソッドでインデントは一段階 2. else 句は使わない 3. すべてのプリミティブ型と文字列をラップする 4. 一行につきドットは1つまで 5. 名前を省略しない 6. すべてのエンティティを小さくする 7. 一つのクラスのインスタンス変数は2つまで 8. ファーストクラスコレクションを使う 9. getter, setter, プロパティを使わない
39
ルールに違反している時は、複数の関心事が混在している
オブジェクト指向エクササイズ
9つのルールで、1000行くらい書いてみると オブジェクト指向らしい設計を体感できる
既存のコードから9つのルールの適用例を探し、 値オブジェクトやファーストクラスコレクションを 実際に作ってみると before/afterでオブジェクト指向設計を体感できる
40
設計の改善
• 名前の変更
–業務の関心事と実装を一致させる
• メソッドの抽出
–手続きに名前をつける
• クラスの抽出
–手続きのグループに名前を付ける
• メソッドの移動/フィールドの移動
–データとロジックの置き場所を同じクラスにする
43
リファクタリング
既存のコードで、いやな臭いを見つける
これを繰り返しているうちに ・いやな臭いに敏感になる ・最初から短いメソッド/小さなクラスを書きはじめる
リファクタリングを適用して 設計改善のbefore/afterを体感する
44
レイヤ構造
• 三層構造ではなく、 三層+ドメイン層
• O-Rマッピング
–データクラス+機能クラス(手続き型)になりがちなツールは避ける
• JPA, Active Record, Entity Framework、…
–オブジェクトが主役の軽量のツールを選ぶ
• myBatis, Spring JDBCTemplate, …
46
プログラミング言語
• 静的な型付き言語 • 独自の型の定義
– 標準ライブラリの型(クラス)は汎用的すぎる – 目的特化の型をどんどん作る
• 型名を明記して意図を表現する – メソッドの返す型、メソッドの引数の型 – インスタンス変数の型 – ローカル変数の型
• 型の設計変更に強い – リファクタリングを楽に安全に
47
基本方針
• ドメインオブジェクトをそのまま使う
• 画面
– ドメインオブジェクトをHTMLにマッピング
– HTTPリクエストをドメインオブジェクトにマッピング
– Spring MVC/Thymeleaf
• API
– JSON<-> ドメインオブジェクトの自動マッピング
– Spring MVC RestController / Jackson
50
簡単にマッピングできない時
• ビュークラスを間にはさむ • 出力
– ビュークラスのコンストラクタにドメインオブジェクトを渡す
• 入力 – 入力データからビュークラスを生成する
– そのビュークラスにドメインオブジェクトのファクトリメソッドを用意する
• ビュークラスは判断/加工/計算のロジックを持つ – DTOではない
• 安易にビュークラスを導入しない – ドメインの関心事の表現(ドメインオブジェクト)とインタフェースが不一致なのはおかしい
51
基本方針(構造)
• アプリケーションクラスにデータソースクラスをインジェクションする
• Repositoryインタフェースを使って、データソース層のクラスを隠ぺいする @Service class OrderService {
@Autowired OrderRepository repository; void register( Order order ) {
repository.register(order);
}
}
53
基本方針
• 軽量の O-R マッピングを使う ドメイン層をO-Rマッピングの仕組みで汚染しない – myBatis – Spring JDBCTemplate
• JPAなど重量級のツールを使う場合 – データの入れ物クラス(@Entity)は、データソース層のクラスとして宣言する
– データソース層で、ドメインオブジェクトと@Entityクラスを変換する
– ドメインオブジェクトには、O-R マッピングのアノテーションを持ち込まない
54
関心事を表現する基礎部品 ドメインオブジェクトの設計パターン
• 値オブジェクト
• 識別オブジェクト
• ファーストクラスコレクション
• 区分オブジェクト
• How ではなくWhatを表現する
• パッケージ
57
値オブジェクト
• 業務の関心事の基本語彙
–日付、期間、数量、金額、単位、名称、備考、…
• ロジックの置き場所
– インスタンス変数として一つか二つの基本データ型(文字列/数値/日付)を持つ
–そのインスタンス変数を使った、判断・計算・加工のロジックを一箇所に集める
– コードの重複がなくなり、変更が楽になる
59
値オブジェクト
• ドメイン固有のString
– PersonName, MailAddress, Telephone, …
• ドメイン固有のint/long/BigDecimal
– Money, Quantity, Rate, …
• ドメイン固有のLocalDate
– DueDate, ExpireDate, DateRange, …
データの用途を明確にする(ドメインの言葉に一致させる 扱うデータの範囲を業務に合わせて限定する
データの扱い方(メソッド)を業務に合わせて限定する 60
値オブジェクト
String List<String>
BigDecimal Integer …
LocalDate Long
起算日 InitialDate 期限 DueDate 有効期間 ValidTerm
金額 Money 数量 Quantity 単位 Unit
品名 ProductName 備考 Remarks 摘要 Abstract
言語で用意された「型」 (汎用)
独自に定義した「型」 (目的特化)
61
値オブジェクトの設計
• 完全コンストラクタ
– すべてのインスタンス変数は、生成時に設定
• 不変
– setter を書かない(状態を変えない)
– 値を変更する時は、別のオブジェクトを生成して返す
– 不変による「安定」
• ロジックの置き場所
– インスタンス変数を使った、判断・加工・計算
– 何もしないで素のデータを返す getter はNG
62
識別オブジェクト
• 関心事を特定する • 人は何で特定するか
– 名前 – 生年月日 – メールアドレス …
• コンピュータは何で特定するか – 一意の管理番号 – equals()
• 両方の目的に使えるオブジェクトを作る
64
ファーストクラスコレクション
• ListやSetをラップしたドメインオブジェクト • PurchaseHistory
• MailBox
• SkillSet
• 「一覧」や「履歴」という関心事の表現
– 「一覧」は利用者の関心事が集中する場所
– 「一覧」の議論は、重要な関心事の発見の良い機会
• コレクションの操作はコードがごちゃごちゃしやすく、変更の副作用が多い
• クラスとして独立させ、そのクラスに閉じ込める 66
ファーストクラスコレクション
コレクション List<Item>だけを持つクラス コレクション操作のロジックを集める場所 外部にコレクションを公開しない 正しい状態を保証する
コレクション操作のロジックがあちこちのクラスに散らばらない/重複しない Item の件数制限や、合算方法に変更があっても他のクラスに波及しない Itemの一覧画面に関する変更は、まずここを見れば良い
ロジックを集める感覚を体で覚える 関心事とクラスを一致させる感覚を体で覚える 67
区分オブジェクト
• 振る舞いを持った Enum • Java言語使用に組み込まれた
Strategy/Stateパターン +
• 区分ごとのロジックを多態で記述
場合ごとのビジネスルールの表現
69
区分オブジェクトの効果
• 複雑な if文記述の解消
–区分ごとの分岐記述( if文,switch文 )は一箇所になる
–場合によっては、まったく書かなくてよい
• どこに何が書いてあるかわかりやすくなる
–区分ごとのロジックをクラス単位で表現
• 区分の追加や削除をした時の、変更の副作用が少ない(範囲を限定しやすい)
70
expireDate.add(-1);
expireDate.previousDay();
expireDate.dayOfFinalAlert ();
業務要件:期限切れの前日にアラートメールを送る
How より What 業務の関心事を明示的に表現する
72
課金ポリシー
適用する()
シーズン料金
レート()
夏料金
レート()
冬料金
レート()
料金
計算() <<interface>>
計算方法(How)を表現
料金区分(What)をクラスで表現
計算()メソッドに埋もれ、 暗黙化する業務知識 シンプルな設計に見えるが、 ルールの変更・追加のたびに 計算()メソッドが肥大化し、 if 文が増殖する
業務知識をそのまま、クラスとして表現 複雑に見えるが、ルール変更・追加が、 楽で安全になる
ロジックをクラスで表現する
73
パッケージ
• 利用者の関心事の「境界」を表現する手段
–関連するドメインオブジェクト(の型)のグループ
–プログラミング単位
–テスト単位
• 新しい発見とともに、積極的にパッケージ名/パッケージ構成を変更し育てていく
• 「第4部 戦略的設計」の主要ツール
75
パッケージ図を使った ロジックの置き場所の俯瞰
76
与信限度額を考慮するとどうなるか? 発注と仕入れまで視野を広げるとどうなるか? 矢印で示した一方向の依存関係で正しく実装しているか/実装できるか?
サービスの会社が 業務システムに取り組む話
• アソビュー
• 全国のレジャー・遊び・体験が探せる日本最大級の検索・予約サイト
• バックオフィス業務は必要最小限、かつ人間系でがんばっているが…
• 事業のさらなら発展のためには • バックオフィスのシステム充実が必須 • ただし、 • ビジネスモデルや業務フローは変わり続ける
79
電子チケットサービス
• アソビューの挑戦
–金流の変更
• before : 主催会社にお金が入り、手数料を請求
• after: アソビューにお金が入り、主催会社に支払い
–バックオフィス業務の複雑化
–確実性や迅速性の要求
–バックオフィスシステムの開発経験不足
82
ドメイン駆動設計
ドメインの オブジェクト モデル
利用者の 「重要な関心事」を 鋭く説明する
選び抜かれた 重要な関心事を コードで表現する
会話を繰り返して 「要点」を選び抜く
重要な「言葉」の解釈を チームで合意する
1章 2章 3章
85
第1章
ドメインの知識をかみ砕く 第3章
モデルと実装を結びつける
第2章
言葉を使った意図の伝達
インクリメンタルに設計する
ざくっとラフスケッチをしてみる
コードで書いて、動かしてみる
リファクタリングをする
行き詰まったら 絵に描いてみる
会話をしてみる
結果をコードに反映する
そうやって設計を改善し続ける
それがもっとも確実で効率的
87
インクリメンタルな設計
• このくらいの状態でコードを書き始める
• 基本オブジェクトでロジックの置き場所を決める – 値オブジェクト
– ファーストクラスコレクション
– 区分オブジェクト
– 識別オブジェクト
• アーキテクチャはテンプレートを使う – Spring Boot
– 基本のレイヤ構造 presentation/application/domain/infrastructure
93
ラフスケッチには登場しない ドメインオブジェクトの例
• Rate – 値オブジェクト – 料率の計算ロジック
• Amount – 値オブジェクト – 金額の四則演算
• DateRange – 値オブジェクト – 有効期間の設定と判定
• ValidType – 区分オブジェクト – チケットの状態区分と有効性の判定
最初から コードで書いて 成長させている
97
オブジェクト指向
インクリメンタルな設計
データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ
「設計不要」でもなく「上流工程で設計する」でもない世界へ
(JPA,Active Record,Entity Framework…)
ドメイン駆動設計を支える技法
101
二つはコインの裏表