ドメイン駆動設計:4つのアンチパターン

6月23日(土)午後、大阪で、ドメイン駆動設計について、しゃべる予定。
第47回 SEA関西プロセス分科会のご案内

紹介の紹介の紹介、みたいな不思議なつながりで、講演の依頼があった。
「ドメイン駆動設計」を監訳された今関さんも参加いただける。

土曜の午後なので、時間が多め。
一方的にしゃべって終わりではなく、今関さんも交え参加者で、いろいろ話す時間を一時間以上とってもらいました(さらに懇親会もゆっくりと)。とても楽しみなイベントです。

ドメイン駆動設計は、訳本がでてから、あちこちで勉強会や読書会が開かれているようだし、ドメイン駆動設計に興味持ったり、現場で取り組む人が、すごく増えた気がしている。
ネットで流れる情報も「ドメイン駆動ってなんぞや?」的なものだけでなく「私は、こう理解している、やっている」的なものが多くなったんじゃないかなあ。
訳本の威力は絶大。

さて、講演の準備は、いつものようにドメイン駆動設計(訳本+原書)の読み直しから。
毎度のことながら「そうかこんなことも書いてあったんだ」と、たくさんの新しい発見。

最近の私のドメイン駆動設計の実践


ここ数カ月、中堅のSIer さんの開発プロジェクトで、ドメイン駆動設計をベースにしたモデリング・設計の支援をさせてもらっている。

伝統的なエクセル仕様書、ウォータフォール的な分業カルチャーの現場だけど、コアメンバーは「ソフトウェアの保守・変更のたいへんさ」に本当に苦しんだ経験があるチーム。
だから、ドメイン駆動設計の考え方やこだわりポイントには、共感してもらえることが多い。
やりがいがある仕事になっている。

クラス図を本格的に書くのは初めてというメンバーが多くても、もともと、その業務分野の知識とソフトウェア開発経験が豊富なメンバーなので、ポイントが共有できると話がとてもはやい。

このコンサルティングと並行して、ここ数週間は  Heroku という素敵な開発環境で、モデリングとプロトタイピングを、自分でコードを書きながら、毎日、数回、公開環境にデプロイしながら、試作している。

もちろん、ドメインのモデリング・実装が中心。
画面遷移、CSVアップロード/ダウンロード、メール送信は、本番用のコードで実装している。
永続化だけは、データベースではなく、Map でドメインオブジェクトを管理するフェイク実装。
ドメインモデルが安定するまで、テーブル設計と ORマッピングは後回し。

今回の講演内容は、この、現場でのモデリング・設計支援経験、自分自身でのモデリング・試作作業から、いろいろ手に入れたものを盛り込もうと思う。
本の読み直しでも、けっこう、目から鱗ネタがあった。
自分では、いままでの勉強会等での発表とは違った、新しいステージの内容になりそうな予感がしている。ちょっとしたブレークスルー感覚。
(妄想?まあ、何か作りはじめる前は、いつもそんなもの)

講演資料の準備もかねて、いくつか気が付いたことを、ブログ書きながら整理してみようと思う。

今回のテーマは「ドメイン駆動設計:4つのアンチパターン」。

アンチパターン:ごった煮コード


ドメイン層が分離できていないコード。

Web アプリケーションだと、HTTPRequest・Session・データベースアクセスなどの関心事と、業務の知識や業務ルールが、同じクラスやパッケージに混在している。

ひどい症状だと、メソッドの中に、業務判断の if 文と HTML 生成コードが、いっしょになっていたりする。

発想として、これをアンチパターンと思っていない開発者もいる。(メタなアンチパターン?)

ドメイン駆動設計というより、「関心の分離」というソフトウェア設計の基本に違反したアンチパターンですね。

ドメイン駆動設計の場合「ドメイン層」を、それ以外の層から分離・独立させる。
ドメイン層が「独立する」とは、他の層がドメイン層に依存する、ということ。
ドメイン層が他の層に依存してはいけない。

「ごった煮コード」は、これができていないわけだから、ドメイン駆動設計的には、完全なアンチパターン。

ここらへんは、発想や意識の問題もあるけど、アーキテクトがフレームワークの選定や使い方をリードしてしまうのが特効薬だと思っている。

既存のコードベースが大きいと現実的には難しいかな?

私たちは、Spring フレームワークを使って実践している。
Spring フレームワークは、ドメイン層とそれ以外の層、という発想が明確なフレームワークで「ドメイン層の独立」を実現するには良い選択肢だと思っている。

このアンチパターンは、小さなリファクタリングの積み重ねでは解消しない気がする。

いちばん重要なのは「発想の転換」。
「ごった煮」じゃだめなんだ、という価値観をチームで共有するのが近道。
地道に改良して改善していける問題ではないと思う。

アンチパターン:トランザクション・スクリプト


ドメイン駆動設計的には、トランザクション・スクリプトはアンチパターン。

手続き的な発想(パラダイム?)でいる限り「ドメイン駆動設計」でエバンスが書いていること、ほとんど理解できないだろうし、実践や応用は無理だと思う。

オブジェクト指向の分析設計スタイルが、ドメイン駆動設計の前提になっている。

何をもってオブジェクト指向のスタイル・発想とするかは難しいけど。

実際、私たちはドメイン層では、継承・抽象クラス・多態性は、原則として使わない。
開発言語も (手続き型のC言語の臭いが強く残る) Java だし、見方によっては、オブジェクト指向プログラミングはしていないのかもしれません。

ドメイン駆動設計的には、

・業務上、関連する情報を、ひとかたまりにする
・その情報を扱う操作は、同じ場所に集める
・その情報と操作の塊を、クラスやパッケージで明示的にカプセル化
・パッケージ間の依存関係は、できるだけ疎結合に

を重視する。
これがオブジェクト指向の発想なんだと思ってやっている。

というわけで(オブジェクト指向スタイルではないので)トランザクションスクリプトは「ドメイン駆動設計的には」アンチパターン。

ところで、私は、実は「トランザクション・スクリプト」パターンで記述した、独立したドメイン層のコードを見たことがありません。

見たことがあるのは、ドメイン層とそれ以外の層を「ごった煮」で、手続き的に延々と書き連ねたコードだけです。

ごった煮コードは、ドメインロジックの記述パターンとしての「トランザクション・スクリプト」とは別物でしょう。

ドメイン層をピュアに「トランザクション・スクリプト」で記述するなんて、ほんとは誰も実践していないのかも?
アンチパターンに取り上げるネタじゃないか。

アンチパターン:どこでも業務ロジック


■UI に、業務判断の if 文がある。
例:ロールが承認者で、状態が承認待ち、だったら承認ボタンを enable にする。

■データアクセス(SQL文)に、業務判断の where 文がある。
例:申込日が、締切の一ヶ月以上前だったら、早期申込みフラグを1 にする。

■テーブルに、業務ルールのトリガーがある。
例:10万円以上の購入依頼があったら、上長承認依頼レコードを作成する

...

例をあげれば、きりがない。
ソフトウェア一般としては、こういう書き方もありなんだと思う。

しかし、ドメイン駆動設計的には、ひどいアンチパターン。

ドメイン層のオブジェクトが、こういう知識・判断力を持つべき。
UIやSQL文で、この業務知識を利用するなら、業務判断能力があるドメインオブジェクトから、判断結果をもらうようにする。

他の層がドメイン層に依存すべし、というのは、こういうこと。

ここらへんも、発想の転換とかパラダイムシフトなんだと思う。

私の場合は、原書と格闘しながらドメイン駆動設計の実践のチャレンジを、2年くらいたったある日、若手の書いたコードとモデルをレビューしていた時に、とつぜん、この発想が変わったのを覚えている。

それ以降は、ビュー記述や、SQLにドメインの知識がまぎれこんで隠蔽されることを、かなり的確に見つけることができるようになった気がする。

UIやSQLに散在して記述している業務知識を、ドメイン層のオブジェクトに移動して集めていけば、業務知識の豊富なドメイン層が自然に育ってくる。

「どこでも業務ロジック」アンチパターンは、「豊かなドメイン層」に発展できる元ネタの宝庫ともいえる。

このアンチパターンの改善は、やっぱり発想の転換が必要。
作業自体は、地道なリファクタリング(ロジックの移動)の繰り返しですね。

アンチパターン:ドメインオブジェクトの getter/setter 大忙し


getter/setter は、内部に private に隠ぺいしたフィールドを、そのまま get したり set したりするメソッド(と定義しておきます)。

ビューを記述する時や、永続化のフレームワークで、こういう getter/setter が必要ではあるので、ドメイン層のクラスは、getter/setter を持つな、という意図ではありません。

問題は、ドメインオブジェクト同士(ドメイン層内部)で getter や setter を頻繁に使うこと。

これは、完全なアンチパターン。

まず setter はドメイン層の中で使う必要性はないはず。

パラメータ付のコンストラクタで生成した不変型のオブジェクト、つまり Value Object を使う限り setter は使う機会はありません。

Java の String クラスがそうですね。オブジェクトの内部情報を書きかえる setter メソッドはゼロです。

ドメイン層のオブジェクトの多くは、こういう Value Object で、設計・実装できるはずなんです。

getter も怪しい。

ある値を get してから、何をやっているんでしょう?

もし if 文とかで条件判断に使っているなら、その if 文ごと get 元のオブジェクトにロジックを移動すべきでは?

もっと低レベルの話だと getFirstName(), getFamilyName() と2回 get して、getした側で firstName + " " + familyName とか加工している。

get 元のオブジェクトに getFullName() メソッドを用意すべきですよね。

これは、ビュー記述や SQL 記述でも、同じことが言える。

ビュー記述( JSP とか velocity マクロ)で get しまくった内容を、ビュー記述側で、合成したり判断をしていたら、ドメイン層に、メソッドを追加したり、便利なサービスを提供するクラスの追加を検討する。

ドメイン駆動設計で、ドメインモデルを充実させると、アプリケーションプログラマが getter/setter を利用する機会は、どんどん減ります。減らすことが良いことなんです。

機械的な getter/setter 利用は、フレームワークにまかせる。

4つのアンチパターン、まとめ


結局、ドメイン駆動設計のアンチパターンになるのは、

◎関心の分離
◎わかりやすい役割分担
◎自然な協調関係

に失敗しているから。

この失敗は、どのレイヤのクラスやパッケージの設計に共通する課題。
ソフトウェア設計の基本スキルをみがくべし。

「ドメイン駆動設計」にフォーカスすると...

ドメイン層のオブジェク群の、

◎関心の分離
◎わかりやすい役割分担
◎自然な協調関係

のヒントは、ドメインエキスパートの頭の中に詰まっている、という考え。

もちろん業務の専門家が、設計原則を意識してるわけではない。
ましてや、設計をリードしてくれるわけではありません。

開発者が、業務の専門家と会話を繰り返しながら、その業務専門家が持っているはずのヒントを見つけにいく。
そうやって、ドメイン層のモデリングと設計を洗練させていく活動が、ドメイン駆動設計だと思っている。

業務がちゃんとできている人は、業務上のさまざまな概念やルールを、(ほとんど無意識に)体系的な知識として使っている。
その「体系的な知識」を、業務の専門家の頭の中から、うまくひっぱりだすのがドメイン駆動設計の基本の活動。

そうやって獲得した体系的な業務知識を、すなおに設計・実装したソフトウェアは、利用者の問題解決にほんとうに役に立つだろうし、業務の都合から発生する、さまざまな追加の要求にも、自然に、安全に対応できるようになる。

それがドメイン駆動設計を実践する成果なんですね。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
<< June 2012 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM