人間のあるべき姿の探索

思索・人形・エンジニアリング

『現場で役立つシステム設計の原則』を読みました

はじめに

現場で役立つシステム設計の原則を知りたいと思っていたのですが、丁度現場で役立つシステム設計の原則について言及されている書籍があったので読みました。

gihyo.jp

ある程度知名度のある書籍で、QiitaやZenn等でまとめられている方がいらっしゃるのですが、自分のアウトプットとして、感想も交えてまとめていきます。

全体の話

この書籍の雰囲気や見通しを立ちやすくするために、参考書籍の一覧を抜粋して紹介します。

『エリック・エヴァンスのドメイン駆動設計ソフトウェアの核心にある複雑さに立ち向かう』『新装版リファクタリング既存のコードを安全に改善する』『SQLアンチパターン』『エンタープライズアプリケーションアーキテクチャパターン』『エクストリームプログラミング

システム設計の全般を対象にしているのですが、ベースの思考としてはオブジェクト指向プログラミングから発展して、ドメイン駆動設計を紹介する形になります。前半はドメインオブジェクト等具体的なプラクティスの話をしてドメインに注目する意義について紹介しつつ、永続化、画面、外部接続とそのドメインが出ていく先に話を広げ、最後にオブジェクト指向開発プロセスといった流れになります。

前半のドメインの話について、システム設計における問題点を挙げつつ、ボトムアップにそのプラクティスを紹介している印象を受けました。最初から具体的なプラクティスの通名を使用せず、あるオブジェクトのコレクションのみをプロパティに持つクラスを作ると紹介してから、それをファーストコレクションクラスと呼ぶ、みたいなあくまで問題とその解決に主眼を置いた書きっぷりでした。

逆に、以前成瀬さんの『ドメイン駆動設計入門』を読んだ際は、DDDをC#のコードでなぞる形で、トップダウンに具体的なパターンの適用方法を紹介されている印象だったので、対比する形で読みました。こちらは実際手を動かす際にパターンをコードに落とし込む勉強になりますし、記述する際のトレードオフについて丁寧に述べられているので、ぜひ読んでみてください。

www.kinokuniya.co.jp

その後の話はベースをDDDというか三層モデル+ドメインの構造を軸に置きつつそれぞれにある程度特化した議論をされており、システム設計の全体を見通す点で参考になりました。もっと知りたい場合は参考書籍を見に行くと良さそうですね。

Chapter 1 小さくまとめてわかりやすくする

最初はローカルなクラス・メソッド設計レベルの話です。オブジェクト指向設計において、メソッドは短くする、クラスを小さく分割する、値オブジェクトでプリミティブ型をラップするみたいな話が出てきます。

値オブジェクトの話が出てきたところで、オブジェクト指向→DDDの繋がりを感じるのですが、クラスはデータとロジックとなる前提の元、値もただのデータではなくその値の値域や制約に関するロジックを持つ、という点が印象的でした。

Chapter 2 場合分けのロジックを整理する

場合分けのロジックはコードを複雑にする、という原則の元それを回避するプラクティスが紹介されます。例えば早期リターンやガード節を使う、elseを使わず単文を連ねるといったテクに加えて、インターフェースによる多態を活用したものがあります。多態って何が嬉しいの?という気持ちはあったのですが、場合分けはロジックを複雑にするから、外側はPerson.Fee()として内部的にPersonを継承したAdult、ChildにFee()メソッドを持たせると外側で場合分けが要らないね、といった感じで腹落ちしました。他にもenumを使えば外側から判定メソッドの引数にenumの値を渡してやることもできます。

個人的に面白かったプラクティスとして、状態遷移を扱う際にその遷移の可否をif文ではなく、コレクションのanyで取得する、といったものがありました。例えば状態ABCがあった時に、canTransition.Add("A", TransitionTo("B")); canTransition.Add("B", TransitionTo("A", "C"));のような形で状態ごとに遷移可能な対象のセットをコレクションに保管することで、そこからAはcanTransition.Get("A")とすればAが遷移可能な状態のセットが取得できて判定ができます。

Chapter 3 業務ロジックをわかりやすく整理する

ここからドメインオブジェクトが登場してかなりDDDっぽい話になっていきます。

重要なポイントとして、データクラスと機能クラスを分離することの問題点が挙げられています。これらを分離すると、データクラスに対する業務ロジックがどこに書かれているか分からなくなり、重複が増え、探すにも見通しが立たなくなります。そこで、業務ロジックは各オブジェクトに紐づく形で同じクラスに記述する、それがドメインオブジェクトになるといった説明がされています。DDDの方針だと集約をまたぐ場合などにはドメインサービスが用いられますが、ドメインオブジェクトは単にモノに対する機能ではなく、業務におけるコトも包含するのか、と納得しました。

また、ドメインモデルにあつめる業務ロジックとして、判断/加工/計算が挙げられており、基本的にはそれらのセットでドメインモデルが構築されるという軸で物事を捉えていくと良さそうだなと思いました。

Chapter 4 ドメインモデルの考え方で設計する

ドメインモデルは業務ロジックをオブジェクト指向で整理する技法、とまとめられています。なるほどね。

業務の関心はヒト・モノ・コトとのことで、モノとコトの概念の分け方はドメインオブジェクトの扱いにおいて大きなポイントだと思ったのですが、業務としてみるとヒトも関わってくるようです。思い返してみると、データフローダイアグラムではアクターとオブジェクト(というよりシステム)、そして業務が関連している様子が描かれていて、この三つが登場人物なのだと分かります。逆に、この観点ではUSMやユースケース図ではモノが抜け落ちており、やはりユーザー側視点に近いように感じます。

ドメインモデルの設計については色々プラクティスがありますが、例えば業務のルールとして起きてよいこと、起きてはいけないことの判断と対応が挙げられています。値オブジェクトやエンティティ、業務ロジックにおいてその値やフローの整合性の検証が入るイメージですね、例えば残高がn円以上であることを検証して、OKであれば支払いを実施する、みたいなルールが記述されていきます。また、観念的な部分で実践しないとつかみづらい話として、業務の言葉を正しく覚え、使えるようになることが良いドメインモデルの設計に直結する、という教えがあります。業務の言葉でコードを書け、ということになりますが、こればかりは書いてみないことには難しいですね…後半でも述べられますが、結局業務とコードを一致させるには開発者が業務のモデル化に参加しつつその議論をブラッシュアップしていく必要があるので、ただコードを書く作業ではなく開発フローの設計の話になるので、ステークホルダー全体での認識を統一する必要がありそうですね。

Chapter 5 アプリケーション機能を組み立てる

前章までがドメイン層の話で、ここからアプリケーション層の話になります。基本的な概念としては、アプリケーション層がドメイン層に閉じ込められた業務知識を組み立ててサービスとしての業務を作っていく、という感じですね。ありがちな問題として、アプリケーション層であるサービスクラスにロジックが書かれてしまう(ドメインの流出)問題がやデータ入出力・プレゼンテーション層の都合に振り回されてしまうことが挙げられており、分かりみが深いですね。

前者については、ドメインモデルはブラッシュアップされるものなので、最初はサービスクラスに書いてみて、だんだんとドメインモデルに移行していったり、ドメインモデルを成長させる必要があることが述べられています。また、プレゼンテーション層からの要求が複雑になる問題に対するアプローチとして、サービスクラスを登録系と参照系に分ける提案がされています。最近CQS(Command Query Separation)、CQRS(Command Query Responsibility Segregation)について調べていたのでこういった分離が便利であることを知っていたのですが、これは永続化の段階だけではなく、業務の種別としても確かに分けられるものだな~と感心しました。勿論プレゼンテーション層から与えられる業務の指示は参照・登録の両方を必要とする場合もあり、例えば残高を参照して支払い可能なら支払い情報を登録するといったものがあるので、参照・登録の各サービスを順番に呼び出すBankAccountScenarioクラスが作られたりして、そこがファサード的に呼び出される形になることもあります。

また、データ入出力の都合に引っ張られる問題ついては、アプリケーション側でインターフェースを宣言し、それをデータ入出力側で継承させることで、逆にアプリケーション側の都合を実装させる方法を紹介しています。レイヤードアーキテクチャで依存性の逆転を用いてドメイン層でリポジトリのインターフェースを定義するイメージですが、確かに三層モデルではアプリケーション層の下に直接データ入出力の層が来るのでそうなるんですね~。

Chapter 6 データベースの設計とドメインオブジェクト

重要な点として、データベースの設計はオブジェクトの設計とは異なる場合があることが挙げられます。ドメインモデルはロジックにも注目していますが、データベースはあくまでデータの永続化、取得、更新のしやすさに重きを置いているので、それに最適化された設計が必要になります。

また、RDBを前提としたSQLの基本的な話として、NOT NULL、一意性、外部キーの制約はつけようねという話もあります。

個人的に面白かったのは、テーブル設計のコツとして「コトの記録」を徹底させるという話でした。これは一般的にイベントソーシングと呼ばれていて、状態管理が必要なレコードについて、該当レコードをUPDATEせず変更の履歴をどんどんINSERTしていく方式です。これによって、過去の状態は過去のレコードを参照すればよく、現在の状態はレコード群をリプレイするように流していけば算出できます。

イベントソーシング、CQRSと相性が良くてコマンドモデルで状態を変更する時に既存レコードを上書きしないので、非同期的にクエリモデル用のビューを生成できたり、都合が良いんですね。

learn.microsoft.com

余談ですが、CQRSはコマンドとクエリを分けることで先の章でも言及されていたように概念の切り分けとして明確になることで業務の表現やプログラミングをやる上で良いですが、それに加えてコマンドとクエリのそれぞれをアーキテクチャ的に分離させることで別々にスケールさせたり、そもそも記録用とビュー用に別々のデータベースを用意することもできるんですね。以前も何かのブログに書いた記憶がありますが、CQRSってDDDによって明確化された概念の気がしていて、DDDの思想ではデータの参照・更新はドメインモデルに基づくという前提があって、それに対して業務都合ではクエリに最適化されたビューが欲しいから、専用のクエリモデルを作った方が良いのではないか?という課題感があるように思います(これに関してはCQSでも同様ですし、単にクエリモデルの話とも言えますが)。

Chapter 7 画面とドメインオブジェクトの設計を連動させる

個人的に驚いた章でした。画面としてばフロントエンドの都合でレイヤードでも三層+ドメインモデルの形でも基本的には独立しているのかなと思っていたのですが、システム設計全体の視点としては、画面設計も業務に則るのだから、画面の構造とドメインの構造は一致しうるのではないか?という話です。

ただ、前提として画面設計をオブジェクト指向に則って実行したうえでの一致というのがあり、画面ベースで機能を作っていくと複数の画面に対して同じコードが重複してしまったり、画面の表示ロジックの中に業務ロジックが混じってしまうといった問題が起こります。逆に、それを避ける為にも、画面の表示ロジックを抽出したうえで、そこで呼び出される業務ロジックがプレゼンテーション層もしくはフロントエンドに書かれるのではなくドメインモデルに閉じ込められるのではないか、ということですね。

Chapter 8 アプリケーション間の連携

一つのアプリケーションとして完結したところで、外部接続についての話です。アプリケーション間の連携は基本的にファイル転送・データベース共有・Web API・メッセージングの4つに分類されており、それぞれの特徴が簡単に紹介されています。この書籍では主にWebAPIに注目しており、HTTPリクエストの基本的な設計の話がされています。RESTful APIで調べると出てくるような情報に加え、オブジェクト指向の前章までの流れに沿った内容としては、APIはあくまで利用側がアプリケーションを組み立てる為の部品のセットなので、細かい単位で提供することが原則となり、密に連携するシステムの場合はファサード的にそれらをまとめたAPIが提供されることもある、といった点が述べられていました。

また、ドメインオブジェクトとWebAPIの比較として、後者は別アプリケーションの関心ごとに着目するために互いに不一致が生じる可能性が指摘されています。アプリケーション内ではドメインモデルを使用することを推していたのですが、他アプリケーションに関しては専用のレスポンスオブジェクトが用意されます。永続化についてもドメインオブジェクトから永続化用のコンテキストモデルへの変換DTOを作ったりするので、やはり関心ごとが異なる場合は間にマッピングする処理が挟まるのだなと思いました。

そのほかには、JSONはシンプルなデータ構造に適しているので複雑な情報をやり取りする場合はXMLを使う、非同期メッセージングを使ったアプリケーション連携のやり方などが紹介されていました。

Chapter 9 オブジェクト指向開発プロセス

ここからは開発プロセスの話なのですが、DDDというかオブジェクト指向の良さを生かすためにドメインモデルの分析と設計を一体にして、それらを時間的にも要因的にも分割させないことを主張していました。

また、自己文書化等についても言及がありましたが、ここまでやるとなるとやはり運用プロセスとして明確にこの思想を押し切るための合意が必要であり、なかなか難しそうだなと思いました。

また、このプロセスをやるにあたってはコードが欠けるだけではなく、対象業務の理解と整理に意欲がある技術者を選んで育成しましょうという指摘もあり、『ジョジョの奇妙な冒険』5部でブチャラティが「プログラムも書く、業務も理解する、両方やらなきゃいけないのがエンジニアの辛いところだな」と言っていたのを思い出しました。

Chapter 10 オブジェクト指向設計の学び方と教え方

ここは一般的なオブジェクト指向の説明って内容も動機も分かりづらいよね…という指摘をしつつ、前半の章のまとめ的な役割も兼ねつつ学び方と教え方を紹介していました。

終わりに

ここ一年くらいはDDDのことを気にかけていたので、そういった面でもかなり面白く読めた書籍でした。実際システム設計をするにあたってはこういったプラクティスを実践するためにステークホルダー全体の認識を合わせなければいけないので難しいですが、意識として業務も理解することや各レイヤー内やレイヤー境界におけるプラクティスを実践できるとかなり良いな、と思いました。とはいえ、こういった知識って座学的知識とコードの記述による練習を要するので、レベル感を合わせるのが大変だな…とも思った次第です。