カテゴリー別アーカイブ: ソフトウェア設計

アナリシスパターンを理解する

最近立ち返ってDDD本を読み進めています。
今回は11章の気づきに関して取りまとめます。

概要

第11章ではアナリシスパターンについて解説している。
本記事ではそれに対する個人的な解釈を述べる。
実際にはアナリシスパターンは非常に抽象的な話で、概念的に理解するのはそこまで難しくはない。

アナリシスパターンとは

アナリシスパターンとは既存の業界についてすでに洗礼されたモデル(それはシステムとしてという文脈とは全く関係なくて構わない)が存在する際に、そのドメインを学習することでドメイン設計に対するアプローチとすることである。

本章では具体的に会計システムの開発を行う際に、会計の専門書を解読することで設計のアプローチとする例が取り上げられている。

また外部の知識と合わせて現場にドメインエキスパートがいるようであれば、ユビキタス言語を用いて設計をすり合わせることでより洗礼されたモデルを設計することができる。

これは非常に強力な手法であり、まず率先して取り入れるべき方法だと思う。
特にその業界に対する知見が全く無い場合丸腰で設計するのは自殺行為に等しい。
システムという側面に囚われすぎず、一般的な専門書などから得られる洞察は膨大なものになるだろう。

ドメインエキスパートについて

この例ではドメインエキスパートが出てき、開発者とともに簡素なクラス図のようなものを用いている。
だが実際に現場にいるドメインエキスパートがここまで開発側の用いている手法に対して理解があることは少ないのではないかと思う。
それは協力的な姿勢であったり、そもそも開発者が用いているクラス図がどのようなもので、どういったヒントを求めているかというコンテキスト的な解釈ができるかどうかという事も含めてである。

そういう意味で、本章に出てきているドメインエキスパートを含むチームはすでにチームビルディングに巧みに成功している例といえ、自分の経験上現実にこのような状況になることは難しい。

チーム全体がDDDを理解しているためには組織全体が積極的に働きかけ、エンジニアだけでなく広い範囲でこうした開発スキームの理解をし、さらに時間をかけて学習している必要がある。

否定的な意見を言ったが、現実的にはクラス図などを用いなくとも、ドメインエキスパートから話を聞くということだけでも大いに有益であり、これも積極的に取り入れることで恩恵に預かれる。

アナリシスパターンの肝

個人的に「業界知識」「ドメイン設計」「実装」この3つのバランスを上手く取ることがアナリシスパターンの肝だと感じる。
まず「業界知識」「ドメイン設計」の部分であるがこの2つは実際に最終的に落とし込む場合に異なる箇所が出てくる。当然全く同じ状態になるかとそうでもない。
アプリケーションを実現するということが第一目的であるため、独自の知識や組み合わせなどが出てくることがあるからである。
適切な境界やモデルが出来上がるまでに何度も設計を吟味してドメインを練り直す必要がある。この際に業界知識に引っ張られすぎてはいけない。

また「ドメイン設計」「実装」に関しては実装が持つルールやフレームワークを使用している場合、また言語の特性によってはドメイン設計をそのまま実装に落とし込むことが困難になる場合がある。
そのためドメイン設計を多少捻じ曲げて実装を行うこともある。
この際なぜそうなっているのかという証拠などを十分に残して置くことなどが大切になるのではないかと思う。


しなやかな設計を理解する

最近立ち返ってDDD本を読んでいるのでそのまとめ。
今回は10章の気づきをば。

概要

10章は設計と実装とをより深く結びつけていくための様々な概念が登場するため、それまでの章と比べて毛並みが違うイメージがある。
具体的にはより実践的なアプローチが紹介されているように思う。

本章で紹介されている見出しを以下にピックアップする。

  • 意図の明白なインタフェース
  • 副作用のない関数
  • 表明
  • 概念の輪郭
  • 独立したクラス
  • 閉じた捜査
  • 宣言的な設計
  • 設計の宣言的スタイル
  • 攻める角度(サブドメインを切り取る)

となっている。
このように列挙してみると色々あり混乱しそうになるが、個人的には結局は「宣言的な設計」ということが目指すべきゴールなのではないかなと言うことだと思う。

それぞれ噛み砕く

意図の明白なインタフェース

これに関しては、DDDという文脈でなくとも実装をかじっている際に誰もが自然と意識していることなのではないかと思う。
要するにわかりやすいインタフェース(メソッド名)を設計しろと言うこと。
先人たちが作ったライブラリまたはアプリケーションを実装する際に別々のレイヤを実装している共同作業者などの成果物が、外部インタフェースを確認するだけで何をやっているか自然にイメージできるような設計にしろ。という解釈。

言わずもがな、人間一人ができる作業領域というのは限られる、他人の成果物を利用するのが当たり前。
OSもその最たるものの一種である。
アプリケーション開発を行う際にはそれらAPIがインタフェースから明確に理解できる必要がある。
内部実装までトレースすることで挙動を確認することは現実的ではない。
要するに副作用など含んだこちらの意図していない挙動を示す実装など使い物にならない、ということ。

これはDDD本内部では言及はないが、いわゆるAPIドキュメントを公開するということもこのアプローチと相違しない考え方なのではないかと思う。
例えばJavaの実装ではJavaDoc、RubyではRubyDocなどのドキュメントの仕組みが充実しているので、それらを合わせて初めから開発をすすめるというのも取れるアプローチなのではないか。

副作用のない関数

こちらDDD文脈でなくとも実装をしているうちに何となくイメージができるものだと思う。
副作用がある関数というのはその気持ち悪さが言語化できなかったとしても、開発者は妙な気持ち悪さというものを感じているのではないか。

ここで一つ悪い実装例を提示する。
自分が保持している液体の容量を持つコップというクラスがあってそれはあとから液体を足すことができる。
アプリケーションとして別のコップから液体を移動することができる。

簡単化のためにメンバ変数をpublicにしている。
ここでものすごくイケていないのは、cup1にcup2を加えた際にcup2の容量まで変更されているということである。
この気持ち悪さを言語化しているのが「副作用」であり、クライアント側から想像していないような変化が内部で生じているという状態。

副作用は単体で悪さをしているがなぜそうなるかということはおそらくもっと広い視点で設計自体をないがしろにしている可能性が高い。
その設計モデルがドメインと全く適合していないのである。(最もこの例ではそれ以前の問題ではあるが)
そのためにこの章で合わせて説明される他のアプローチと合わせて対応する必要がある。

表明

副作用はできるだけなくしたほうが良いというのは先程の例を見れば一目瞭然だと感じていただけるはずだ。
ただしやむを得ずなくならないというようなパターンもある。
副作用を含んだメソッド呼び出しのことを「コマンド」と呼ぶが、それでもこのコマンドをどうにか担保するというのが「表明」と呼ばれる対応である。

表明はこれまでの概念と比べて実装的な側面が強い。
ぶっちゃけていうとユニットテストと言っても差し支えはないだろう。

副作用が起こる前後の「事前条件」と「事後条件」というものを定義して、コマンドを実施した際の変化を担保しろということ。
またこの表明が存在することによって対象ドメインの内部実装まで確認せずともそのふるまいが理解できる。
テストケースを見ればどういう挙動かどうか推測できるという感覚である。

また言語によってはこの表明(assertionと呼ばれる)という仕組みを実装していることがあり、その場合は実装中に含まれることになる。

この「表明」については個人的なところあまり頻繁に活用するものではないという印象がある。どうしてもやむを得ないときだけに抑えるべきだろう。
結局表明を確認しなければいけないほど内部の振る舞いがこじれているということは、設計に立ち返って新たな適合するドメインを模索する必要があるのではないかと思う。

概念の輪郭

インタフェース、クラス、集約、操作などを考慮する際に自分の直感と立ち向かって常にリファクタリングを行うことを惜しむな。
実装途中に特定の処理があるメソッド中にさながらトランザクションスクリプトのようになってきたらそれはモデルが適切ではなくなっている。新しいエンティティやバリューオブジェクトなどを切り出すということが必要になってくるだろう。

この概念の輪郭については実装当初から完全に洗い出して着手するということは、あまり現実的ではない。
特にそれに対する知識が不足しているような状況であればなおさらのことである。

実装しているうちに当初考えていたモデルが適合しなくなって来た際にはその直感を大事にして、新しい輪郭を検討する必要がある。

リファクタリングが小さな範囲に収まっている場合は大きな輪郭としては適切に設計できているということである。
それが大きな範囲になってしまった場合はそもそもの根底部分の設計が間違っていた可能性がある。
この際にも非常に大きなコストを払う必要があるが、輪郭を再度設計する必要がある。

現場にいると様々なプロジェクトで既存の実装を捨てて一から作り直すような、大規模なリファクタリングを選択するシーンがそんなに少なくないと思う。
そこに技術者が新しい技術を導入したいという意思が介在していることも大きいが、その一端には古いモデルを捨てて新しい輪郭に沿った実装を行いたいという意味合いも含まれると思う。
業務に詳しくなっていくうちに、それまでの設計がそぐわないことに気づくことはよくある。

独立したクラス

クラス同士が過度に依存していることはあまり好ましくない。
クラスの同士はなるべく低結合とするべき。

こちらもそもそも不必要な依存が大量に発生している場合は設計に立ち返って見るべきである。

閉じた操作

閉じた操作とはインタフェースに同クラスを与えて、返り値にも同クラスが返却されるようなインタフェースのことを指す。
この状態が出来上がると直感的には、そのクラスのみが変更されて更に変更された新しいクラスが返却されてくるのだなというイメージができる。
またそれによって副作用なども含みようがないので非常に明瞭である。

このアプローチは積極的に採用すべき。
逆説的だがこれまで上げたアプローチが達成できていれば、自然と閉じた操作ができるか、またはほとんどコストを掛けずに適用することができる状態になっているはず。

宣言的な設計

閑話休題。これまでの話と少しそれるのだが宣言的な設計というものがある。
「宣言的」の対義は「手続き的」であり、コンピュータは当然後者に親和性が高い。
人間が介在する際には、宣言的である方が自然であり、解釈しやすく、同然間違いも生まれにくい。という考え方である。

ルールを矯正することで宣言的に動作する代表的で馴染みのあるものはフレームワークである。
こういったモノは理論的には上手く機能するように思うが実際にはうまくいかないことも多々ある。
まず一つのリスクとしてはフレームワークが巨大である場合学習コストが高くつく、ということである。
しかもそれはプロジェクトなどに適用する場合全員がもれなく従う必要がある。
またそのフレームワークがドメインで実現したいことをサポートしなかった場合、何らかの形で対応する必要がある。
大抵その対応は醜いものになるだろう。

DDD本では適用するのは一部の効率的に機能する部分をサポートするようなフレームワークに留めるのが良いのではないか。ということを指摘している。

設計の宣言的スタイル

本章の目指すところ。
宣言的な設計を目下のドメインの実装に取り込むこともでき、これが達成できれば非常に人間にとって親和性の高い気持ちのよい実装が実現できる。
一行一行目を通してようやく実装を把握するような処理ではなくて、メソッド名をひと目見て把握できるような宣言とするべきである。
その末にはまるで述語を記述するような形でのメソッドが適用できるようになる。

解説を投げてしまうが本章で説明されている「仕様」の組み合わせ。の例は素晴らしい。
閉じた操作に則っていることで仕様からまた新しい仕様を動的に生成できる。
仕様を持ち回すことができ、コードのクライアントはまるで述語を記述するように実装できる。
これは言語を書いているようで、非常に直感的で人間と親和性が高い。

攻める角度(サブドメインを切り取る)

設計を行う際に、どういったアプローチを取るかというのは無限にある。
その中で切り出すドメインによってはとても適切であり、効果的に表現力を持てることがあることがある。

その最たるものとして「数学」がある。
そのドメインの中に内在する数学を切り取ることで他のドメインからは一切の数学を排除できる。
また数学は誰もが学習していることであり、学習コストが低い。

その他の特別なドメインを切り出す場合、それが一般的でない場合全員が学習する必要があり、またモチベーションなどの側面からも効果的ではない。


MVCというデザインパターンはもはや十分ではない

概要

今さら私が提示するような話でもないほど同じような話はネット上に腐るほど存在しているだろう。
にも関わらず不思議とシステム開発の現場レベルで見てみると、昔と変わらない手法をずっと続けていたり、また優秀なアーキテクトに恵まれていない現場などはひどく絡みあったコードと格闘していることは多い。
改めて近代的なデザインパターン・アーキテクチャとは何かということについて自分なりに詰めてみようと思う。

初めてに言ってしまうと、ずばりこの記事の着地は ドメイン駆動設計(DDD)への招待である。
同じような疑問を感じている人の何かヒントになればと思う。

人々はオブジェクト指向を手にした

昨今のソフトウェア製作の設計パターンは変化しているのだろうか、ということをまず考えてみよう。
「コンピュータの登場から今まで」のような大きなくくりで見た時には、ソフトウェアの設計パターンは大きく変化していると断言していいだろう。(といってもそんな昔に私は生まれていないが)

時代ごとに変化する大きな要因の一つに、偉大な先人たちが開発してきたコンピュータ言語がある。
昔はアセンブラ・C言語などの、手続き型言語が大部分を占めていた時代があるが、この頃開発においてドメインに着目するというよりかは手続きそれ自体に着目している。
これは言語のパラダイム的にやむを得ない部分が大きいが、いずれにしてもプログラミングされたその内容はそれぞれが意味をあまり持たない。個別には意味がわからない処理の連続が、結果として業務ロジックとして存在している。

その後時代は流れてついに人類はオブジェクト指向言語を手にする。このオブジェクト指向というのはプログラミングパラダイムの革命の一つと言っていいだろう。
オブジェクト指向によってドメインモデルと実装がかなり親和性を持って結合できるようになったのである。
そしてコンピュータ資源も豊富になってきた今、高級言語を使っていればあまりメモリなどの資源を期にすることもない。
実装都合でねじ曲げてきた手続き型への束縛からも開放されつつある。

これが爆発的となり、様々なツールなどが世にでる。sbtなどのマルチプロジェクトを取り替えるような機構ができたりして、その実装性能よりも、実装とドメインモデルをどれだけ近づけて、人間の脳に理解しやすいようにプログラミングを行う時代になっている。

MVCの誕生

Web業界ではそれまで多くのシステムは cgi と呼ばれる仕組みを利用してシステムを実装していた。
これは先程の話で言うとオブジェクト指向の誕生前で、割りかし手続き型の側面が強かった。

これがオブジェクト指向の出現によってMVCというデザインパターンの実装が用意になり、それをサポートする多くのフレームワークが誕生した。
多くの(初級)開発者たちはとりあえずMVCすればそれなりに構造化された実装を手にすることができた。

MVCの功として、まず多くの初級開発者たちにとって開発の目印になってくれたことである。
それまでとくらべてエンジニアたちの一種のヒントのようなトピックを投下して議論の対象となった。

また実際そのデザインパターンはシステムがあまり大きくないうちは非常に効率的に開発できる(ように見える)ためそれほど規模の大きくないシステムにとっては問題がそれほど露呈しない場合も多い。

MVCは銀の弾丸ではない

実際MVCのすべてが否定されるべきものではない、一種のアンチパターンとして利口なUIパターンなどというものがあるがそういった多くの場合はとらないでおくべき選択肢を序盤に排除してくれるような働きをしている。

ただ単純にMVCという一言で対応するにはシステム開発はそれほどシンプルではなかったという話である。

個人的に思うところは MVC の表す Model の責務が大きすぎるのである。
そのため人によって解釈が異なったり、またそもそもそこの設計を怠ったりということで大きなシステムに対応できなくなっているパターンをよく目にするように思う。

具体的によく目にするのが Model 内部にDBレイヤへのアクセス処理などが記述されていて、どんどんモデルが膨らんでいくなどのようなことである。

本来のクラスの責務をしっかり検討する必要があり、MVC を拡張するようなよりドメインとマッチした設計思想が必要なのである。

DDDへの招待

そこで DDD である。昔からあったのかもしれないが、最近特に多く目にするようになってきた。

DDD とはドメイン駆動設計の略称であるが、これはドメイン(業務ロジック)に特に着目して、それをいかに齟齬なく実装に落としこむかということに着目した設計手法である。

実際かなり具体的な設計思想を(先人たちの経験の蓄積として)もっており、これらを盛り込むことでシステムが大きくなってきた際にも拡張性がある実装を担保できる。

たとえば具体的には先程の話で一緒くたに Model と称していたものを Entity や ValueObject など(他にもいろいろあるが)いうようなより詳細なドメインモデルに落としこむ。
これによりMVCで取り扱っていたものを、より適切な責務へと分割することができる、、、などなど

その内容については長くなるのでまたの機会に割愛するが、DDDのバイブルである下記の書籍に目を通すと良いと思う。一度目を通しておいて損はないです。