ドメイン駆動で開発する ラフスケッチから実装まで

103
ドメイン駆動で開発する 初期のラフスケッチから実装まで 20161031ギルドワークス 増田 1

Upload: -

Post on 16-Apr-2017

5.677 views

Category:

Software


0 download

TRANSCRIPT

ドメイン駆動で開発する 初期のラフスケッチから実装まで

2016年10月31日

ギルドワークス 増田

1

ドメイン駆動設計の効果

役に立つソフトウエアを

確実に

効率的に

ドメイン駆動設計でやらないと…

2

設計の善し悪し

3

拙い設計

• コードが重複しまくっている • 条件分岐の密林があちこちにある • どこに何が書いてあるかわからない • 変更した時にどこで何が起きるか推測できない

• やっていることは解読できるが、なぜ、そこでその処理が必要か意味がわからない

• パッケージ名/クラス名/メソッド名/変数名/コメントが嘘だらけ

• でも動いてる

動いていなければ、ろくでもない設計であることは誰でもわかるのに… 4

善い設計

• コードの重複がなくなる

• 条件分岐の密林がなくなる

• どこに何が書いてあるかわかりやすくなる

• 変更した時に影響範囲を限定しやすくなる

• 処理の意図がすぐわかる

• パッケージ名/クラス名/メソッド名/変数名がわかりやすく正確になる

– コメントはむしろノイズ

5 ドメイン駆動設計を実践すると、自然にこうなっていく

ドメイン駆動設計を 支える技法

6

オブジェクト指向

インクリメンタルな設計

データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ

「設計不要」でもなく「上流工程で設計する」でもない世界へ

(JPA,Active Record,Entity Framework…)

技術的にはこの2つ越境が、ドメイン駆動設計を実践するブレークポイント

7

二つはコインの裏表

ドメイン駆動設計の学習曲線と 2つのブレークポイント

オブジェクト指向設計

インクリメンタルな設計

パラダイムシフトに3ヵ月から半年くらい

実プロジェクトでいっしょにやりながら パラダイムシフトに半年から数年

8

オブジェクト指向

9

判断/加工/計算の ロジックを整理する技法

ロジックとデータをまとめて 一つのプログラミング単位にする

人間にわかりやすい単位に プログラムを分割して整理する

クラス インスタンス変数

メソッド

パッケージ クラス メソッド 名前

わかったつもりでもなかなかできない オブジェクト指向らしいロジックの整理のしかたを体で覚える

インクリメンタルな設計

10

最初から良い設計は手に入らない

コードを書いて動かしてみれば わかることがたくさんある

経験から学んだことを反映して 設計の改善を続ける

仕様の抜け漏れ 要求の勘違い 拙い設計

毎日、 知識を広げ 理解を深め コードに反映

どうしてもきれいな答えを最初から見つけたくなる 設計を少しずつ改善するスピード感を体で覚える

ドメイン駆動設計の 手ごたえとハードル

11

ドメイン駆動設計の手ごたえ

• 対象領域の業務を学びながらソフトウェア開発する面白さ

• ソフトウェアが育っていく楽しさ

• 対象領域の知識とコードが一致している時のコードのわかりやすさ

• 変更が楽で安全になる気持ちよさ

• 正しい仕事をしている安心感

12

ドメイン駆動設計のハードル

• 発想の転換 – 「動いた」駆動から「善い設計」駆動へ

– 「技術」駆動から「ドメイン」駆動へ

• 「善い設計」の判断基準の変化

• 設計・開発手法の切り替え –オブジェクト指向へ

– インクリメンタルな設計へ

• 実装の制約との戦い

– フレームワークやプログラミング言語の制約 13

ここらへんの 実践経験を紹介します

ドメイン駆動設計

14

ドメイン駆動設計の基本の活動

知識をかみ砕く

言葉を活用する

モデルと実装を一致させる

第1章

第2章

第3章

15

基本の活動

ドメインの オブジェクト モデル

利用者の 「重要な関心事」を 鋭く説明する

選び抜かれた 重要な関心事を コードで表現する

会話を繰り返して 「要点」を選び抜く

重要な「言葉」の解釈を チームで合意する

1章 2章 3章

16

第1章

ドメインの知識をかみ砕く 第3章

モデルと実装を結びつける

第2章

言葉を使った意図の伝達

プロジェクトを始めてみたものの…

17

開発者が対象業務を知らない

言葉がばらばら、思いもばらばら

ふわっとしてあやしげな要望

どうすればよいか?

18

どうすればよいか?

19

リレーションシップ駆動 要件分析(RDRA)

・コンテキストモデル ・業務フローモデル ・イベント/状態モデル

ICONIX

・ドメインオブジェクトの発見 ・ドメインモデルの育て方 ・ロバストネス分析

問題領域を把握する技法

初期のドメインモデルを 2時間以内で描く

モデル間を関連づけて 整合性と網羅性を確保 ユースケースと実装の

ギャップを埋める 20

こんな感じでやってみた

21

ドメイン駆動の開発プロセス

業務フロー

モデルの改良

要約、骨格

画面・帳表 ユースケース

属性の追加 モデルの洗練

イベント 状態遷移

データモデル

初期の ラフスケッチ

コンテキスト図

手がかかり

ドメインオブジェクトの設計と実装

ロバストネス分析

必要ならシーケンス図

操作追加

Java ソース

基盤クラス追加

DDL/SQLソース

問題領域の把握

構造化用語集

22

最初にやっていたやり方

• モデルの作成に時間をかけた – モデリングツールの利用(Enterprise Architect) – クラス図 – ユースケース記述 – ロバストネス図 – ER図

• 慣れて来たら、なんでもかんでも図にすることはなくなった – 複雑になるところだけ – しっかり設計したいところだけ

• 設計ドキュメントの作成が一段落してからコードを書いていた 23

がんばって設計したつもりなのに あいかわらず修正や拡張は

たいへんだった

コード 汚い / 変更 やっかい

24

汚いコードと格闘しながら いろいろ勉強してみた

25

オブジェクト指向 エクササイズ

インクリメンタルな設計

抽象データ型 契約による設計 シームレスな開発

コードで意図を 表現する

既存のコードの 設計を改善する

ドメインロジックの 設計パターン

言葉たいせつ モデルと実装の一致

通読した本は一冊もない そのかわり何回も何十回も拾い読みしている

• オブジェクト指向がだんだん体になじんできた – 人間の関心事の表現と、ロジックの整理の単位(クラス)が一致してきた

• インクリメンタルな設計の良さがだんだんわかってきた

• ドメイン駆動設計って、もともとそういう本だよね、そういえば – オブジェクト指向ソフトウエアの開発者向けに書いた

– 設計と開発プロセスの議論の基礎としてエクストリームプログラミングを使用する

26

オブジェクト指向

インクリメンタルな設計

データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ

「設計不要」でもなく「上流工程で設計する」でもない世界へ

(JPA,Active Record,Entity Framework…)

ドメイン駆動設計を支える技法

27

二つはコインの裏表

ドメイン駆動設計の学習曲線と 2つのブレークポイント

オブジェクト指向設計

インクリメンタルな設計

パラダイムシフトに3ヵ月から半年くらい

実プロジェクトでいっしょにやりながら パラダイムシフトに半年から数年

28 私自身は、孤独な戦いの中で、ここまでに数年かかった

近頃は こんなやり方で やっています

29

ドメイン駆動で開発する 初期のラフスケッチから実装まで

事例紹介

30

実際にやっていることを お伝えするための

前置き

オブジェクト指向を体で覚える 技術方式の選択

ドメインオブジェクトの設計パターン

この話が40ページくらい続きます ご了承ください

31

オブジェクト指向設計を 体で覚える

まずはここから

ドメイン駆動設計のために

32

体で覚える オブジェクト指向設計

33

頭でわかっているつもりではだめ オブジェクト指向らしい設計が

直観的にわかり オブジェクトらしいコードに 自然に手が動くように

実践的に練習する

オブジェクト指向エクササイズ ソフトウェア設計を改善する9つのルール

リファクタリング 既存のコードの設計を改善する

34

オブジェクト指向 エクササイズ

オブジェクト指向の良さを生み出す9つのルール

35

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

リファクタリング

オブジェクト指向の良さを生み出す設計改善のガイドライン

41

いやな臭い

• コードが重複している

• メソッドが長い

• クラスが大きい

• 引数が多い

• データとロジックの置き場所が別のクラスに分かれている

42

設計の改善

• 名前の変更

–業務の関心事と実装を一致させる

• メソッドの抽出

–手続きに名前をつける

• クラスの抽出

–手続きのグループに名前を付ける

• メソッドの移動/フィールドの移動

–データとロジックの置き場所を同じクラスにする

43

リファクタリング

既存のコードで、いやな臭いを見つける

これを繰り返しているうちに ・いやな臭いに敏感になる ・最初から短いメソッド/小さなクラスを書きはじめる

リファクタリングを適用して 設計改善のbefore/afterを体感する

44

技術方式はたいせつ

ドメイン駆動設計を実践するには

45

レイヤ構造

• 三層構造ではなく、 三層+ドメイン層

• O-Rマッピング

–データクラス+機能クラス(手続き型)になりがちなツールは避ける

• JPA, Active Record, Entity Framework、…

–オブジェクトが主役の軽量のツールを選ぶ

• myBatis, Spring JDBCTemplate, …

46

プログラミング言語

• 静的な型付き言語 • 独自の型の定義

– 標準ライブラリの型(クラス)は汎用的すぎる – 目的特化の型をどんどん作る

• 型名を明記して意図を表現する – メソッドの返す型、メソッドの引数の型 – インスタンス変数の型 – ローカル変数の型

• 型の設計変更に強い – リファクタリングを楽に安全に

47

技術方式 わたしたちの選択

Java, Spring Boot

48

プレゼンテーション層と ドメインオブジェクト

49

問題領域の オブジェクトモデル

基本方針

• ドメインオブジェクトをそのまま使う

• 画面

– ドメインオブジェクトをHTMLにマッピング

– HTTPリクエストをドメインオブジェクトにマッピング

– Spring MVC/Thymeleaf

• API

– JSON<-> ドメインオブジェクトの自動マッピング

– Spring MVC RestController / Jackson

50

簡単にマッピングできない時

• ビュークラスを間にはさむ • 出力

– ビュークラスのコンストラクタにドメインオブジェクトを渡す

• 入力 – 入力データからビュークラスを生成する

– そのビュークラスにドメインオブジェクトのファクトリメソッドを用意する

• ビュークラスは判断/加工/計算のロジックを持つ – DTOではない

• 安易にビュークラスを導入しない – ドメインの関心事の表現(ドメインオブジェクト)とインタフェースが不一致なのはおかしい

51

データソース層と ドメインオブジェクト

52

問題領域の オブジェクトモデル

基本方針(構造)

• アプリケーションクラスにデータソースクラスをインジェクションする

• 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

ドメイン層

問題領域の オブジェクトモデル

55

ドメインオブジェクトの 設計パターン

ドメインオブジェクトの候補を見つけ ドメインオブジェクトを実装を楽にする指針

56

関心事を表現する基礎部品 ドメインオブジェクトの設計パターン

• 値オブジェクト

• 識別オブジェクト

• ファーストクラスコレクション

• 区分オブジェクト

• How ではなくWhatを表現する

• パッケージ

57

値オブジェクト (ドメインロジックの置き場所の基本)

58

値オブジェクト

• 業務の関心事の基本語彙

–日付、期間、数量、金額、単位、名称、備考、…

• ロジックの置き場所

– インスタンス変数として一つか二つの基本データ型(文字列/数値/日付)を持つ

–そのインスタンス変数を使った、判断・計算・加工のロジックを一箇所に集める

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

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

識別オブジェクト (関心事の特定方法)

63

識別オブジェクト

• 関心事を特定する • 人は何で特定するか

– 名前 – 生年月日 – メールアドレス …

• コンピュータは何で特定するか – 一意の管理番号 – equals()

• 両方の目的に使えるオブジェクトを作る

64

ファーストクラスコレクション (主要な関心事とロジックの置き場所)

65

ファーストクラスコレクション

• ListやSetをラップしたドメインオブジェクト • PurchaseHistory

• MailBox

• SkillSet

• 「一覧」や「履歴」という関心事の表現

– 「一覧」は利用者の関心事が集中する場所

– 「一覧」の議論は、重要な関心事の発見の良い機会

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

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

ファーストクラスコレクション

コレクション List<Item>だけを持つクラス コレクション操作のロジックを集める場所 外部にコレクションを公開しない 正しい状態を保証する

コレクション操作のロジックがあちこちのクラスに散らばらない/重複しない Item の件数制限や、合算方法に変更があっても他のクラスに波及しない Itemの一覧画面に関する変更は、まずここを見れば良い

ロジックを集める感覚を体で覚える 関心事とクラスを一致させる感覚を体で覚える 67

区分オブジェクト (場合ごとのビジネスロジックの表現)

68

区分オブジェクト

• 振る舞いを持った Enum • Java言語使用に組み込まれた

Strategy/Stateパターン +

• 区分ごとのロジックを多態で記述

場合ごとのビジネスルールの表現

69

区分オブジェクトの効果

• 複雑な if文記述の解消

–区分ごとの分岐記述( if文,switch文 )は一箇所になる

–場合によっては、まったく書かなくてよい

• どこに何が書いてあるかわかりやすくなる

–区分ごとのロジックをクラス単位で表現

• 区分の追加や削除をした時の、変更の副作用が少ない(範囲を限定しやすい)

70

How より What (意図の明確なインタフェース)

71

expireDate.add(-1);

expireDate.previousDay();

expireDate.dayOfFinalAlert ();

業務要件:期限切れの前日にアラートメールを送る

How より What 業務の関心事を明示的に表現する

72

課金ポリシー

適用する()

シーズン料金

レート()

夏料金

レート()

冬料金

レート()

料金

計算() <<interface>>

計算方法(How)を表現

料金区分(What)をクラスで表現

計算()メソッドに埋もれ、 暗黙化する業務知識 シンプルな設計に見えるが、 ルールの変更・追加のたびに 計算()メソッドが肥大化し、 if 文が増殖する

業務知識をそのまま、クラスとして表現 複雑に見えるが、ルール変更・追加が、 楽で安全になる

ロジックをクラスで表現する

73

パッケージ (重要なモデリング要素)

74

パッケージ

• 利用者の関心事の「境界」を表現する手段

–関連するドメインオブジェクト(の型)のグループ

–プログラミング単位

–テスト単位

• 新しい発見とともに、積極的にパッケージ名/パッケージ構成を変更し育てていく

• 「第4部 戦略的設計」の主要ツール

75

パッケージ図を使った ロジックの置き場所の俯瞰

76

与信限度額を考慮するとどうなるか? 発注と仕入れまで視野を広げるとどうなるか? 矢印で示した一方向の依存関係で正しく実装しているか/実装できるか?

実際にやっていることを お伝えするための

前置き

オブジェクト指向を体で覚える 技術方式の選択

ドメインオブジェクトの設計パターン

ようやく前置きの終わり

77

ドメインのラフスケッチから実装まで

事例 : アソビュー

78

サービスの会社が 業務システムに取り組む話

• アソビュー

• 全国のレジャー・遊び・体験が探せる日本最大級の検索・予約サイト

• バックオフィス業務は必要最小限、かつ人間系でがんばっているが…

• 事業のさらなら発展のためには • バックオフィスのシステム充実が必須 • ただし、 • ビジネスモデルや業務フローは変わり続ける

79

電子チケットサービス

• ネット上で電子チケットの購入

• 入場時に、電子チケットの提示と「もぎり」

80

「もぎり」 電子チケットの購入

81

電子チケットサービス

• アソビューの挑戦

–金流の変更

• before : 主催会社にお金が入り、手数料を請求

• after: アソビューにお金が入り、主催会社に支払い

–バックオフィス業務の複雑化

–確実性や迅速性の要求

–バックオフィスシステムの開発経験不足

82

そこで ドメイン駆動設計

83

業務を理解する 関係者の言葉と思いを合わせる 実装可能なモデルを手に入れる

84

ドメイン駆動設計

ドメインの オブジェクト モデル

利用者の 「重要な関心事」を 鋭く説明する

選び抜かれた 重要な関心事を コードで表現する

会話を繰り返して 「要点」を選び抜く

重要な「言葉」の解釈を チームで合意する

1章 2章 3章

85

第1章

ドメインの知識をかみ砕く 第3章

モデルと実装を結びつける

第2章

言葉を使った意図の伝達

ドメインオブジェクトの 候補を見つける

インクリメンタルに設計する

86

インクリメンタルに設計する

ざくっとラフスケッチをしてみる

コードで書いて、動かしてみる

リファクタリングをする

行き詰まったら 絵に描いてみる

会話をしてみる

結果をコードに反映する

そうやって設計を改善し続ける

それがもっとも確実で効率的

87

ドメインオブジェクトの 候補を見つけるために ザクッとラフスケッチ

88

サービスの概要と用語の洗い出し

89

初日にやったラフスケッチ キックオフミーティングに参加する前の予習

チケット販売システムの 目的/背景をチームで確認

90

業務の流れを追いかける

91

UXデザイナもこれを参考に ワイヤーを起こし始める

営業企画がこれを参考に 利用案内の準備を始める

関心事をざくっと整理する

92

インクリメンタルな設計

• このくらいの状態でコードを書き始める

• 基本オブジェクトでロジックの置き場所を決める – 値オブジェクト

– ファーストクラスコレクション

– 区分オブジェクト

– 識別オブジェクト

• アーキテクチャはテンプレートを使う – Spring Boot

– 基本のレイヤ構造 presentation/application/domain/infrastructure

93

仕様を固めつつ リファクタリングしつつ

コードを書くのに行き詰まったら

94

絵を描きながら会話する

95

重要な概念をクラスに抽出 ロジックの置き場所の発見

ファーストクラス コレクション

第16章の 知識レベル

絵を描きながら会話する

96

クラスを整理する パッケージ構成の改善

最初のラフスケッチ

ラフスケッチには登場しない ドメインオブジェクトの例

• Rate – 値オブジェクト – 料率の計算ロジック

• Amount – 値オブジェクト – 金額の四則演算

• DateRange – 値オブジェクト – 有効期間の設定と判定

• ValidType – 区分オブジェクト – チケットの状態区分と有効性の判定

最初から コードで書いて 成長させている

97

やってみた結果

• 2か月での迅速なリリース

• その後、事業もシステムも順調に成長中

• 企画部門や経営者から「チケットのような作り方」に高い評価

• IT戦略としてのドメイン駆動設計の推進

98

本日のまとめ

99

ドメイン駆動設計の効果

役に立つソフトウエアを

確実に

効率的に

100

オブジェクト指向

インクリメンタルな設計

データクラス+機能クラスの世界から、ドメインオブジェクトの世界へ

「設計不要」でもなく「上流工程で設計する」でもない世界へ

(JPA,Active Record,Entity Framework…)

ドメイン駆動設計を支える技法

101

二つはコインの裏表

オブジェクト指向を体で覚える

オブジェクト指向エクササイズ ソフトウェア設計を改善する9つのルール

リファクタリング 既存のコードの設計を改善する

102

インクリメンタルに設計する

ざくっとラフスケッチをしてみる

コードで書いて、動かしてみる

リファクタリングをする

行き詰まったら 絵に描いてみる

会話をしてみる

結果をコードに反映する

そうやって設計を改善し続ける

それがもっとも確実で効率的

103