ドメイン駆動設計のための
オブジェクト指向入門
2016年1月21日 増田亨(@masuda220)
有限会社システム設計 代表
ギルドワークス株式会社 取締役
1
本日の内容
• 基礎知識
– ドメイン駆動設計
– オブジェクト指向設計
• 事例「転職支援サービス」
– システムの概要
– ドメイン層の隔離 初級・中級・上級
– ドメインオブジェクト 「善い部品」
– より深い洞察に向かうリファクタリング...
基礎知識
ドメイン駆動設計
オブジェクト指向設計
3
ドメイン層のソフトウェア設計
オブジェクト指向設計
インクリメンタルな設計(XP)
ドメイン駆動設計
4
ドメイン駆動設計
ドメイン
モデル
ドメインの
「重要な関心事」を
鋭く説明する
選び抜かれた
重要な関心事を
コードで表現する
会話を繰り返して
「要点」を明確にする
「重要な語彙」を
チームで合意する
1章
2章
3章
5
第1章
ドメイン...
ドメイン駆動設計
ドメインを分析する人がコードを書く
毎日、分析し、毎日、コードを書く
「会話」をしながら意図を確認する
コードを書く人がドメインを分析する
分析・設計・実装を「フェーズ」に分けない
分析・設計・実装の「担当者」を別にしない
「...
ドメインを学び
学んだことを
コードで表現する
ドメイン駆動設計
7
オブジェクト指向/XP
ドメインの隔離
ドメインの知識の置き場所
ソフトウェアの中核
他の3層に依存しない
4章
8
実装
業務の知識の置き場所
ソフトウェアの中核
他の3層に依存しない
9
ドメイン層のオブジェクト
• Plain Old Java Object
– Not Plain Old Java Beans (getters/setters)
• 業務の「関心事」の表現
– 知りたいこと(誰が、いつ、なぜ、…)
– 業務ル...
オブジェクト指向:コードの整理
• 「クラス」単位に関連するコードを整理する
• どこに何が書いてあるか特定しやすくなる
• 変更があちこちに飛び火しない
– データクラスと機能クラスを分けると、変更が飛び
火しやすくなる
• 変更した箇所のテ...
アンチパターン
変更コストが高くなる設計
機能クラスAと、機能クラスBに、同じデータを扱うロジックが重複する
変更時に、読む範囲が広がり、変更箇所が増え、テスト範囲が広がる
12
ビュー
コントローラ
サービス
データアクセス
…
ビュー
コン...
変更コストが下がる設計
判断・計算・加工のロジックを一箇所に集める
変更時に、一箇所を読み、一箇所を変更し、一箇所テストする
13
オブジェクト指向:ドメイン層の構造
集約(アグリーゲート)が開発単位
・分析+設計+実装
× 画面単位の分析・設計・実装
× 機能単位の分析・設計・実装
14
オブジェクト指向:アーキテクチャ
• 構造
– オブジェクトの集合体
– 強く関連する部品は集約する(アグリゲート)
– アグリゲート同士は、分離性を高め、必要最小限につなげる
– 「変更」を前提にしたネットワーク構造
– メタファ : LAN...
オブジェクトの設計
独自の「型」を設計する
オブジェクトの「コラボレーション」を設計する
実現手段(how)を隠ぺいする
16
オブジェクトの設計
独自の「型」を設計する
• 「型」
– クラス/インタフェース
– オブジェクトの種類
– 関心事の分類
– メソッドの引数
– メソッドの戻り値
• ドメイン層は「独自の型」だらけにする
– 「独自の型」=利用者の関心事
...
オブジェクトの「コラボレーション」の設計
責任駆動設計
コラボレーション
共同作業
協調動作
メッセージ・
パッシング オブジェクト
インタラクション
オブジェクト
コミュニケーション
役割分担
擬人化
相互作用
契約による設計SRP GRAS...
「コラボレーション」の設計原則
• お互いが対等の立場
• 双方向の権利と義務、その明文化
• 一時的な関係(契約期間)
• 相手を詳しく知りたがってはいけない
契約による設計
Design by Contract
by バートランド・メイヤー...
「契約による設計」
理解するには、現実世界の「契約」の知識が必要。
技術者にはあまり良いメタファーではない。
一方、ビジネスでは「契約」は重要な関心事。
業務ルールの基本でもある。
「ドメイン駆動設計」をやるなら「契約」を勉強しよう。
「業務知...
参考:契約
21
契約書
雇用契約
売買契約
利用規約
保険約款
雇用者 被雇用者
買い主 売り主
契約の成立 引き渡し
支払
請求権
所有権
移転違約
係争
返品
返金 保証期間
申込
撤回
契約行為
免責
16章 責務のレイヤ
・意思決定
...
オブジェクトの設計
実現手段(how)を隠ぺいする
if/switch
for/while
List, Set, Map
Stream
Optional
int,short,long
float,double
char
byte
boolean...
ドメイン層では
実現手段(how)を隠ぺいする
• int/String/LocalDateは「値オブジェクト」で包む
• for/while/List/Set/Map/Stream/Optional は、特別な
クラス(「ファーストクラスコレ...
事例の研究
「転職支援サービス」
アーキテクチャ
コード例
メトリクス
24
転職支援サービス
25
アーキテクチャ
26
ドメイン層の隔離
初級 中級 上級
フレームワークと規約
web/service/model/
datasource …
誰でもできる
貧血ドメインモデル
ドメインのロジックを
プレゼンテーション層/
サービス層/データソース
層からはがして、...
コード例
求人情報(Offer)
28
アーキテクチャ
29
30
プレゼンテーション層
OfferController.java
@Controller
@RequestMapping("/offers/{offerId}")
public class OfferController {
@Autowired...
サービス層
OfferService.java
@Service
public class OfferService {
@Autowired
OfferRepository repository;
public Offer findBy(Of...
データソース層
OfferDatasource.java
@Repository
public class OfferDataSource implements OfferRepository {
@Autowired
OfferMapper ...
34
ドメイン層
Offer.java
public class Offer {
OfferIdentifier id = new OfferIdentifier();
OfferTitle title = new OfferTitle();
Des...
ドメイン層
OfferRepository.java
public interface OfferRepository {
Offer findBy(OfferIdentifier id);
OfferSummaries listOfOffer...
メトリクス
クラス数とか行数とか
37
転職支援サービス
38
アーキテクチャ
39
40
Thymeleaf
(HTMLフレンドリー)
MyBatis
(SQL フレンドリー)
メトリクス
項目 求職者 転職アドバイザ
画面種類(HTMLテンプレート)
(内、登録系)
193
(47)
260
(46)
プレゼンテーション層 @Controller 128 96
サービス層 @Service クラス
更新系機能(メソッ...
ドメイン層のクラスの行数
クラス数
行数
30行から60行で7割
10行以下はインタフェース宣言
42
Offer 集約(抜粋)
43
語彙が1000語くらい
では混乱しない
関連づけながら
覚えるから
語彙の整理
44
それぞれの集約の内部の語彙を、
一度に知る必要はないから
語彙が1000語くらい
では混乱しない
ドメインオブジェクト
「善い部品」
参照オブジェクト
値オブジェクト
ファーストクラスコレクション
振る舞いを持つ区分定数
how より what
45
ドメインオブジェクトの設計
(おさらい)
• 独自の「型」を設計する
– コードの整理の単位
– 知識の表現の手段
• オブジェクトの「コラボレーション」の設計
– 役割分担
– 協力関係
• 実現手段(how)を隠ぺいする
– Java 言語...
参照オブジェクト
ドメイン駆動設計の Entity パターン
47
集約のルート オブジェクトのコラボレーションの元締め
主要な関心事への参照点
顧客との会話の語彙の起点
参照オブジェクト vs @Entity
• 参照オブジェクト(ドメイン駆動設計のEntity)
– 集約のルート
– 業務の主たる関心事の表現手段
• JPA の @Entity
– データの入れものクラス
– テーブルと対応
• 名前
• フ...
値オブジェクト
String
List<String>
BigDecimal
Integer
…
LocalDate
Long
起算日 InitialDate
期限 DueDate
有効期間 ValidTerm
金額 Money
数量 Quan...
値オブジェクト
• 基礎部品:業務の関心事の基本語彙
– 日付、期間、数量、金額、単位、名称、備考、…
• 目的に特化して制約をかける
– String, BigDecimal, LocalDate, … 汎用のデータ型をラッピ
ングする
– ...
値オブジェクトの実装パターン
• 完全コンストラクタ
– すべてのインスタンス変数は、生成時に設定
• 不変
– setter を書かない(状態を変えない)
– 値を変更する時は、別のオブジェクトを生成して返す
– 不変による「安定」
• ロジ...
実装例
52
public class NumberOfChangingJob {
@NotNull(message = "転職回数を選択してください。", groups = Default.class)
@Range(min=0, max=1...
実装例
53
public class DateOfBirth {
Integer year = LocalDate.now().getYear() - 35;
Integer month;
Integer day;
public LocalD...
値オブジェクトの効果
• コードにドメインの言葉が増える
(暗黙の概念が明示的に)
– × String getName( Long id )
– ◎ PersonName fullName(CustomerNumber id)
• コードの重...
一覧オブジェクト
(ファーストクラスコレクション)
• ListやSetをラップしたドメインオブジェクト
• Offers
• MailBox
• SkillSet
• 「一覧」や「履歴」という関心事の表現
– 「一覧」は利用者の関心事が集中す...
一覧オブジェクト vs. SQL 問合せ
• SQLの抽出条件を単純にして、微妙な抽出や
加工は、一覧オブジェクトにやらせる、という
選択肢もある
• どちらのやり方がドメインの関心事をうまく表
現できるか
• 変更があった時に、どちらが楽で安...
区分オブジェクト
• 振る舞いを持った Enum
• 区分ごとのロジックを別クラスに記述
• Java言語使用に組み込まれた
Strategy/Stateパターン
• if 文/switch文を書かない工夫
説明とコード例は、googleで
「...
実装例
58
interface PayAmount {
Yen amount();
String name();
String description();
}
//それぞれのクラスでインタフェースを実装する
class DeadAmount...
列挙を使った実装例
59
enum PaymentType{
dead( new DeadAmount() ),
retired( new RetiredAmount() ),
normal( new NormalAMount() );
pri...
区分オブジェクトの効果
• 関心事の明示的なコード表現
• 複雑な if文記述の解消
– 区分ごとの分岐記述( if文 or switch文 )は、一箇所に
なる
– 場合によっては、まったく書かなくてよくなる
• どこに何が書いてあるかわかり...
expireDate.add(-1);
expireDate.previousDay();
expireDate.dayOfFinalAlert ();
業務要件:期限切れの前日にアラートメールを送る
How より What
業務の関心事を明示...
課金ポリシー
適用する()
シーズン料金
レート()
夏料金
レート()
冬料金
レート()
料金
計算() <<interface>>
計算方法(How)を記述
料金の用語(What)をクラスで表現
計算()メソッドに埋もれ、
暗黙化する業務...
深い洞察に向かう
リファクタリング
63
8章から13章
リリース後に発生したちょっとした改善
要望: スカウトの開封/未開封が見づらい
修正: メール状況で「未読」の場合、赤表示にした
64
増田 亨 (管理)
増田 亨 (管理)
変更したコード
+.mail-status-unread {
+ color: #990000;
+ font-weight: bold;
+}
css/custom.css
-<td th:text="${mailSummary.mailSt...
ちょった待った
ドメイン層で
この関心事は
どう表現している?
66
メールの状態
public enum MailStatus {
未読,
既読,
返信済み,
未選択;
public boolean unread() { return this == 未読; }
public boolean opened() ...
スカウトメール一覧
public class ScoutMailSummary {
MailId mailId;
MailStatus mailStatus;
ScoutStatus scoutStatus;
MemberForAgencySu...
改善のアイデア
public class ScoutMailSummary {
ScoutMailStatus mailStatus;
ScoutStatus scoutStatus;
MemberForAgencySummary receiv...
学び方のヒント
70
オブジェクト指向設計の学び方
言葉⇒体験⇒言葉
本やスライドで用語を学ぶ
実際にやってみる(コードを書く)
元の本やスライドを読み直す
オブジェクト指向の「用語」はわかりにくい
コードで書いてみて、「もう一度」、説明を読み直すと
わからなかった...
成功を体験する
「変更作業」が学びのチャンス
• 既存コードを変更しながら以下を意識する
– コードを読んだ範囲
– 変更箇所の数
– テストが必要な範囲
– 副作用の有無(安心感)
• オブジェクト指向設計に書き換えてから変更してみる
– 「...
成功体験をするための
推薦図書
• リファクタリング
• 実装パターン
– オブジェクト指向の効果を引き出す
コードの書き方の実践ノウハウ
• 技術評論社 Software Design 再編集版 (3月発行予定)
– オブジェクト指向の「入門...
参考資料
• オブジェクト指向エクササイズのススメ
– http://www.slideshare.net/yojik/ss-1033616
• 責務を持ったオブジェクトの「コラボレーション」の設計を、わ
かりやすく学べる
• オブジェクト指向...
ドメイン層のソフトウェア設計
オブジェクト指向設計
インクリメンタルな設計(XP)
ドメイン駆動設計
75
ドメインを学び
学んだことを
コードで表現する
ドメイン駆動設計
76
オブジェクト指向/XP
ドメイン駆動設計
ドメイン
モデル
ドメインの
「重要な関心事」を
鋭く説明する
選び抜かれた
重要な関心事を
コードで表現する
会話を繰り返して
「要点」を明確にする
「重要な語彙」を
チームで合意する
1章
2章
3章
77
第1章
ドメイ...
ドメイン駆動設計
ドメインを分析する人がコードを書く
毎日、分析し、毎日、コードを書く
「会話」をしながら意図を確認する
コードを書く人がドメインを分析する
分析・設計・実装を「フェーズ」に分けない
分析・設計・実装の「担当者」を別にしない
「...
Upcoming SlideShare
Loading in...5
×

ドメイン駆動設計のためのオブジェクト指向入門

136
-1

Published on

DDD Alliance 勉強会 2016-1-21 @東京

Published in: Software
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
136
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

ドメイン駆動設計のためのオブジェクト指向入門

  1. 1. ドメイン駆動設計のための オブジェクト指向入門 2016年1月21日 増田亨(@masuda220) 有限会社システム設計 代表 ギルドワークス株式会社 取締役 1
  2. 2. 本日の内容 • 基礎知識 – ドメイン駆動設計 – オブジェクト指向設計 • 事例「転職支援サービス」 – システムの概要 – ドメイン層の隔離 初級・中級・上級 – ドメインオブジェクト 「善い部品」 – より深い洞察に向かうリファクタリング • 学び方のヒント – 言葉⇒体験⇒言葉 – 推薦図書/参考資料 2
  3. 3. 基礎知識 ドメイン駆動設計 オブジェクト指向設計 3
  4. 4. ドメイン層のソフトウェア設計 オブジェクト指向設計 インクリメンタルな設計(XP) ドメイン駆動設計 4
  5. 5. ドメイン駆動設計 ドメイン モデル ドメインの 「重要な関心事」を 鋭く説明する 選び抜かれた 重要な関心事を コードで表現する 会話を繰り返して 「要点」を明確にする 「重要な語彙」を チームで合意する 1章 2章 3章 5 第1章 ドメインの知識をかみ砕く 第3章 モデルと実装を結びつける 第2章 言葉を使った意図の伝達
  6. 6. ドメイン駆動設計 ドメインを分析する人がコードを書く 毎日、分析し、毎日、コードを書く 「会話」をしながら意図を確認する コードを書く人がドメインを分析する 分析・設計・実装を「フェーズ」に分けない 分析・設計・実装の「担当者」を別にしない 「コード」と動くソフトウェアで意図を確認する 分析と実装を、同じ人が毎日やるなら、ドキュメントは不要 オブジェクト指向/XP 6 保守の現場ではあたりまえ?
  7. 7. ドメインを学び 学んだことを コードで表現する ドメイン駆動設計 7 オブジェクト指向/XP
  8. 8. ドメインの隔離 ドメインの知識の置き場所 ソフトウェアの中核 他の3層に依存しない 4章 8
  9. 9. 実装 業務の知識の置き場所 ソフトウェアの中核 他の3層に依存しない 9
  10. 10. ドメイン層のオブジェクト • Plain Old Java Object – Not Plain Old Java Beans (getters/setters) • 業務の「関心事」の表現 – 知りたいこと(誰が、いつ、なぜ、…) – 業務ルール(制約、しきい値、手順、…) – 業務ルールが破られた時の業務ルール(制限、通知、…) • 業務サービスの実行役 – 「判断」の結果を返す – 「計算」の結果を返す – 「加工」の結果を返す – (基本データ型は返さない) 10 業務上の「判断」「計算」「加工」
  11. 11. オブジェクト指向:コードの整理 • 「クラス」単位に関連するコードを整理する • どこに何が書いてあるか特定しやすくなる • 変更があちこちに飛び火しない – データクラスと機能クラスを分けると、変更が飛び 火しやすくなる • 変更した箇所のテストOKなら全体もOK 11
  12. 12. アンチパターン 変更コストが高くなる設計 機能クラスAと、機能クラスBに、同じデータを扱うロジックが重複する 変更時に、読む範囲が広がり、変更箇所が増え、テスト範囲が広がる 12 ビュー コントローラ サービス データアクセス … ビュー コントローラ サービス データアクセス …
  13. 13. 変更コストが下がる設計 判断・計算・加工のロジックを一箇所に集める 変更時に、一箇所を読み、一箇所を変更し、一箇所テストする 13
  14. 14. オブジェクト指向:ドメイン層の構造 集約(アグリーゲート)が開発単位 ・分析+設計+実装 × 画面単位の分析・設計・実装 × 機能単位の分析・設計・実装 14
  15. 15. オブジェクト指向:アーキテクチャ • 構造 – オブジェクトの集合体 – 強く関連する部品は集約する(アグリゲート) – アグリゲート同士は、分離性を高め、必要最小限につなげる – 「変更」を前提にしたネットワーク構造 – メタファ : LAN(アグリゲート)と INTERNET(全体) • 工法 – 「変更」が前提だから • インクリメンタル(+1)に開発する • 少しずつ成長する全体 • 毎日変化する全体 – いつでもすぐに変更できるように • 分析・設計・実装で「フェーズ」を分けない • 分析・設計・実装を同じ人間が担当する • 毎日、分析・設計・実装をする 15
  16. 16. オブジェクトの設計 独自の「型」を設計する オブジェクトの「コラボレーション」を設計する 実現手段(how)を隠ぺいする 16
  17. 17. オブジェクトの設計 独自の「型」を設計する • 「型」 – クラス/インタフェース – オブジェクトの種類 – 関心事の分類 – メソッドの引数 – メソッドの戻り値 • ドメイン層は「独自の型」だらけにする – 「独自の型」=利用者の関心事 – 基本データ型=コンピュータの関心事 17 5章 モデルの構成要素 9章 概念を掘り出す 10章 概念の輪郭
  18. 18. オブジェクトの「コラボレーション」の設計 責任駆動設計 コラボレーション 共同作業 協調動作 メッセージ・ パッシング オブジェクト インタラクション オブジェクト コミュニケーション 役割分担 擬人化 相互作用 契約による設計SRP GRASP どれもわかりにくい言葉だが、基本は同じことをいっている 「オブジェクト」と「オブジェクト」が協力して仕事をする 18 ご近所 (neigborhood) 集約 (aggregate) 集団 (cluster)
  19. 19. 「コラボレーション」の設計原則 • お互いが対等の立場 • 双方向の権利と義務、その明文化 • 一時的な関係(契約期間) • 相手を詳しく知りたがってはいけない 契約による設計 Design by Contract by バートランド・メイヤー 影響: ワーフスブラック 「オブジェクトデザイン」 ケントベック 「XP」「リファクタリング」「実装パターン」 エリックエヴァンス 「ドメイン駆動設計」 … 19 注意! assert 構文が契約による設計ではない
  20. 20. 「契約による設計」 理解するには、現実世界の「契約」の知識が必要。 技術者にはあまり良いメタファーではない。 一方、ビジネスでは「契約」は重要な関心事。 業務ルールの基本でもある。 「ドメイン駆動設計」をやるなら「契約」を勉強しよう。 「業務知識」の学習と、「契約による設計」の学習で 二度おいしい。 20
  21. 21. 参考:契約 21 契約書 雇用契約 売買契約 利用規約 保険約款 雇用者 被雇用者 買い主 売り主 契約の成立 引き渡し 支払 請求権 所有権 移転違約 係争 返品 返金 保証期間 申込 撤回 契約行為 免責 16章 責務のレイヤ ・意思決定 ・ポリシー ・確約 ・業務
  22. 22. オブジェクトの設計 実現手段(how)を隠ぺいする if/switch for/while List, Set, Map Stream Optional int,short,long float,double char byte boolean ==, >=, <= +, - String StringBuilder LocalDate Java 言語仕様 Java API null こういうものを ドメイン層で「表だって」 使ってはいけません。 徹底的に隠しましょう。 業務の関心事の表現手段として「不適切」 特に public なメソッド(=業務知識の表現)の 引数の「型」、戻り値の「型」として使わない。 配列 22
  23. 23. ドメイン層では 実現手段(how)を隠ぺいする • int/String/LocalDateは「値オブジェクト」で包む • for/while/List/Set/Map/Stream/Optional は、特別な クラス(「ファーストクラスコレクション」)で包む • 「列挙型」(enum)で、if/switchを減らす • Bean Validation で、if を減らす • null/Optional を渡さない • null/Optional を返さない • 基本データ型を渡さない • 基本データ型を返さない • コレクション/配列を渡さない • コレクション/配列を返さない コード例は後ほど紹介 23
  24. 24. 事例の研究 「転職支援サービス」 アーキテクチャ コード例 メトリクス 24
  25. 25. 転職支援サービス 25
  26. 26. アーキテクチャ 26
  27. 27. ドメイン層の隔離 初級 中級 上級 フレームワークと規約 web/service/model/ datasource … 誰でもできる 貧血ドメインモデル ドメインのロジックを プレゼンテーション層/ サービス層/データソース 層からはがして、ドメイン 層に「移動」する 浅い「リファクタリング」 知識豊富になったドメイン層の 「見通し」を良くする 深い「リファクタリング」 「コア(主要な関心事)」の隔離 「コア(主要な関心事)」の抽象化 27 4章から7章 9章から12章 8章、13章、15章
  28. 28. コード例 求人情報(Offer) 28
  29. 29. アーキテクチャ 29
  30. 30. 30
  31. 31. プレゼンテーション層 OfferController.java @Controller @RequestMapping("/offers/{offerId}") public class OfferController { @Autowired OfferService offerService; @ModelAttribute("offer") public Offer offer(@PathVariable("offerId") Integer id) { return offerService.findBy(new OfferIdentifier(id)); } @RequestMapping(value = "", method = RequestMethod.GET) public String show( @ModelAttribute("offer") Offer offer, Model model) { return "offers/offer"; } } 仕事番号を含むURL サービスのインジェクション Offerアグリゲートの取得 HTMLテンプレート名 ( offer.html ) 31
  32. 32. サービス層 OfferService.java @Service public class OfferService { @Autowired OfferRepository repository; public Offer findBy(OfferIdentifier id) { return repository.findBy(id); } public OfferSummaries listOf(Criteria criteria) { return repository.listOfOfferSummary(criteria); } } 検索条件 ドメインの「型」を渡し、ドメインの「型」を返す 基本データ型を使わない 32 リポジトリのインジェクション
  33. 33. データソース層 OfferDatasource.java @Repository public class OfferDataSource implements OfferRepository { @Autowired OfferMapper mapper; @Override public Offer findBy(OfferIdentifier id) { return mapper.findBy(id); } } SQL Mapperのインジェクション interface OfferMapper { Offer findBy(OfferIdentifier id); } <select id="findBy" resultMap="offer"> SELECT offer.offer_id, offer.title, ... </select> DAO自動生成のおまじない SQLと Offerオブジェクトのマッピング定義 ドメインの「型」を返す 33
  34. 34. 34
  35. 35. ドメイン層 Offer.java public class Offer { OfferIdentifier id = new OfferIdentifier(); OfferTitle title = new OfferTitle(); Description description = new Description(); Features features = new Features(); EmploymentType employmentType = new EmploymentType(); Location location = new Location(); Holiday holiday = new Holiday(); AnnualSalaryRange annualSalaryRange = new AnnualSalaryRange(); CompanySummary company = new CompanySummary(); ... } Offer アグリゲートのルート 35
  36. 36. ドメイン層 OfferRepository.java public interface OfferRepository { Offer findBy(OfferIdentifier id); OfferSummaries listOfOfferSummary(Criteria criteria); } public class OfferSummaries { int totalCount; List<OfferSummary> values; … } public class OfferSummary { OfferIdentifier identifier = new OfferIdentifier(); OfferTitle title = new OfferTitle(); Description description = new Description(); Location location = new Location(); } 求人一覧用の要約アグリゲート OfferSummaries.java 求人一覧(ファーストクラスコレクション) OfferSummary.java 36
  37. 37. メトリクス クラス数とか行数とか 37
  38. 38. 転職支援サービス 38
  39. 39. アーキテクチャ 39
  40. 40. 40 Thymeleaf (HTMLフレンドリー) MyBatis (SQL フレンドリー)
  41. 41. メトリクス 項目 求職者 転職アドバイザ 画面種類(HTMLテンプレート) (内、登録系) 193 (47) 260 (46) プレゼンテーション層 @Controller 128 96 サービス層 @Service クラス 更新系機能(メソッド数) 参照系機能(メソッド数) 105 (90) (188) 100 (78) (173) ドメイン層のクラス数 内リポジトリ数(=アグリゲートの種類) クラス数/アグリゲート(単純平均) 1,110 (89) (11) 956 (75) (11) データソース層 @Repository + Mapper interface 185 174 SQL Map定義(SQLテンプレート) 79 77 テーブル数 303 ざっくり20か月、100人月くらい41
  42. 42. ドメイン層のクラスの行数 クラス数 行数 30行から60行で7割 10行以下はインタフェース宣言 42
  43. 43. Offer 集約(抜粋) 43 語彙が1000語くらい では混乱しない 関連づけながら 覚えるから
  44. 44. 語彙の整理 44 それぞれの集約の内部の語彙を、 一度に知る必要はないから 語彙が1000語くらい では混乱しない
  45. 45. ドメインオブジェクト 「善い部品」 参照オブジェクト 値オブジェクト ファーストクラスコレクション 振る舞いを持つ区分定数 how より what 45
  46. 46. ドメインオブジェクトの設計 (おさらい) • 独自の「型」を設計する – コードの整理の単位 – 知識の表現の手段 • オブジェクトの「コラボレーション」の設計 – 役割分担 – 協力関係 • 実現手段(how)を隠ぺいする – Java 言語仕様の隠ぺい ( int, String, if, for , .. ) – Java API の隠ぺい ( Collection, Stream , … ) 46 5章 モデルの構成要素 9章 概念を掘り出す 10章 概念の輪郭
  47. 47. 参照オブジェクト ドメイン駆動設計の Entity パターン 47 集約のルート オブジェクトのコラボレーションの元締め 主要な関心事への参照点 顧客との会話の語彙の起点
  48. 48. 参照オブジェクト vs @Entity • 参照オブジェクト(ドメイン駆動設計のEntity) – 集約のルート – 業務の主たる関心事の表現手段 • JPA の @Entity – データの入れものクラス – テーブルと対応 • 名前 • フィールド構成(カラム構成) • 基本データ型レベルでのマッピング – 業務の関心事と一致しない • テーブルは「データの整理」という関心事に引っ張られる • 実装手段(基本データ型)が丸見えになりがち – 変更コストがあがる • 機能クラスが別にできて、そちらでコードが重複しがち • データモデルとオブジェクトモデルのギャップを埋めるコードが変更の障害 48
  49. 49. 値オブジェクト String List<String> BigDecimal Integer … LocalDate Long 起算日 InitialDate 期限 DueDate 有効期間 ValidTerm 金額 Money 数量 Quantity 単位 Unit 品名 ProductName 備考 Remarks 摘要 Abstract 言語で用意された「型」 (汎用) 独自に定義した「型」 (目的特化) 49
  50. 50. 値オブジェクト • 基礎部品:業務の関心事の基本語彙 – 日付、期間、数量、金額、単位、名称、備考、… • 目的に特化して制約をかける – String, BigDecimal, LocalDate, … 汎用のデータ型をラッピ ングする – 値の範囲、文字種、形式を業務要件に沿って制約する – 制約による「安定」 • ロジックの集約 – そのデータを使う判断・計算・加工のロジックを一箇所に集 める – コードの重複がなくなり、変更が楽になる 50
  51. 51. 値オブジェクトの実装パターン • 完全コンストラクタ – すべてのインスタンス変数は、生成時に設定 • 不変 – setter を書かない(状態を変えない) – 値を変更する時は、別のオブジェクトを生成して返す – 不変による「安定」 • ロジックの置き場所 – インスタンス変数を使った、判断・加工・計算 – 何もしないで素のデータを返す getter はNG 51
  52. 52. 実装例 52 public class NumberOfChangingJob { @NotNull(message = "転職回数を選択してください。", groups = Default.class) @Range(min=0, max=10, message="転職回数は0から10の間で入力してください。") Integer value = 0; … } public class PersonName { @NotEmpty(message = "名を入力してください。") String firstName = ""; @NotEmpty(message = "姓を入力してください。") String lastName = ""; public String fullName() { return String.format("%s %s", lastName, firstName); } } 転職回数は、その分野の専門家の微妙な関心事…
  53. 53. 実装例 53 public class DateOfBirth { Integer year = LocalDate.now().getYear() - 35; Integer month; Integer day; public LocalDate getValue() { return LocalDate.of(year, month, day); } @AssertTrue(message = "生年月日を入力してください") public boolean isNotEmpty() { if (year == null || month == null || day == null) return false; return true; } public String getAgeText() { return String.format("%s歳", getAge()); } } 年齢は、その分野の専門家の微妙な関心事…
  54. 54. 値オブジェクトの効果 • コードにドメインの言葉が増える (暗黙の概念が明示的に) – × String getName( Long id ) – ◎ PersonName fullName(CustomerNumber id) • コードの重複を防ぐ – データを使う判断・加工・計算のロジックを、データを 持つクラスに集約して、一元管理する • コードが安定する – 値の範囲や文字種を用途に合わせて限定 (不正データが混入しない) – 状態が変わらない(いつ参照しても同じ結果) 54 9章 概念を掘り出す 10章 概念の輪郭
  55. 55. 一覧オブジェクト (ファーストクラスコレクション) • ListやSetをラップしたドメインオブジェクト • Offers • MailBox • SkillSet • 「一覧」や「履歴」という関心事の表現 – 「一覧」は利用者の関心事が集中する場所 – 「一覧」の議論は、重要な関心事の発見の良い機会 • コレクションの操作は、コードがごちゃごちゃしやす く、変更の副作用が多い – クラスとして独立させ、そのクラスに閉じ込める 55 9章 概念を掘り出す 10章 概念の輪郭
  56. 56. 一覧オブジェクト vs. SQL 問合せ • SQLの抽出条件を単純にして、微妙な抽出や 加工は、一覧オブジェクトにやらせる、という 選択肢もある • どちらのやり方がドメインの関心事をうまく表 現できるか • 変更があった時に、どちらが楽で安全か • CPU/通信/メモリのコストダウンによって、適 用できる範囲が広がってきている 56
  57. 57. 区分オブジェクト • 振る舞いを持った Enum • 区分ごとのロジックを別クラスに記述 • Java言語使用に組み込まれた Strategy/Stateパターン • if 文/switch文を書かない工夫 説明とコード例は、googleで 「場合わけの書き方あれこれ」で検索、 または「ソフトウェアデザイン9月号」 場合ごとのルールを理解し表現する道具 57
  58. 58. 実装例 58 interface PayAmount { Yen amount(); String name(); String description(); } //それぞれのクラスでインタフェースを実装する class DeadAmount implements PayAmount {...} class RetiredAmount implements PayAmount {...} class NormalAmount implements PayAmount {...} class Client { private PayAmount amount ; Yen getAmount() { return amount.getAmount(); } } 「給付金額」を知りたいクライアント側のクラス
  59. 59. 列挙を使った実装例 59 enum PaymentType{ dead( new DeadAmount() ), retired( new RetiredAmount() ), normal( new NormalAMount() ); private PayAmount amount; private PaymentType( Payamount amount ) { this.amount = amount; } Yen getAmount() { return amount.getAmount(); } } PaymentType type = PayamountType.valueOf("dead"); ... Yen amount = type.getAmount(); 区分ごとの生成も if文やswitch文は不要
  60. 60. 区分オブジェクトの効果 • 関心事の明示的なコード表現 • 複雑な if文記述の解消 – 区分ごとの分岐記述( if文 or switch文 )は、一箇所に なる – 場合によっては、まったく書かなくてよくなる • どこに何が書いてあるかわかりやすくなる – 区分ごとの業務ルールや知識のロジックの置き場所を クラス単位で分離 • 区分の追加や削除をした時の、変更の副作用が 少ない 60 9章 概念を掘り出す 10章 概念の輪郭
  61. 61. expireDate.add(-1); expireDate.previousDay(); expireDate.dayOfFinalAlert (); 業務要件:期限切れの前日にアラートメールを送る How より What 業務の関心事を明示的に表現する 61
  62. 62. 課金ポリシー 適用する() シーズン料金 レート() 夏料金 レート() 冬料金 レート() 料金 計算() <<interface>> 計算方法(How)を記述 料金の用語(What)をクラスで表現 計算()メソッドに埋もれ、 暗黙化する業務知識 シンプルな設計に見えるが、 ルールの変更・追加のたびに 計算()メソッドが肥大化し、 if 文が増殖する 業務知識をそのまま、クラスとして表現 複雑に見えるが、ルール変更・追加が、 楽で安全になる How より What 62 9章 概念を掘り出す 10章 概念の輪郭
  63. 63. 深い洞察に向かう リファクタリング 63 8章から13章
  64. 64. リリース後に発生したちょっとした改善 要望: スカウトの開封/未開封が見づらい 修正: メール状況で「未読」の場合、赤表示にした 64 増田 亨 (管理) 増田 亨 (管理)
  65. 65. 変更したコード +.mail-status-unread { + color: #990000; + font-weight: bold; +} css/custom.css -<td th:text="${mailSummary.mailStatus.name()}">返信済み </td> message/list.html +<td th:text="${mailSummary.mailStatus.name()}" th:classappend="${mailSummary.mailStatus.unread()}? 'mail-status- unread'">返信済み </td> mailStatus#unread()は作ってあったので、ドメイン層は変更しなかった 65
  66. 66. ちょった待った ドメイン層で この関心事は どう表現している? 66
  67. 67. メールの状態 public enum MailStatus { 未読, 既読, 返信済み, 未選択; public boolean unread() { return this == 未読; } public boolean opened() { return this == 既読; } public boolean replied() { return this == 返信済み; } public boolean unknown() { return this == 未選択; } ... } MailStatus.java メール全体で「共通」の状態管理用のenum 「スカウトメール」に特化していない 要望は「未読/既読」の2択 ギャップがある 67 要望: スカウトの開封/未開封が見づらい
  68. 68. スカウトメール一覧 public class ScoutMailSummary { MailId mailId; MailStatus mailStatus; ScoutStatus scoutStatus; MemberForAgencySummary receiver; Agent sender; Subject subject; EvaluationType evaluationType; CountOfTotalAgencySend total; SendDate sendDate; ... } ScountMailSummary.java 一覧表示用の情報が、平坦にならんでいる 「スカウトメール」の「未読/既読」が埋もれている 68
  69. 69. 改善のアイデア public class ScoutMailSummary { ScoutMailStatus mailStatus; ScoutStatus scoutStatus; MemberForAgencySummary receiver; Agent sender; Subject subject; MailId mailId; SendDate sendDate; EvaluationType evaluationType; CountOfTotalAgencySend total; ... } ScountMailSummary.java 使った人からのフィードバックから、ドメインを学習し、 学んだことをコードに反映する 「スカウトメール状態」クラスを新規作成 段落で、別格の関心事であることを表現 画面に表示しない(関心事が低い)情報を格下げ 69
  70. 70. 学び方のヒント 70
  71. 71. オブジェクト指向設計の学び方 言葉⇒体験⇒言葉 本やスライドで用語を学ぶ 実際にやってみる(コードを書く) 元の本やスライドを読み直す オブジェクト指向の「用語」はわかりにくい コードで書いてみて、「もう一度」、説明を読み直すと わからなかった「用語」がわかるようなっていることが多い 71
  72. 72. 成功を体験する 「変更作業」が学びのチャンス • 既存コードを変更しながら以下を意識する – コードを読んだ範囲 – 変更箇所の数 – テストが必要な範囲 – 副作用の有無(安心感) • オブジェクト指向設計に書き換えてから変更してみる – 「変更」の前にリファクタリング • 名前の変更、メソッドの抽出、クラスの抽出、メソッドの移動 • 「善い部品」が自然に増える • 既存コードの設計と比較して、変化を実感する – コードを読む範囲 – 変更箇所の数 – テストが必要な範囲 – 副作用の有無(安心感) 72
  73. 73. 成功体験をするための 推薦図書 • リファクタリング • 実装パターン – オブジェクト指向の効果を引き出す コードの書き方の実践ノウハウ • 技術評論社 Software Design 再編集版 (3月発行予定) – オブジェクト指向の「入門記事」を集めたムック本 • オブジェクト指向入門 – 読むのは「そうとう」たいへん – 良いことがたくさん書いてある – なんど読んでも新しいヒントが見つかる • (もちろん)ドメイン駆動設計 – なんど読んでも新しいヒントが見つかる 73 注意! 「入門」ではない ハードルが高すぎる
  74. 74. 参考資料 • オブジェクト指向エクササイズのススメ – http://www.slideshare.net/yojik/ss-1033616 • 責務を持ったオブジェクトの「コラボレーション」の設計を、わ かりやすく学べる • オブジェクト指向の設計と実装の学び方 – http://www.slideshare.net/masuda220/ss-14263541 • 「学び方」に焦点をあてた、オブジェクト指向設計入門 • 創造的な学習のコツ 「学習パターン」 – http://learningpatterns.sfc.keio.ac.jp/ • 「学び」のコツ • 「ドメイン」を学ぶためのヒント満載 • 「ドメイン駆動設計」や「オブジェクト指向設計」の学びにも 74
  75. 75. ドメイン層のソフトウェア設計 オブジェクト指向設計 インクリメンタルな設計(XP) ドメイン駆動設計 75
  76. 76. ドメインを学び 学んだことを コードで表現する ドメイン駆動設計 76 オブジェクト指向/XP
  77. 77. ドメイン駆動設計 ドメイン モデル ドメインの 「重要な関心事」を 鋭く説明する 選び抜かれた 重要な関心事を コードで表現する 会話を繰り返して 「要点」を明確にする 「重要な語彙」を チームで合意する 1章 2章 3章 77 第1章 ドメインの知識をかみ砕く 第3章 モデルと実装を結びつける 第2章 言葉を使った意図の伝達
  78. 78. ドメイン駆動設計 ドメインを分析する人がコードを書く 毎日、分析し、毎日、コードを書く 「会話」をしながら意図を確認する コードを書く人がドメインを分析する 分析・設計・実装を「フェーズ」に分けない 分析・設計・実装の「担当者」を別にしない 「コード」と動くソフトウェアで意図を確認する 分析と実装を、同じ人が毎日やるなら、ドキュメントは不要 オブジェクト指向/XP 78 保守の現場ではあたりまえ?
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×