最近立ち返ってDDD本を読んでいるのでそのまとめ。
今回は10章の気づきをば。
概要
10章は設計と実装とをより深く結びつけていくための様々な概念が登場するため、それまでの章と比べて毛並みが違うイメージがある。
具体的にはより実践的なアプローチが紹介されているように思う。
本章で紹介されている見出しを以下にピックアップする。
- 意図の明白なインタフェース
- 副作用のない関数
- 表明
- 概念の輪郭
- 独立したクラス
- 閉じた捜査
- 宣言的な設計
- 設計の宣言的スタイル
- 攻める角度(サブドメインを切り取る)
となっている。
このように列挙してみると色々あり混乱しそうになるが、個人的には結局は「宣言的な設計」ということが目指すべきゴールなのではないかなと言うことだと思う。
それぞれ噛み砕く
意図の明白なインタフェース
これに関しては、DDDという文脈でなくとも実装をかじっている際に誰もが自然と意識していることなのではないかと思う。
要するにわかりやすいインタフェース(メソッド名)を設計しろと言うこと。
先人たちが作ったライブラリまたはアプリケーションを実装する際に別々のレイヤを実装している共同作業者などの成果物が、外部インタフェースを確認するだけで何をやっているか自然にイメージできるような設計にしろ。という解釈。
言わずもがな、人間一人ができる作業領域というのは限られる、他人の成果物を利用するのが当たり前。
OSもその最たるものの一種である。
アプリケーション開発を行う際にはそれらAPIがインタフェースから明確に理解できる必要がある。
内部実装までトレースすることで挙動を確認することは現実的ではない。
要するに副作用など含んだこちらの意図していない挙動を示す実装など使い物にならない、ということ。
これはDDD本内部では言及はないが、いわゆるAPIドキュメントを公開するということもこのアプローチと相違しない考え方なのではないかと思う。
例えばJavaの実装ではJavaDoc、RubyではRubyDocなどのドキュメントの仕組みが充実しているので、それらを合わせて初めから開発をすすめるというのも取れるアプローチなのではないか。
副作用のない関数
こちらDDD文脈でなくとも実装をしているうちに何となくイメージができるものだと思う。
副作用がある関数というのはその気持ち悪さが言語化できなかったとしても、開発者は妙な気持ち悪さというものを感じているのではないか。
ここで一つ悪い実装例を提示する。
自分が保持している液体の容量を持つコップというクラスがあってそれはあとから液体を足すことができる。
アプリケーションとして別のコップから液体を移動することができる。
public class Cup { public int volume; public Cup(int volume) { this.volume = volume; } public void add(Cup other) { this.volume += other.volume; other.volume = 0; } } public class Main { public static void main() { Cup cup1 = new Cup(10); Cup cup2 = new Cup(20); cup1.add(cup2); } }
簡単化のためにメンバ変数をpublicにしている。
ここでものすごくイケていないのは、cup1にcup2を加えた際にcup2の容量まで変更されているということである。
この気持ち悪さを言語化しているのが「副作用」であり、クライアント側から想像していないような変化が内部で生じているという状態。
副作用は単体で悪さをしているがなぜそうなるかということはおそらくもっと広い視点で設計自体をないがしろにしている可能性が高い。
その設計モデルがドメインと全く適合していないのである。(最もこの例ではそれ以前の問題ではあるが)
そのためにこの章で合わせて説明される他のアプローチと合わせて対応する必要がある。
表明
副作用はできるだけなくしたほうが良いというのは先程の例を見れば一目瞭然だと感じていただけるはずだ。
ただしやむを得ずなくならないというようなパターンもある。
副作用を含んだメソッド呼び出しのことを「コマンド」と呼ぶが、それでもこのコマンドをどうにか担保するというのが「表明」と呼ばれる対応である。
表明はこれまでの概念と比べて実装的な側面が強い。
ぶっちゃけていうとユニットテストと言っても差し支えはないだろう。
副作用が起こる前後の「事前条件」と「事後条件」というものを定義して、コマンドを実施した際の変化を担保しろということ。
またこの表明が存在することによって対象ドメインの内部実装まで確認せずともそのふるまいが理解できる。
テストケースを見ればどういう挙動かどうか推測できるという感覚である。
また言語によってはこの表明(assertionと呼ばれる)という仕組みを実装していることがあり、その場合は実装中に含まれることになる。
この「表明」については個人的なところあまり頻繁に活用するものではないという印象がある。どうしてもやむを得ないときだけに抑えるべきだろう。
結局表明を確認しなければいけないほど内部の振る舞いがこじれているということは、設計に立ち返って新たな適合するドメインを模索する必要があるのではないかと思う。
概念の輪郭
インタフェース、クラス、集約、操作などを考慮する際に自分の直感と立ち向かって常にリファクタリングを行うことを惜しむな。
実装途中に特定の処理があるメソッド中にさながらトランザクションスクリプトのようになってきたらそれはモデルが適切ではなくなっている。新しいエンティティやバリューオブジェクトなどを切り出すということが必要になってくるだろう。
この概念の輪郭については実装当初から完全に洗い出して着手するということは、あまり現実的ではない。
特にそれに対する知識が不足しているような状況であればなおさらのことである。
実装しているうちに当初考えていたモデルが適合しなくなって来た際にはその直感を大事にして、新しい輪郭を検討する必要がある。
リファクタリングが小さな範囲に収まっている場合は大きな輪郭としては適切に設計できているということである。
それが大きな範囲になってしまった場合はそもそもの根底部分の設計が間違っていた可能性がある。
この際にも非常に大きなコストを払う必要があるが、輪郭を再度設計する必要がある。
現場にいると様々なプロジェクトで既存の実装を捨てて一から作り直すような、大規模なリファクタリングを選択するシーンがそんなに少なくないと思う。
そこに技術者が新しい技術を導入したいという意思が介在していることも大きいが、その一端には古いモデルを捨てて新しい輪郭に沿った実装を行いたいという意味合いも含まれると思う。
業務に詳しくなっていくうちに、それまでの設計がそぐわないことに気づくことはよくある。
独立したクラス
クラス同士が過度に依存していることはあまり好ましくない。
クラスの同士はなるべく低結合とするべき。
こちらもそもそも不必要な依存が大量に発生している場合は設計に立ち返って見るべきである。
閉じた操作
閉じた操作とはインタフェースに同クラスを与えて、返り値にも同クラスが返却されるようなインタフェースのことを指す。
この状態が出来上がると直感的には、そのクラスのみが変更されて更に変更された新しいクラスが返却されてくるのだなというイメージができる。
またそれによって副作用なども含みようがないので非常に明瞭である。
このアプローチは積極的に採用すべき。
逆説的だがこれまで上げたアプローチが達成できていれば、自然と閉じた操作ができるか、またはほとんどコストを掛けずに適用することができる状態になっているはず。
宣言的な設計
閑話休題。これまでの話と少しそれるのだが宣言的な設計というものがある。
「宣言的」の対義は「手続き的」であり、コンピュータは当然後者に親和性が高い。
人間が介在する際には、宣言的である方が自然であり、解釈しやすく、同然間違いも生まれにくい。という考え方である。
ルールを矯正することで宣言的に動作する代表的で馴染みのあるものはフレームワークである。
こういったモノは理論的には上手く機能するように思うが実際にはうまくいかないことも多々ある。
まず一つのリスクとしてはフレームワークが巨大である場合学習コストが高くつく、ということである。
しかもそれはプロジェクトなどに適用する場合全員がもれなく従う必要がある。
またそのフレームワークがドメインで実現したいことをサポートしなかった場合、何らかの形で対応する必要がある。
大抵その対応は醜いものになるだろう。
DDD本では適用するのは一部の効率的に機能する部分をサポートするようなフレームワークに留めるのが良いのではないか。ということを指摘している。
設計の宣言的スタイル
本章の目指すところ。
宣言的な設計を目下のドメインの実装に取り込むこともでき、これが達成できれば非常に人間にとって親和性の高い気持ちのよい実装が実現できる。
一行一行目を通してようやく実装を把握するような処理ではなくて、メソッド名をひと目見て把握できるような宣言とするべきである。
その末にはまるで述語を記述するような形でのメソッドが適用できるようになる。
解説を投げてしまうが本章で説明されている「仕様」の組み合わせ。の例は素晴らしい。
閉じた操作に則っていることで仕様からまた新しい仕様を動的に生成できる。
仕様を持ち回すことができ、コードのクライアントはまるで述語を記述するように実装できる。
これは言語を書いているようで、非常に直感的で人間と親和性が高い。
攻める角度(サブドメインを切り取る)
設計を行う際に、どういったアプローチを取るかというのは無限にある。
その中で切り出すドメインによってはとても適切であり、効果的に表現力を持てることがあることがある。
その最たるものとして「数学」がある。
そのドメインの中に内在する数学を切り取ることで他のドメインからは一切の数学を排除できる。
また数学は誰もが学習していることであり、学習コストが低い。
その他の特別なドメインを切り出す場合、それが一般的でない場合全員が学習する必要があり、またモチベーションなどの側面からも効果的ではない。
コメントを残す