オブジェクト指向 アーキテクチャ モデリングメモ

以下クラス分けについて

情報や実物からの視点

・メソッドから内部のデータを全て使う。高凝集度の単位。
・コンストラクタで必要なデータを渡してしまう。

ドメインオブジェクト

ドメインオブジェクトの粒度について、注文クラスでは粒度が大きく、商品、数量、金額などに分けるべきという考え方もある。

値オブジェクト
AmountやCurrencyなど原始的な単位でクラス化する。

コレクションオブジェクト
コレクションにはadd等基本的な操作があるが、業務用の操作を加えてラップする。

区分オブジェクト
ifで処理するような区分が出たらクラス化しインターフェースで扱う。

状態遷移のマップ化
とりうる状態をマップ等で管理する。

変更や拡張性からの視点

・変更する理由が1つだけ。(単一責任)ただし、最初から理由が複数存在するか分からないのでリファクタリングしてクラス分け。

・何かを変更や拡張をしようとした際に影響がある単位。(変更や拡張がまったくないのであれば単位は大きくても問題ない)

・ポリモーフィズムの単位。(型のカプセル化)流動的要素をクラスにしてインターフェースでカプセル化する。(クライアントは実装を知る必要がない)

流動的要素から

・単一責務をもう少し具体的にすると、クラスに1つだけの流動的要素とする。流動的要素とは現実世界には複数の選択肢があり、それらが追加される可能性があること。

・流動的要素はメソッド(機能)ではなく主にデータ。

クラス分けについて、ここまで。

継承について

・継承の代わりにインターフェース。継承での再利用はフレームワーク等でのみ有効。継承は縦(isa)だったが、インターフェースで役割を分割して横の関係で再利用。

依存について

・クライアントが使わないメソッドに依存させない。(インターフェースを分離)役割を細かく分離することで役割が明確になる。

・newするとそこに依存する。インタフェースに依存させる。早い段階で変更がありそうな領域をクラス化しておく。

オブジェクト生成とオブジェクト利用はそれぞれ別のオブジェクトにする。(ファクトリー)

利用側はインスタンスをインターフェース型で受け取る。生成側はインターフェースを実装する。インターフェースの定義は上位レイヤーにて行う。(依存性逆転)

以下はアーキテクチャ。

MV系の基本

MVC/MVP/MVVM等、基本はMに対するVの抽象化。
プレゼンテーション、ドメインの分離。

GUIの場合、VCが分離できないので、VC+Mとなる。
Document-ViewやModel-Viewなど。ただ、Vが肥大化しやすい。

MVC

いくつかの考え方

単純なデザインパターンの一種(オブザーバ)と考える意見もあるし、レイヤーアーキテクチャと類似点から、プレゼン層からインフラ層まで対応関係にあるという意見もあるし、さらに、レイヤーアーキテクチャのプレゼン層の中にMVCがあるという考え方もある。

CからMに依存するのは共通だが、VとMはいくつかの考えがある。
・VからMに依存するタイプ(VからMを直接見に行く)
・MからVに依存する(VがMを監視するオブザーバパターン。これが正統派)
・MとVが依存しない(すべてCを経由させる。ASPMVCとか)

上記を踏まえ一般的な考え方。

・Vは静的なテンプレート程度で、JSONなど動的生成を呼出すだけならCからのみでVが無くてもいいぐらい。Cにリクエストを通知。

・CはMに操作を流すだけでMの状態を管理しないというのが正統派。リクエスト処理、モデル操作、ビュー操作、DBトランザクション、DTO作成等をCで行うとする考え方もあるし、それらをサービスレイヤーにするべきという考え方もある。

・MはDBアクセス、ドメインロジック等、CとVで行うこと以外。

MVC+サービスクラス

CからMのアクセスにサービスレイヤーを挟む。

具体的にはCでモデル操作として実装していた部分を抜き出し、CからSのメソッドを利用するような感じにすること。

ここではMVCにプラスサービスクラスという意味だが、レイヤー系の話の中でアプリケーション層をアプリケーションサービス略してサービスというように扱う場合もあるので注意。

MVC実装例

様々な考え方もあるのでWeb系フレームワーク等の標準的な一例。

・Cは1つのクラス。そのメソッドがアクション。主にリソースがクラスでCRUDがメソッド。
・アクションの1つにVが1つ。あるいはアクションの1つに機能が1つ。
・vはテンプレートで、機能は計算の埋め込み程度。
・CV以外はMに実装。Mが肥大化したMのレイヤー化。
・DBの取扱はテーブル1つに1つのクラスでインスタンスが行となる。

MV系のM

ドメインロジックのパターン。

・トランザクションスクリプトパターン。
画面表示に必要な単位で使い捨てのオブジェクトを返す。

・ドメインモデルパターン
画面とは対にならないモデル構造があり、そのオブジェクトを更新する。

・テーブルモジュールパターン
テーブルとクラスを対応させる。

DBの処理方法

・テーブルデータゲートウェイ
テーブルとインスタンスを対応させる。

・行データゲートウェイ
行とインスタンスを対応させる。

・アクティブレコード
行データゲートウェイに近いがドメインロジックでラップ

・データマッパ
テーブルとオブジェクトの対応を責務とするレイヤー

リポジトリパターン

データマッパの1つとして。

一般的にはドメイン層のDBアクセスの抽象化となる。(アプリ層からもDBアクセスを許している場合はそれも含む)

基本はモデルベースで、crudを実装する。必要なモデルごとに作る。内部でインピーダンスミスマッチを吸収する。

DB操作のインターフェースをドメイン層に配置し実装を永続化層で行う。(DI)

MVP

Vの変更トリガーは正式にはMだけなので、Mまでいかないような非ドメインロジック(UIの色を変えるとか)を持つ場所がない。そこでMVPとなる。MVPはMまでいかない非ドメインロジックをPで管理する。MVPに似たパターンとしてMをプレゼンテーションモデルとドメインモデルに分ける考え方もある。

MVVM

GUIのVC+MはVCが分離できずVCが肥大化しやすいためVCをVとVMに分けるという考え方。結局MVCに近いのだけれど、ただのCではなくVのモデル化(VM)はバインディングで自動でやることが大きな特徴。(手動でも通常モデル化するので楽になるし)VとVMをデータバインディングすることで、VとVMの依存を薄くしつつVに必要なロジックをVMに持たせることができる。

MVPの変化パターンとして考えると分かりやすい。(XAMLなど実装環境前提)MVPでのPと同じ責務をVMが担当している考えに近い、基本はプレゼンロジックと、Vの状態管理がVMの責務。

レイヤーアーキテクチャに関わること

環境によって用語や考え方が違う。共通しているのは依存関係をコントロールすること。だいたい、ドメインからの依存を禁止してその他の層からドメインへの依存はOKとするか、上部から一方通行の依存のみOKとするか。上部からの一方通行だけど、アプリとドメイン2つからインフラへの依存をOKにするか場合もある。

各層のデータのやりとりについて、各層はDTOでやりとりする場合。ここでのDTOは主にモデルオブジェクト。または、業務用データオブジェクトでやりとりするという意見もある。業務用データオブジェクトは、decimalをラップした受注金額クラス等のようなイメージ。

アプリとプレゼンは1対1でバインド。

・プレゼン(UI)

・アプリ
下位層に行く必要のない処理や、下位層へ指示の調整。
トランザクション管理

・ドメイン
ビジネスルール

・インフラ
DBアクセス
あるいは技術的な要素をここに集める場合もある。

MVCとレイヤーアーキテクチャの関係

MVCとレイヤーに対応関係があると考える場合。(前述のように諸説あるが)

プレゼン=VC
アプリ=S
ドメイン=M
インフラ=M

CQRS

更新系と参照系のモデルを分け、参照系は単純化する。

ドメインオブジェクトとアーキテクチャについて

MV系のMにいるべき、レイヤーのドメインにいるべきというのが基本だが、DTOのようなものを作らずにドメインはどこからでも使えるべきという考え方もある。

DBモデリングメモ

・トランザクション系データからマスタデータを参照する場合、履歴を管理する必要を考える。
・コードに意味を持たせる場合(何行目が部署とか)変化に対して弱くなることを意識する。
・1対多などの関係はnullを許容するかどうか考える。
・多対多は関係テーブルを設ける。
・明細は別テーブル。
・階層構造は上位名称を入れておく。
・ロール。(取引先として、顧客か仕入先を別テーブル)
・依存関係、FKがある方が依存している。依存している方が子。PKは重複できないけど、FKは重複できるので、FKある方が多。

・複合主キーとなっている関係従属を取り除く正規化は当たり前に発生するので、正しいモデリングをすると必然的に複合主キーが現れる。オブジェクトに合わせてサロゲートキーを入れる場合、制約はアプリ側で対応する。例えば仕入価格を、仕入先・商品・開始年月で識別するような場合。これは複合主キーなので、組み合わせが重複してはいけないし(ユニーク制約)更新されてもいけない(更新制約)サロゲートキーを導入するならアプリ側で対応する。

・業務アプリにおいてクラスとDBモデリングは近いという前提でスタートすることはできる。(当然、リレーションや継承などそれぞれに特有の考え方はある)ただし、クラスの場合重要なのは関心の単位、変更の単位という視点であり、DB上ではただのカラムになるような情報がクラスに切り出される場合もある。

・テーブルは永続化されるがクラスはされない場合もあり、範囲としてはクラスの方が広い。なので、テーブルをそのままクラスにはできてもクラスをテーブルにはできない。

・テーブルがそのままドメインオブジェクトで、テーブル操作に必要なメソッドは全て自分で持つという方法もある。(テーブルゲートウェイ)ただし、業務上の関心の単位でドメインを作る場合、テーブルと同じだとクラスの粒度が大きすぎる。