[読書メモ]現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の実践技法 をざっくり読んでみた
最初に
現場で役立つシステム設計の原則を読んでいる際に、個人的につけていたメモです。
いわゆるまとめ記事ではなく、内容も飛び飛びで自分の感想もりもりですが、参考になれば。
本の感想ですが、タイトルの通り、現場に役立てるための理論的な部分と現実的な落とし込み方のバランスの良さが印象的でした。
言っていることは分かるが○○な時はあるくない?という疑問が次のページで解消されることが多かったりと、筆者のこれまでの経験や悩みから得た貴重な知識が平易な言葉語られておりとても学びとなりました。おすすめです。
第1部 ちいさくまとめて分かりやすくする
変数名は変に省略せずに、普通の名前を付けよう、共通するロジックは別のクラスとして切り出して関心事を閉じ込めよう。
ドメイン駆動に関する話が入っている書籍で必ず最初に出てくる基本事項だが、読むたびにやっぱ大事だなぁと思う。
特に、業務的に異常な値を持たないように制御するためにオリジナルのクラスを作成して、暗黙知となっているビジネス的なるルールをそこに閉じ込めるという話が良かった。
Moneyクラスとかはよく例に出てきておりそれはそれで分かりやすいのだが、個人的には電話番号の例がしっくりきた。
「数量」は単なるintではありません。「電話番号」は単なるStringでは ないのです。
業務の関心事に対応する1対1で作成して、関心事に関する処理を同一のメソッドにまとめる。そのためには、そもそも業務の関心事とはということになるのだろう。ここをミスって1対nになってしまい、神クラスが出来上がりそう。だからこそモデリングが大事、という話になりそうな気がする。
コレクションオブジェクトの実装で参照を返したくなる時には、いったん立ち止まってそもそも参照を返した後にやりたいことは何か、その処理をコレクションオブジェクト側に巻き取れないかを考えるというのが良かった。
第2部 場合分けのロジックを整理する
何かしらのクラス(変数一つとっても?)を使う側が知らないといけないことが多ければ多いほど、密結合になる。少なければ少ないほど疎結合になる。つまり、使う側が使用するクラスが暗黙的に所持しているルール・関心事を無理くり使う側が守ろうとすると、その分関心事が漏れ出すということなのかもしれない。
結合を弱くするための方法として、丁寧に値オブジェクトかして、そのうえでInterfaceを用いた設計にすることで、多態性を持たせることが重要だと解釈したが、あっているのかは正直分からん。
第3部 業務ロジックを分かりやすく整理する
三層アーキテクチャでは、データクラスという入れ物に業務ロジックを処理するために必要なデータを詰めて、プレゼンテーション層、アプリケーション層、データソース層が必要な時に取り出して利用する、というスタイルになりがち。特に業務ロジックを実際に裁くのはアプリケーション側に実装されることになるため、データとロジックが切り離される。その結果に多様なロジックがアプリケーション層のいろんなところで重複し、何処に難のロジックが実装されているのかが見えなくなってしまう。そこで、データとロジックを組み合わせて、アプリケーション層で実装していた内容を新しいクラスに切り出すことが再設計の第一歩となる。
単純に値を取り出すgetterを実装して、getterを利用したクラス側でロジックを組んでいるのであれば、そもそもgetterで取得してやりたかったことは何なのか、クラスの方に切り出してインスタンス変数を用いてクラス内で完結して結果だけを受け取る形にできないかを考えることが大事になりそう。第1部と同じ。
こうしてデータとロジックが組み合わされたクラスを作っていくとどんどん増えるので、パッケージ化して見通しをよくしようというそのあとの保守の話があったのもよかった。
第4部 ドメインモデルの考え方で設計する
むずい。かなり難しい。
第3部で書かれていたデータロジックをばらばらにせずに一つの関心事を扱うクラスにすることで、コードの重複を減らし変更に強い構造にすることの重要性に続き、具体的に業務フローを分析・設計してコードに落とせるようにクラスとしてまとめる流れについて書いてあったが、難しい。
書いてあることはどうにか理解できる一方で、普段自分が触っているシステムにどう適用できるかとかを考えてみるが、なかなかイメージがつきにくい。もともと業務的なドメイン知識が浅すぎるというのが大きな理由なのかもしれないが。とはいえ、ドメインオブジェクトはデータとロジックがまとめられたオブジェクトなので、あとから設計に変更があったとしても手戻りが少なく、ボトムアップ的に実装をして、サイクルを回すことができる、という話はハッとさせられた。分析をしっかりして、関心事の整理ががっつりできた段階で初めてコードに落とすというプロセスを経るものだと思っていたので、学びとなった。もともと変更に強くするための設計手法なんだから、当たり所がそこまで悪くなかったら無駄になることはないし、修正もできるというのはそりゃそうではあるか。
また、ドメインモデルを洗練させてクラスの命名と業務的な関心事が一致させるために、その領域の専門家が当たり前のように理解している暗黙的をチーム内で言語化してモデルとしておとすためのコミュニケーションの重要性について書かれていることも印象的だった。
これまでの章と同様、アプリケーションは業務的なお困りごとを効率よく解決するために存在しているため、そもそもの存在理由と解決したい課題、すなわち目的であり、いわゆる関心事をきちんととられることがやはり重要。なんだけど、これが難しいんだよなぁ。。。。
どうやって機能を実現するかに注目するのではなく、 ある特定の業務のデータとそのデータを使った判断/加工/計算などの業務ロジックだけを切り出した独立したオブジェクトを作ります。
ちょっとミクロな視点で考えると、ある変数があったとしてそれらに対して個別でバリデーションを行っていたり、何らかのビジネスルールに基づいて判断/加工/保存をしている処理が分散して実装されていないか、1つのクラスとしてデータとロジックをまとめられそうなものはないか、という視点で考えるとちょっと具体になるので理解しやすいかもなぁと勝手に納得した。
第5部 アプリケーション機能を組み立てる
アプリケーション層がドメインモデルとデータソース層を使いこなすことで、業務的なニーズに対応する処理を実現し、結果をドメインオブジェクトとしてプレゼンテーション層に返す、というのが基本的な考え方となる。
以下の役割の都合上、アプリケーション層は汚れがちとなる。
- ドメインオブジェクトが業務ロジックの置き場所として機能していない
- プレゼンテーション層の関心事に不倫わされる
- データの入出力の都合に振り回される
何か業務ロジックを実現したい場合、即座にアプリケーション層に実装してしまうのではなくて、正しいドメインオブジェクトに処理を実装できないかと考えることが重要となる。サービスモデルを作りながら、ドメインモデルを強化するタイミングを見つけて改善する、というサイクルを回していくことが肝要。
サービスクラスはプレゼンテーション層(特に画面)から呼ばれるため、不正な値が渡ってくる可能性が高い。しかし、それらの可能性を考慮しすぎて防御的なプログラミングにするときりがなく、不必要にコードが複雑化してしまう。
そこで、サービスを提供する側と利用する側で何を守るべきかというルールに基づいて呼び出し、呼び出されるように設計することを契約による設計という。この場合、業務ロジックとして絶対に守るべきところはドメインオブジェクトで防御するはずなので、NULLじゃないかとか、基本的な最低限のバリデーションをするのがバランスとしては良いのだろうか。
サービスクラスとリポジトリクラスにおいて単にCRUD操作をリポジトリクラスに実装して、それを単にサービスクラスが呼び出すというのはよろしくない。もともと、リポジトリクラスを用いて永続化することで達成したい関心事があって、それが達成されれば内部的にどう保存しているかはサービスクラスの関心事では本来ないはずだからだ。なので、リポジトリクラスのメソッド名には具体的な保存方法ではなっくて、業務的な目標を達成するために必要な操作を意味する命名をするとサービスクラス側にデータクラスの知識がにじみ出にくくなるのがGoodという話なのかな。
第6部 データベースの設計とドメインオブジェクト
良い業務アプリケーションを作成するためには記録・参照のためのデータベースの設計も同じくらい重要となる。良いデータベースの設計をするためには以下がポイントとなる
- 名前を省略しない
- 適切なデータ型を使う
- 制約をきちんと使う
適切なデータがを使う話は、とりあえず文字列の変数にいろんな意味合いを持たせるのではなくて丁寧にオブジェクト化して堅牢なつくりにする値オブジェクトの考え方に通じる部分がありそう。
制約の話の中で、カラムを追加したかったらテーブルを新しく作って、外部キー制約をつける方法が良い。新しくカラムを追加するために、それまでのデータに初期値的な虚の値を入れることは望ましくないというのが良かった。
データベースには正確なコトを記録することが重要で、状態は過去のコトの記録からおのずと算出できる。とはいえ、素早く現在の状態を確認したいニーズがあるので、そのために状態を記録するテーブルを作成して、コトの記録が発生したタイミングで通知し、更新を書けるという風に処理を切り分ける話が出てきて、イベント駆動がでてきたのがとても分かりやすかった。
このように、ほかの章もそうだが、これが理論的に正しい一方で、業務的なニーズでこういうところがあるから現状のシステムになっていることを理解したうえでよりよい方法を提唱する、という構図がとても良いなと感じている。著者の増田さんがこれまでどのようにして苦しみながらシステムを改善してきたかの気持ちの部分がにじんでいて個人的にはGoodだった。
第7部 画面とドメインオブジェクトの設計を連動させる
画面とドメインオブジェクトを連動させる設計にすることで、コードと画面が対となり、改修しやすくなる。そのためには、汎用的な画面にあらゆる機能を押し込めるのではなくて、小さくタスク単位で画面を切り出してドメインオブジェクトとの関連性が見やすくすることが求められる。
ドメインオブジェクトに書くべきロジックとプレゼンテーション層に書くべきロジックの切り分けについて、例えば画面表示であればhtmlの仕様に基づいた実装にドメインオブジェクトが引っ張られたり、ドメインオブジェクト内にhtml特有の実装が漏れ出すのは良くない。しかし、情報を単に文字列で表現するような場合においてはドメインオブジェクトの関心事となるため、最終的には画面などに用いられるロジックでも、ドメインオブジェクトに実装することが適切だという考え方が良かった。
例えば、あるドメインオブジェクトでは、特定の条件を満たしているかを判定するロジックを実装しておき、返ってきたフラグ値に基づいて表示するテキストをプレゼンテーション層側で組み立てるというのも方法としてはあるが、次第にプレゼンテーション層側にドメインオブジェクトの業務ロジックがにじみだしていくので避けたほうが良さそうだと理解できた。
第8章 アプリケーション間の連携
APIはアプリケーションを組み立てるための部品を提供する役割を持つものと定義すると、アプリケーション側の要望に応じて登録と参照を同時に行うAPIを提供するのもよいが、登録と参照用のAPIをそれぞればらばらで提供してアプリケーション側で組み立てるとアプリケーション側で柔軟に機能を組み立てることができるので良いという話があった。これは、登録と参照が合わさってアプリケーション側から詳細が隠れている状態の方が都合が良いというメリットもあるのでバランスだと思うが、記載されているように、複合的なサービスの提供はサービスを提供する側にアプリケーションを利用する側の知識が漏れ出してしまい、アプリケーション間の結合度が上がるので避けたほうが良いという主張は納得感があった。
あくまで「APIはアプリケーションを組み立てるための部品」ととらえて直交性の高い機能を提供することをまず中核に据えることの重要性が理解できた。
第9章 オブジェクト指向の開発プロセス
細かい仕様をドキュメントに起こすのではなくて、関心事を正しくとらえてコードに落としていくことでコード自体が一線級のドキュメントになる。
なぜなら実装されているクラスやメソッドの名前がそのまま業務の用語や概念と一致するようになるからだ。
業務的な内容とコードの一致度合いが高いとおのずと抜け漏れが何処にあるかがわかるようになるため、進捗管理もしやすい。コードベースでどこまで実装できていて、何処が実装できていないかが分かるからだ。これは各機能がドメインオブジェクト隣きちんと分割されていることにも起因するのだろう。
第10章 オブジェクト思考設計の学び方と教え方
長いクラス内の実装を少しずつ外に切り出してメソッド化したり、小さいところから少しずつリファクタリングしていくことで次第に見通しの良いコードができていく。小さくリファクタリングするためには、部品単体として機能するドメインオブジェクトと考え方がないとしっくりこないところでもあるので、体系的に学びながらそのうえで手を動かして感覚を養っていくスタイルが良いのかもしれない。
まずは自分でメンテナンスをしているDiscordのコードをリファクタリング師ながら練習していくのがよさそうだなと感じた。
Discussion