PoEAAをがんばって読んだ
https://www.shoeisha.co.jp/book/detail/9784798105536
動機
ネット上でよく話題になる、ValueObjectやService層などの議論の内容を正確に理解するため、前提とされている知識を得たかったから。
各所でやっぱりエンジニアって基礎が大事みたいな話を見かけるようになった中で、Webの設計パターンの基礎がこの本かなと思ったため。
読み終わるまで
以前頭から200ページぐらい読んだことがあったのだが、読み方が良くなかったのか全然頭に入ってこなかった上に途中でやめてしまっていた。
デザパタや設計に関して色々調べてるし今ならスラスラ読めるんじゃないかな〜と思って頭から読む形でリトライしたが、すぐにわからない語彙が溢れて目が滑るようになったので途中で下記のように方針を変えた。毎日1時間読んで一ヶ月くらいで読破した。
- 毎日1時間1,2章を少しずつ読む
- 分からない単語が出てきたら読むのを一旦中断して、その単語について解説している章を読む
- 書いてある内容の中で知らなかったこと、納得したことを自分の言葉で言い換えてみる
- できるまで繰り返し読む、ChatGPTに聞く、インターネットで調べる を行う
これは、前回のシステム設計の面接試験を読んだ
のときに手応えを感じた手法で、なるべくゆっくり読むことを意識した。
読んでいて難しく感じたところ
- XMLとかSOAPとか、現在では主流から外れてしまった技術が推奨されているのでピンとこない
- サンプルコードがJavaとC#なのは良いのだが、JavaEEとか.NETを前提にしており、エンティティBeanがどうこう言われても何をするコンポーネントなのか知らないし、書籍の中ではできないことになっている機能も現行では使用可能になっていたりする
- 訳のせいなのか分からないが、1行前に述べたことと反対のことを言っているように見える記述が結構ある(読解力の問題かとは思うが…)
- 自分側の前提知識がRailsに偏っているため、ActiveRecordやApplicationControllerなどの定義が食い違う
- 章の構成がTableDataGateway => Gatewayみたいな順番になっていて、大元の概念の前に具体的なパターンを出されるので後半肩透かし感がある
読みすすめる上で特に参考になったページ
-
- 各概念を対立構造で要約してくれているので読後の理解度チェックに役立った
-
パターンカタログ(日本語) - Martin Fowler's Bliki (ja)
- レビューで特に訳書のパターン名の揺れ(特にロック関係)について指摘されていることが多かったため、語彙のすり合わせに使用した
読書中のメモ
p.21 MartinFowlerはアプリケーションをPresentation, Domain, Databaseの3レイヤに分けられるとし、PresentationとDatabaseには類似性はあるが、使用される側、する側という点で非対称性があるとして、ヘキサゴナルアーキテクチャような対称性のあるレイヤ化スキーマと区別している
ヘキサゴナルとオニオンとクリーンアーキテクチャの説明のときによく出てくる図の違いが分からなくなったので調べた
ヘキサゴナルは外部(DBとかGUI)と内部(アプリケーション)の2層に分けてその境界にアダプタを設置することでアプリケーションを外部の変更から守る
オニオンはドメインを守るというのはヘキサゴナルと共通しているが、各層の分割が細かい(インフラストラクチャ、アプリケーションサービス、ドメインサービス、ドメインモデル)。また、内側の層がinterfaceを用意して外側の層に実装を強制することで依存関係を逆転させている
クリーンアーキテクチャを説明するときによく出てくる図は各層の中が更に分割されている(リポジトリとか)
なんか普通のレイヤードアーキテクチャの中にも下に向いてれば層またいで依存していいよ派と1階層下のレイヤにだけ依存していいよ派がいるのでややこしいが、オニオンとクリーンよくある図は層をまたいでもいい派
ヘキサゴナルアーキテクチャ(ポートアンドアダプター)とは何か #設計 - Qiita https://qiita.com/cocoa-maemae/items/b08c4cf95d47e314e2dc
Hexagonal Architecture(ヘキサゴナルアーキテクチャ) とは https://zenn.dev/heyyou/articles/f380adb8d1fe8f
オニオンアーキテクチャとは何か #設計 - Qiita https://qiita.com/cocoa-maemae/items/e3f2eabbe0877c2af8d0
p.22 ドメインロジックであるかを考えるときはUIが複製されたときにそのロジックも複製されるかどうかで考える
WebとCLIの入れ替え、RDBとファイルの入れ替えを考えてみる。例えばブラウザ上で売上が伸びているときに赤で強調表示する、という機能があったとき、CLIではどうなるか。売上が伸びているかの判定と、伸びていたときに赤く表示する、の2段階になっていると考えて、後者はCLIでは不要な機能なので前者をドメインロジックとする
p.25 複雑性のブースター
分散、明示的なマルチスレッド化、パラダイムの食い違い、マルチプラットフォーム、高いパフォーマンス要求などがあると複雑性が一気にあがることを指して、これらを複雑性のブースターと呼んでいる
p.38 ドメインが複雑になりアクティブレコードが崩壊したときはデータマッパーを使いDBとドメインを分離する
小さいクラスにドメインロジックを組み込むとテーブルとドメイン1:1の組み合わせが失敗し始める。ゲートウェイでDBとドメインを分離しても良いが、結局DBのスキーマとドメインは結合したままになってしまうので、データマッパーを使ってドメインモデルをDBから完全に分離する。
段階を踏んで分離しましょうという話かも。今RailsのプロジェクトではActiveRecordのクラスを大きくするよりテーブルを分割しhas_oneのモデルを作って肥大化を避ける、みたいなことをしてるけど、これもパフォーマンスの問題は出てくると思うので…でもそうなったら一時テーブル作るとか、チューニングしたクエリを使ってActiveRecordを自分でインスタンス化するとかするかも。データマッパーはあんまり自分では作りたくないかな…
限界との向き合い方、みたいな項目が参考になりそう
Active Recordから考える次の10年を見据えた技術選定 / Architecture decision for the next 10 years at PIXTA - Speaker Deck https://speakerdeck.com/yasaichi/architecture-decision-for-the-next-10-years-at-pixta?slide=35
あとついでにコマンドクエリの分割について調べたら、CQSとCQRSという単語があってメソッド単位分割と層分割があるというのを知った。メソッド単位で分割する、はあんまり見たことないのでよく出てくるのはCQRSの方かも
CQSとCQRSの違いはメソッドの分離かモデルの分離かという観点 #設計 - Qiita https://qiita.com/hirodragon/items/6281df80661401f48731
p.41 Unit of Work
ビジネストランザクション内での変更作業の記録をとって1つのクラスで管理することで、書き込みの最適化と並行性の問題を解決する。
Apexのリファクタをするときに fflib-apex-commonで出てきた記憶がある。New, Dirty, Clean, Deletedを管理して、トランザクション、コミットをコントロールする。RailsのActiveRecordにもdirty?とかchanged?とかあるが、こちらはより広範な観点から変更を管理する
p.44 オブジェクトとテーブルの依存関係は逆転している
あるオブジェクトが一対多の関係を持つときコード上ではコレクションを持つ、という形になっているがテーブル上では参照は子テーブルの方にある
言われてみれば当たり前なんだけどこういう所がデータマッピング複雑さにつながるのかなと思った
p.46 更新のトポロジカルソート
更新時の参照整合性について、現在はトランザクションが終了するまで参照整合性を保留することができるが、それがなかった場合DBは書き込みごとに照合を行う
このとき適切な順番で更新が行われるようにしなければならないが、その技法の1つとしてトポロジカルソートがでてきた
トポロジカルソートは有向非巡回グラフでの各頂点を辺の向きに沿うように並び替えるアルゴリズム。
つまり複数処理の前後関係を考慮してある処理が前提とする処理がちゃんとその前に実行されるようにする、みたいな話だと理解した
p.47 オブジェクトとテーブルの対応関係
- シングルテーブル継承: 階層にある全てのクラスを格納するために1つのテーブルを使う
- 具象テーブル継承: 階層にある各具象クラスを格納するために1つのテーブルを使う
- クラステーブル継承: 階層にあるクラスごとに1つのテーブルを使用する
個人的にはクラステーブル継承のほうがNULL値を含むカラムが減るから好きだけど、Fawlerはシングルテーブル継承派らしい
まぁアクティブレコードじゃなくてデータマッパーとかでテーブルとドメインを切り離している前提ならシングルテーブル継承のほうがパフォーマンス的にいいのかも
p.53 レジストリ
他のオブジェクトが共通的なオブジェクトやサービスを見つけるのに利用できる、よく知られたオブジェクト
グローバルなオブジェクトだと思っていいとのこと
p.55 列番号インデックスと列名インデックス
聞いたことない単語だったけどクエリの結果が配列で取れるか、ハッシュで取れるかみたいな話だった
p.58 MVCのCは入力コントローラ
コントローラ、という単語が異なるコンテキストで使用されるためそれと区別するために入力コントローラという単語を使っている
入力コントローラ: リクエストから情報を抜き取り、ビジネスロジックを適切なモデルに送り、レスポンスを表示するために必要なビューを決定する
アプリケーションコントローラ: 画面フローのコントロールを処理する
p.61 トランスフォームビュー
現在ののWebフレームワークで標準なのはテンプレートビューだが、静的な構造を書くのには適しているが複雑なロジックは書きづらい
そういうときトランスフォームビューと併用することでドメインを部分ビューに変換する。
なんか本にはどちらかを使う、って書いてあったから相反するものなのかと思ってたけど併用もできる?
ReactのJSXがテンプレートビューで、プログラム部分がトランスフォームビューという説明はわかりやすかったけど…
TemplateView & TransformView #PoEAA - Qiita https://qiita.com/tanakahisateru/items/40ab71907d93c07b2b65
p.62 ツーステップビュー
UIは同じだけど裏のデータが違う、みたいなときにそれぞれにビューを用意するのではなく、一旦論理的なビュー(JSONとか?)を作ってから表示することによって、ビューを1つにまとめることができる、みたいな話だと理解
p.63 ページコントローラとフロントコントローラ
入力コントローラのパターン
ページコントローラは個々のWebページの処理を担う。今だとPostやGetなどリソース単位になっていることが多い。
フロントコントローラは一旦すべての入力を受け付けて、URLの解釈やページコントローラへの割当て、エラーハンドリングなんかを行う
PageController & FrontController #PoEAA - Qiita https://qiita.com/tanakahisateru/items/c245f73a1f07f34d90df
p.71 並行性の問題を考えるときは更新だけでなく読み取りのことも考える
書き込みの喪失だけでなく読み取りが古い、誤っていることによっても問題が起きる
読み込まれるデータすべてのバージョンマークを見て管理しようとするとコンフリクトや重要でないデータの待機などの問題が発生しやすくなるため、用途に合わせてどこが一貫していなければならないか分析する必要がある
p.72 デッドロックの対策法
- デッドロックを検出できるようにし、犠牲となる人を選んでその人の変更を破棄させ先に進めさせる
- すべてのロックに時間制限を設け、必ずロックが解除されるようにする手法もある
- 作業を開始した時点ですべてのロックを取得することでそれ以上のロックが行われないようにする
- ロックを取得する順番を指定する
- ある人がロックしたものを取得しようとしたときにその人が自動で犠牲候補になるように設定する
p.74 ACIDの定義
- Atomicity(原子性): トランザクションの中で実行される一連のアクションの各ステップは完了されるか、無になるかどちらか
- Consistency(一貫性): トランザクションの開始から終了までシステムのリソースは必ず一貫した正常状態である必要がある(外部キー制約とかが満たされている状態のこと)
- Isolation(分離性): 個々のトランザクションの結果はそれが問題なくコミットされるまでその他のオープンなトランザクションから見えてはいけない
- Durability(耐久性): コミットされたトランザクションの結果はどのような種類のクラッシュが起きても永続化されていなければらない
p.75 遅延トランザクション
ロングトランザクションを避け、1リクエスト1トランザクションにする手法の一つとして遅延トランザクションがある。
すべての読み込みをトランザクション以外で行い、更新するときだけトランザクションをオープンにする
一貫性のない読み込みの問題が発生する可能性があるので、特殊な場合でないかぎりメリットがあまりない
p.75 ドメインのレイヤスーパータイプ
あるレイヤ内のすべてのオブジェクトに重複したメソッドを定義する型。
RailsでいうとApplicationRecordみたいな感じ
p.78 ビジネストランザクションとシステムトランザクション
システムトランザクション: RDBMSとトランザクションモニタによってサポートされているトランザクション
ビジネストランザクション: ユーザのユースケースの最初から最後までのやつ。口座振込でいうと、ログイン、口座の選択、金額の設定、振込の決定までの一連のアクション
p.81 ロックの種類
このあたり訳と普遍的に使われてる単語が違うようだったのと、微妙にレイヤの違う概念が混ざってるので整理
軽オフラインロック(楽観ロック, 楽観的オフラインロック): 変更がかち合わなければOKの精神で変更中のデータの読み取りを許す
https://bliki-ja.github.io/pofeaa/OptimisticOfflineLock
重オフラインロック(悲観ロック, 悲観的オフラインロック): 変更がかち合うことが予想されるので読み取りも許さず後続トランザクション自体をずらす
https://bliki-ja.github.io/pofeaa/PessimisticOfflineLock
緩ロック(粗粒度ロック): 関連するレコードを一つずつロックするのではなく、データ群をまとめて一単位でロックする
https://qiita.com/tanakahisateru/items/b0f2c42e2dc611b726dd
暗黙ロック: ロックを直接管理しなくてすむよう、メカニズムを担うレイヤーに排他処理を設ける
p.89 サーバアフィニティ
特定のセッションのためのすべてのリクエストを1つのサーバで処理する
スティッキーセッションとか
IPアドレスで振り分ける事が多いが、プロキシとかのせいでリクエストが集中してしまう可能性があるのであんまりよくない
p.91 リモートでメメントを格納できない場合…
オブジェクトの状態をネットワークを介して効率的に保存することができないとき、の意味
そういえばGoFにMementoってあった
p.95 分散オブジェクト
マイクロサービスでは外部呼び出しを明示的にやるけど、分散オブジェクトではそれも隠蔽されていて、あたかもローカルで実行しているような見た目になるという理解
p.98 リモートファサード
分散境界でリモートコールの詳細な呼び出しを隠蔽し、粗い粒度のインターフェースを提供する
データアクセスのパフォーマンス改善に使うとのこと。
RemoteFacade & DataTransferObject #PoEAA - Qiita https://qiita.com/tanakahisateru/items/285d0ef12e068a03c71b
p.99 非同期メッセージベース
非同期の環境でWebサービスを使う、っていうのがあんまり想像できなかったが、メッセージキューとかを使うやつのことか
p.125 通常のオブジェクトにすべての特定の状況での振る舞いを入れ、肥大化が問題となった場合にそれを修復する
あるモデルの特殊な振る舞いが現れたときに、すぐに分割するのではなく一旦保留しておいて後からキレイにすればいいのではということか
ドメインモデルとDB層がきちんと分かれていればそれでいいのかも。
p.125 再入可能性(リエントラント)
関数やサブルーチンが複数スレッド、プロセスから同時に安全に呼び出すことができることを再入可能という
エンティティBeanに再入可能性がないみたいな話で出てくるんだけど、エンティティBeanが何をしてるのかが分かってないので理解できなかった
エンティティBeanはリモートアクセスをサポートしていて、あるエンティティBeanがリモートで別のエンティティBeanを呼び出したとき、呼び出されたエンティティBeanから再度元のエンティティBeanを呼び出すことはできない、っていうのを再入可能性がないっていってるってことかと思ったらこれも違う…
しかも調べたらリエントラントであるかどうかを指定できるようになってるみたい?
非リエントラントの場合、同時にクライアントがエンティティBeanに接続しようとしたときに例外が投げられるらしい
エンティティBeanはデータの整合性を保ちつつ、複数同時リクエストを捌くことはできないみたいな話か?ちょっとこのあたり全然わからんかも
p.136 テーブルモジュールとテーブルデータゲートウェイの違い
テーブルモジュールはテーブルに関連するビジネスロジックをデータセットを単位としてまとめるやつ
テーブルデータゲートウェイはテーブル単位の操作をアプリケーション層に提供するだけのやつ
テーブルモジュール内でクエリ生成することもあるし、テーブルデータゲートウェイからデータセットを提供してもらうこともあるみたいな理解
p.139 テーブルモジュールのInsertがインスタンスメソッドになってる
テーブルモジュールの初期化時にDBとの接続を確保するみたいなやつだと意味があるのか
=> テーブルモジュールはインスタンスの場合もあれば静的メソッドの塊の場合もあるって書いてあった
p.140 テーブルモジュールでのカプセル化
テーブルモジュールであるレコードから特定のデータをとるとき、メソッドをつくってカプセル化するかレコードから直接取るかはその時次第
一般にはカプセル化が推奨されているとのこと
p.143 ドメインロジックとアプリケーションロジック
ビジネスロジックを純粋に問題ドメイン(代金の算出など)を扱うドメインロジックと、管理者への通知やアプリケーションの統合を行うアプリケーションロジックに分ける
ツイートにいいねがついたとき通知を飛ばす、はアプリケーションロジックになるのか?
記事が評価されたときにユーザに通知を飛ばす、っていうロジックをモデルに置くかコントローラに置くかで迷ったことがあったので気になった
多分アプリケーションロジックなんだろうけど、ActiveRecordにおいたほうが便利だよね…?
p.143 サービスレイヤの実装方法
ドメインファサード: ビジネスロジックは実装せず、ドメインモデルの呼び出しのみ行う
操作スクリプト: アプリケーションロジックは直接実装、ドメインロジックは委譲。レイヤスーパータイプを使ってサービス間の共通の振る舞いを抽出する
p.146 セッションファサード
リモートファサードのEJBでの実装例
荒い粒度のインターフェースを提供して複数のEJB呼び出しをカプセル化する
p.146 サービスレイヤをいつ使うべきでないか
一種類のインターフェースしか持たず、ユースケースのレスポンスにマルチトランザクションリソースが関連していない場合は不要
Railsだと複数DBとかDB+外部APIとかをやってない場合はページコントローラで直接やったほうが良いということか
p.149 Contract.readForUpdate(contractNumber)
なんかサービスレイヤの説明でドメインを使いますって言って、Contract.readForUpdateとか出てくるのでややこしいな…
ドメインって外部レイヤに依存していいの…?よくないのでは。
Contractの取得先がDBかそれ以外かを問わないっていう感じになってるからいいのか…?
Contractがインタフェースに依存していて、データ取得側がインターフェースを実装するよう強要されていれば依存関係が逆転するからいいのか?
p.151 サービスレイヤ内で通知の実装に関する詳細を書くのではなくゲートウェイに委譲している
通知などに関してはオブザーバパターンを使うよりも操作スクリプトで書いてしまったほうが読みやすくシンプル
p.154 テーブルデータゲートウェイではfindByIdのような単一レコードを意図したものでもdatasetを返す
datasetはIDataReaderのようなレコードの集合を扱うクラスであったり、オブジェクトの配列であったりする
p.155 テーブルデータゲートウェイを介してデータマッパーをデータベースと通信させる
テーブルデータゲートウェイでメタデータを使用しつつ、ドメインオブジェクトへのアサインは手動で行うときに有効
なんかリポジトリっぽくなってくる気がするけど、コンテキストと狙いが違うっぽい。
データマッパー: ドメインとデータベースを独立させ、仲立ちをする
リポジトリ: ドメインのコレクションに対する抽象化されたインターフェースを提供して、永続化の手法を隠蔽するより抽象的なパターン
リポジトリはデータマッパーの役割を含む、という理解
c# - What is the difference between the Data Mapper, Table Data Gateway (Gateway), Data Access Object (DAO) and Repository patterns? - Stack Overflow https://stackoverflow.com/questions/804751/what-is-the-difference-between-the-data-mapper-table-data-gateway-gateway-da
マッパーとゲートウェイの違い
Patterns of Enterprise Application Architecture読書メモ(データソース編) - 小野マトペの納豆ペペロンチーノ日記 https://matope.hatenablog.com/entry/2014/05/13/193709
TableDataGatewayはドメインモデルと併用しない、っていうのがあったからよく分からなかったけど単なるGatewayとTableDataGatewayは別物か
依存の向きが逆になってる、みたいな話はこういうことか?
# Gateway
class Customer
def initialize(customer_id)
@customer_id
end
def pricing
PricingGateway.get_customer_pricing(@customer_id)
end
end
customer = Customer.new(customer_id)
customer.pricing
# Mapper
class Customer
attr_accessor :pricing
def initialize(customer_id)
@customer_id
end
def set_pricing(pricing)
self.pricing = pricing
end
end
class PricingMapper
def self.set_customer_pricing(customer)
pricing = fetch_customer_pricing
customer.pricing = pricing
end
end
customer = Customer.new(customer_id)
pricing = PricingMapper.set_customer_pricing(customer)
customer.pricing
p.159 DataSetHolder, DataAdapter
ADO.NETの解説で出てきた
PersonGatewayのレイヤスーパータイプDataGatewayとしてDataGatewayがあり、DataGatewayはデータを保持するためにDatasetHolderを持っている
DatasetHolderはDataAdapterとDatasetを持っている
DataAdapterが何をするのかイマイチピンとこなかったので調べた。
データセットクラスのインスタンス化、DB接続のOpenから取得、Closeまでを処理するとのこと。
DataSetHolderのUpdateはレコードセットが処理単位なので変更してないレコードも全件Updateみたいなことをやってて効率悪そうだなぁと
そういうときはUnitOfWorkとか使うんだろうな
p.163 行データゲートウェイでは各テーブルごとに1つのFindクラスとGatewayクラスになる
Gatewayクラスに直接staticメソッドとして取得処理を置いてしまうと、特定のデータソースに依存してしまうため独立したFindクラスを用意する
p.163 行データゲートウェイの更新
1つのレコードに対して2つの行データーゲートウェイインスタンスが実行されると、2つ目のデータゲートウェイの変更が1つめの変更を取り消すことになってしまうため、一意マッピングなどをつかってインスタンス化をコントロールする必要がある。
サンプルコードではRegistryにFind結果をメモしている感じだった
p.164 DB構造からドメインオブジェクトを隠すために、行データゲートウェイを使うこともある
基本はアクティブレコードにするか、データマッパー+ドメインオブジェクト
行データゲートウェイをメタデータからの自動生成にしてデータマッパーと組み合わせてつかうのもあり
p.166 行データゲートウェイではレコードのフィールドへのアクセスはカプセル化される
テーブルデータゲートウェイではカプセル化してもいいし、直接とってもいいよみたいな感じだった
p.167 JavaのLongとlongの違い
PersonFinderにfind(Long id)
とfind(long id)
を定義しててなぜと思ったので
longがプリミティブ型でLongはそれのラッパークラスらしい。Java5から自動で変換されるようになったので今は不要なコードかも
p.171 レポート目的ならビューを元にしたアクティブレコードを使うのもあり
更新とかを行わずSELECTしてデータをオブジェクトのフィールドに入力するだけなら複雑になりすぎないのであり
p.171 外部キーマッピングはそのままにしておく
インスタンス化時に関連レコードも読み込んでおくのではなく、一旦そのままにしておいて必要なときにJOINしたクエリでロードするとかレイジーロードする
p.177 挿入や更新を行う場合、データベースマッピングレイヤはどのオブジェクトが変化したかを知っておく必要がある
Unit of Workがそれを管理する優れたパターンである
p.177 マッパーはクライアントがオブジェクトをどのように使用するかを知り、最小限のデータベースクエリを設定する
使用側によってfindを変える必要があるので、クラス図には現れない依存が発生すると思った
あと、ドメインの元データとなるテーブルのフィールドがドメイン間で重複しているときとかどうすんのかなと思ったが、まぁ切り出して一意マッピングで同じオブジェクトを返すようにするとか、そもそもドメインを読み込むときに使ったレコードのインスタンスをUnit Of Workに登録しておくとかがあるのかな
p.179 メモリ上のオブジェクトはマッピングレイヤを完全に無視することはできない
各オブジェクトは関連するものであり、データベース内のすべてのデータをレコードに載せることができない以上レイジーロードで抽出をコントロールしなければならないので、メモリ上のオブジェクトを触るときはマッピングレイヤの挙動も意識しないといけない、という理解
p.180 データマッパーでfindメソッドを呼び出すためにドメインオブジェクトを必要とする場合がある
ドメインオブジェクトの複数のフィールドを使って検索する必要があるときとかに、データマッパーがドメインオブジェクトを引数に取ることがあるけど、レイジーロードを使って引数として取るはずだったドメインオブジェクトからの関連で取得することができる、みたいな話だった
また、どうしてもドメインからfindしたいときは、ドメインパッケージ側にインターフェースを作ってマッパー側に実装を強制することで依存の向きをコントロールできる
(セパレートインターフェース)
p.180 マッパーはドメインオブジェクトのフィールドにアクセスする必要がある
保存の時とかにはマッパーがドメインから値を取り出して保存しないといけないので、publicメソッドかリフレクションみたいなのが必要になってくる
p.180 ステータスフィールド
マッパーにしか公開していないつもりのpublicメソッドがDB読み込みというコンテキストの外から呼ばれた場合に例外を投げるため、ステータスフィールドを使って保護する
isLoadingFromDBみたいなメンバを用意して、DB読み込み中のときだけtrueにしてpublicメソッド内で判定に使えという話だと理解
p.181 ドメインオブジェクトの初期化方法
- rich constructor: コンストラクタの引数に必須のデータを取って配置した状態から始めるやつ
- pros. 最初から完成度の高いオブジェクトができる。不変フィールドが存在するとき不変にできる
- cons. 循環参照に気をつけないといけない
- case書いてもいいが、難しいので引数なしの空のオブジェクトを作成し、一意マッピングへ即座に挿入することで循環を防ぐ
- 空のオブジェクトを作成したあとに必須データを配置
p.182 データベースマッピングレイヤは自分で作るな、買え
p.184 Mapper内での一意マッピングからの読み込みは2回行われる
- SQL発行前にIDで
- SQL発行後データをドメインオブジェクトにアサインする前に任意のデータで
ID以外の項目(名前とか)を使って複数のレコードを取得するとき、すでに読み込み済みのオブジェクトが存在しているかもしれないが、SQL取得前に一意マッピングからすべて読み込まれているかを確認することはできないため、SQL発行後のデータを使って一意マッピングを再確認する
p.186 レイヤスーパータイプにfindMany
StatementSourceみたいなSQLをラップしたinterfaceを引数に取るようにしておき、実クラスのfind内で特定のクエリを渡すことで共通化できる
p.198 UnitOfWorkの一貫性のない読み込みチェック
処理中、すでに読み込んだオブジェクトについても通知するためビジネストランザクション中にデータベース上の一切のオブジェクトに変更がないことを検証することで一貫性のない読み込みをチェックできる
、registerCleanみたいなのを使って読み込みしかしてないオブジェクトもUoWに登録することで、書き込み前に読み込まれたオブジェクトが変化していないか確認できる、っていう理解
p.200 アスペクト指向プログラミングってなに
複数の場所から利用される共通処理をまとめてビジネスロジックから分離する
ログ出力を複数メソッドにもたせたいとかそういうやつか
メソッドに間接点(Joint Point)を設けて、振る舞い助言(Advice)を追加する
任意の間接点、振る舞い助言の定義をまとめたモジュールをアスペクトと呼ぶ
アスペクト指向プログラミング - Wikipedia https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
UoWの文脈だと、インスタンス生成時に必ずUoWに登録する、みたいなやつのことを指している?
p.201 TOPLinkのユニットオブワークコントローラ
UoW自体がDBからの読み込み処理を行い、確定したオブジェクトが読み込まれたときに登録まで行う
p207 抽象ドメインオブジェクトにマーキングメソッドを提供
ドメイン全てにmarkDirtyのようなメソッドを提供し、setter内などでmarkDirty()を忘れず呼ぶ
結局registerCleanはデータマッパーから呼ばないといけないし、markRemoved以外の削除もありうるという話なので、置かないで全部UoWから呼び出しにしたほうがいいのでは?
p.208 暗黙的なUoW管理
ControllerのアクションみたいなやつをラップしてリクエストごとにUoWの生成とコミットが行われるようにする
UoWはstaticフィールドにThreadLocalを持っていて、staticにアクセスできるようになってるのでアクション内で実行されるコードはUoWが存在する前提で呼び出すことができる
p.210 明示的な一意マッピング
find("Person", 1)よりもfindPerson(1)のほうがいい
型チェックができるし、どこで呼ばれているかがわかりやすい
p.212 依存マッピング
データベースの他のテーブルから参照されない、子クラスの読み込みを親クラスのDataMapperに任せることでマッピングの手順を簡素化できる
UML上ではコンポジションで表されるような関係
p.214 レイジーロードの実現方法
-
レイジーイニシャライズ: カプセル化されたgetメソッドが呼ばれたときにインスタンス変数を使ってメモした値を返したり、DBから取ってきたりする
-
仮想プロキシー: 使用するクラスのシンプルなラッパーを持つクラスを提供する。SupplierがList productsを持つとき、ProductのMapperを持つVirtualListで初期化してVirtualListがListの動きをプロキシする
-
バリューホルダー: 汎用のValueHolderがあり、loaderに個別のLoaderをセットして初期化する。仮想プロキシーとの違いはListを直接返すか、Listのプロキシを返すかってところか?
-
ゴースト: 全てのドメインオブジェクト自体にローディング状態を持たせて、MapperからはGhost状態で初期化して返す。その後ドメインオブジェクトのプロパティへのアクセスがあった時点でMapperがドメインオブジェクトのプロパティを再読み込みしてセットする。ドメインからMapperへのアクセスはRegistryを経由して行っていた。またドメインオブジェクトのリストに関しては、コレクションクラスを用意して一括でLoadを行うことでN+1(リプルローディング)を防ぐ
結局親クラスからの子クラス取得をカプセル化して、読み込んだかどうかをメモしつつ返す、っていう基本動作は変わらなくて、間に何を挟むか、と何を返すかの違い?
DataMapperを使っていなければドメイン層とDBアクセスが癒着しててもまぁいいかって感じだけど、Mapperを使ってる場合はInterfaceを用意するとかで依存の向きをコントロールする必要があるのでややこしくなる
p.215 バーチャルコレクション?
あんまり他の文献が出てこなかったので推測だが、コレクションクラスの中身が仮想プロキシーとかになってるやつのことかな?
p.216 バリューリストハンドラ?
大量のデータを読み込むときに、ページングなどの機能を提供しつつデータ保持、データ読み込みの効率化を図るクラス
p.232 一意フィールドは意味のないキーを使ったほうがいい
タイプミスや、分析漏れによって一意で不変であったはずのキーがそうでなくなることがあるので
p.233 データベース一意キー
データベースのすべてのテーブル全体の行において一意であるキー
p.233 テーブル一意キーと継承
具象テーブル継承、クラステーブル継承を使うとき、各テーブルではなく階層構造に一意キーを使う、とは…?
継承階層すべてのエンティティが階層全体で一意であることを保証するキーを持つ。
VehicleとCar, BikeがあったとしてCar.Id, Bike.Idをキーにするのではなく、Vehicle.Idを使うみたいな話か
次の継承グラフ一意の場合、あえてテーブル一意を使用することにしている
がよくわかんないんだよな…?前の文章と矛盾してない…?Vehicle.Idが一意のときも、Bike.Id, Car.Id使うっていう話だよね?
どっち使ってもいいみたいな話か…?
p.233 キーオブジェクトの汎用リスト上で1つのキークラスを持つとキーの各部分に対し明示的なフィールドの各ドメインクラスのキークラスを持つこととなる?
キーオブジェクトの構造を一般化するためにキークラスを作るが、キークラスのフィールドは自体は個別に持っており、ドメインに合わせたキークラスを継承で作るみたいな話か?
p.236 シリアライズLOB
オブジェクトのグラフを直列化し、一つの大規模オブジェクトとしてデータベースフィールドに格納する
p.242 コンビニエンスメソッド
あんまり一般的な語彙ではないっぽい
他のメソッドでも(複雑にはなるけど)書ける処理を、より簡単に記述するためのメソッド
意味的にはショートカットとかが近そう
コンビニエンスメソッド - Journal InTime(2005-11-17) https://shugo.net/jit/20051117.html
め
p.256 バックポインタ
双方向リンクの一部で、オブジェクトの関連の文脈だとAlbumが複数のTrackを持つとき、TrackからのAlbumへの参照のこと
p.258 単一値参照
Railsでいうとbelongs_toなんだけど、名前はhas_oneっぽいんだよな
has_oneよりbelongs_toのほうがオブジェクトの関連とDBの構造が一致するというのはわかる
でも更新のときは外部キーじゃなくて関連先レコードの手キーを使ってるな。
あぁsetArtistみたいにしたときにartistIDに値が入るわけじゃなくて、関連が追加されるだけだからか
p.267 リンクテーブルを参照するテーブルは他にない
改めて考えると関連テーブルはオブジェクトの関係をDBに保存するためだけのテーブルなのだから、そこに対してリンクされたオブジェクト以外からアクセスがあったりロジックが入るのはおかしいか
と思ったけど人/会社テーブルのような関係についての滋養法を含む関連テーブルはドメインオブジェクトに対応するって書いてあるな
DB上の見た目が同じでも意味合いが違う場合があるということか
p.278 メソッドオブジェクト
複雑なメソッドそれ自体をカプセル化するために、独立したオブジェクトに切り出したやつ
引数を取る代わりに、フィールドにセットして内部で使用できる
p.278 複数オブジェクトの関連テーブルマッピングの一括読み込み
関連読み出しを行うメソッドオブジェクトAssociationTableLoaderを定義して、汎用的な構造にしている
データの読み込み => 一意マッピングへの挿入 => オブジェクトの関連セット
Employee has many Skillsのとき
一行ずつ読み込んで、LoaderのinProgressにマッピングしたEmployeeにSkillを突っ込んでいく(Employee返却時の順番を保証)、全部読み終わったらMapper側の一意マッピングに突っ込んで
最終的にその一意マッピングから再度完成したEmployeeを読み込んで配列にして返す
p.272 Dependentの重要な特性は一意フィールドを持たないため一意マッピングに格納されない
複数の親で共有されていると読み込みや書き込みで競合するから、親クラスは子クラスを完全に専有していなければならなさそう
p.284 UoWと依存マッピングを併用しない
Dependentの追跡ができないため、削除・再挿入が正常に動かなくなる
p.289 組み込みバリューとバリューオブジェクト
バリューオブジェクトがDBに永続化されるとき、フィールドにマッピングされる。このマッピングのことを組み込みバリューと呼んでいる
p.292 BLOB(バイナリ形式),CLOB(テキスト形式)
XMLなどを使ったテキスト形式(シリアライズLOB)のほうがバージョニング問題に強く可読性も良いが、無駄な容量を食う
p.294 LOB参照オブジェクトに外部オブジェクトが埋め込まれている場合、シリアライズLOBはうまく機能しない
ここでの外部オブジェクトは他のDBのオブジェクトやファイルシステム上のファイルで、単純にシリアライズできないので、これをサポートする参照スキームを考えておく必要がある
p.297 DTD
Document Type Definition(文書型定義)
HTML, XMLのスキーマを記述するための言語
p.304 シングルテーブル継承でのオブジェクトの更新
Updateメソッド内で、レコードに値を挿入するSaveを呼び出す
Saveでは具象クラスからスーパークラスのメソッドを呼び出すことで固有の処理と共通処理を分けている
Insertの場合もSaveを呼び出した後に実際にDBにレコードを挿入しにいく
p.315 具象テーブル継承(リーフテーブル継承)のキー
階層構造のすべてのテーブルに対して一意になるようなキーを持たなければならない
スーパークラスのコレクションを管理するようなときのため
テーブル名と各テーブルのプライマリキーを使った複合キーにするのもあり
p.316 具象テーブル継承のデメリット
スーパークラスが他のオブジェクトと関連するとき、外部キーを含むリンクテーブルが必要になってしまう
SELECTによってスーパークラスを検索するとき、すべてのテーブルを検索する必要が出てきてしまう
p.324 継承マッパー
RDBのテーブル構造をオブジェクト指向の継承階層構造に変換する
Mapperを継承構造に合わせて階層化することで各クラスに合わせた変更可能ポイントを確保できる
insert, update時はスーパークラスのメソッドを呼び出し、その後各Mapperで固有の処理を行う
p.326 汎用スキーム
汎用スキームは継承データベースマッピングに有効
っていう文脈で出てきたけど何を指してるのかがいまいち…
シングルテーブル継承、具象テーブル継承、クラステーブル継承のことを指してる?
p.328 リフレクティブプログラム
多分RailsでいうとActiveRecordのMethod missingで取ってきたりセットしたりするやつ
p.338 クエリーオブジェクトはインタプリタプログラム
SQLに変換できるようなオブジェクト
ドメインオブジェクトの言語を使って、テーブル構造の言語を作成するということ
p.340 クエリーオブジェクトの使用タイミング
手動で作成したデータソースレイヤがある場合は使わない
ドメインモデルやデータマッパーを使っているときのみ必要になり、メタデータマッピングも必要になる
Elasticsearchとかでクエリを作るときにもCriteriaとQueryObjectの考え方って使えそう
Criteria自身がドメインオブジェクトを受け取って自身を満たすか確認するisSatisfiedBy
を持つ、というのが何か便利に使えそう(テストやインメモリからの検索)
p.345 リポジトリ
ドメインレイヤとデータマッパーの仲介役となり、メモリ上のドメインオブジェクトコレクションの役割を果たす
クライアントはクエリーから返したいオブジェクトの特性を指定するCriteriaオブジェクトを作成して渡す
クエリーオブジェクトを直接使用する場合は、クライアントがクエリの発行まで認識しているが、リポジトリを使用した場合はCriteriaが満たされていることのみを認知する
サンプルコードではPersonから扶養家族(dependents)を取るとき、Person#dependentsでRepositoryを呼び出して使ってるけどこれ依存が双方向になっててちょっとやな感じだけどな
p.352 MVCの2種類の分離
- モデルからプレゼンテーションの分離
- ビューからコントローラの分離
- ViewとControllerが分離している状態っていうのがあんまり想像できない
- 本来であればeditとshowってアクション分ける必要なくて、インスタンスをセットするアクション1個とedit.html.erb, show.html.erbでいいはずだよね。みたいな話か?
- 1Controller1Viewだと分離はしていない
- GUIフレームワークにおけるアプリケーションコントローラとの混同
- ViewとControllerが分離している状態っていうのがあんまり想像できない
p.355 スクリプトレットコード
テンプレートビュー(JSPとか)内の埋め込みコードのこと
p.363 ASP.NETのコードビハインド
UIの表示部分とプログラム制御部分のファイルを分けるやつ
p.368 インターセプティングフィルタ
Railsでいうとbefore_actionみたいなやつ
p.375 テンプレートビューの条件付きタグ
テンプレートビューにロジックが入り込むきっかけになるので、独自タグをつくるとかして宣言的に書けるようにしたほうがいい
p.385 XSLT
XMLをCSVとかHTMLに変換するためのルールを記述できる言語
ビューのパターンは大体ドメインに直接依存するテンプレートビューから始まって、ドメインとビューを切り離したくなったらViewModelとかを間に挟んでTwoStepViewみたいにすればいい、サイト全体の外観を変えたいときはLayoutを使ったり、カスタムタグみたいなのを使いたいときはPartialを使えばいいってなってるからあんまりこだわらなくていいきがするなぁ。
複数の事業者(バックエンドが違う)に統一的なフロントを提供する、とかってなったらDTOとしてXML,JSONを使ってトランスフォームビューを使うみたいな選択肢が出てくるのかな
まぁそのときでもReactとかVueでいいよなぁ
p.403 アプリケーションコントローラ
入力コントローラからリクエストを受けて適切なDomain CommandとViewを選択するための集中管理ポイント
注文とかでいうと、注文ステップごとにアクションを作るんじゃなくて単一の/order
にアクセスする中でステップに合わせて画面が切り替わっていくような感じ?
RailsのApplicationControllerはPoEAAのApplicationControllerというよりは、InputControllerとかFrontControllerの概念に近いのかな
p.404 アプリケーションコントローラがUI機構へのリンクを持つ?
ダイレクトアプリケーションコントローラっていう単語がよくわからなかった。
意味合い的にはHTTPとかリッチクライアントに直接アクセスするコントローラと、抽象化されたUI機構へのリンクだけを持っていてHTTPとかとは直接やりとりしない、ということかと
p.410 アプリケーションコントローラはコードによる利用の目的で読み込むことができる
アプリケーションコントローラが完全なURLを返さない、最終的にはフロントコントローラがレスポンスの統合などを行うことによって1リクエスト内で複数のアプリケーションコントローラを呼び出すこともできる
p.412 リモートファサード
ドメインロジックを一切含まない。トランザクションスクリプトを含んだときはセッションファサードと呼ぶべき
ドメインオブジェクトをコピーして返すのではなく、DTOを使う
リモートファサード自体の数は減らして1つの中に多数のメソッドを入れる構造にする
内部的に同じ機能でもクライアントが異なるコマンドとみなす場合、異なるメソッドとして定義する
p.415 サービスレイヤとリモートファサード
サービスレイヤはリモート以外でも使うことができる
ドメインモデルがリモート、プロセス内の両方で使われる場合ドメインレイヤの上にサービスレイヤを置き、さらにその上にリモートファサードを置く
p.429 アセンブラオブジェクト
データ変換オブジェクトは複数サービス間にまたがるため、ドメインオブジェクトに依存させたくない
データ変換オブジェクトは外部インターフェースのためドメインオブジェクトを依存させたくない
そのため、間にMapperの一部であるアセンブラオブジェクトを挟む
p.448 軽オフラインロックのカラム
Version以外にもmodifiedBy, modifiedAtをカラムに追加することで、コンフリクトが起きた場合の情報通知を行う
p.450 オフライン並行性
何をオフラインと呼んでいるのかが分からなかったが、リアルタイムで変更がDBに反映されないような状況(モバイルアプリでフォーム入力してからサブミット)を指している
p.451 重オフラインロックにおけるロックの種類
書き込み専用ロック: ほかのプロセスやスレッドは同時に読み込むことができるが、あるプロセスしか書き込みできない
なんか本文を読むと↑の解釈なんだけど、一般的な意味としては読み込みも書き込みもブロックする、という感じ
読み込み専用ロック: 他のプロセスやスレッドが同時にデータを読み込むことはできるが、どのプロセスも書き込みはできない
読み書きロック: 同時読み込みを許可しつつ、書き込み操作は排他的になる。書き込み操作をおこなうプロセス、スレッドが存在する場合、新たな読み込み操作や他の読み込み操作は待機状態になる
同時の読み込みロックは許可されている
p.455 本書で紹介しているオフラインロック技法のすべては、システムが独自のトランザクションモニタを持っていることを前提としている
ビジネストランザクションが1つのシステムトランザクションに収まっている場合はDBとかFWが用意しているトランザクションロジックを使え
そうでないときに初めてロックをテーブルで管理したりすることを検討する
p.463 緩ロック
緩ロックをつかう際はアクセスポイントを提供するルートと、含まれているものを定義する境界を持つ集合体を使う。
- 共有軽オフラインロック: Versionオブジェクトを共有し、Versionオブジェクト内のvalueを使って軽ロックを実装する
- 共有重オフラインロック: 重ロックを取得後にVersionをincrementしてCommitすることで、その後に読み込まれるデータが最新であることを保証する
- ルート軽オフラインロック: root(Owner)がVersionを持っていて、OwnerのVersionをincrementすることでロックを獲得する
p.475 「プラミング」コード
だいたい接続コードとかアダプタみたいな意味合い
p.475 暗黙ロックで重ロックを管理するとき
ユーザーの作業開始時にあるデータを編集するために必要なほかのロックを取得する時点を発見できない
そのため、結局書き込み時点でしか衝突を検知できず重オフラインロックの、ユーザーに同じ作業を2度させてはならないという目的とコンフリクトする
また、暗黙ロックを使うことでビジネス上不要なロックが形成されてシステムの並行性が制限される場合がある
p.481 スタートポロジーにhttpクライアントが含まれている場合
クライアントのなかにhttp通信するやつがいる、みたいな感じ
スター型トポロジーは1つの集線装置(ハブやスイッチ)に複数のノードを接続しているトポロジーのこと
https://www.infraexpert.com/study/networking1.html
p.486 データベースセッションステートの実装パターン
セッション中に保留されるデータがある場合、Pendingフィールドをテーブルに追加するか、Pendingテーブルを使う
PendingテーブルはシリアライズLOBを使ってテーブル関係を圧縮してよいのでは、とのこと
p.490 サービススタブ
外部リソースへのアクセスをメモリ上で実行するテスト用のオブジェクトに置き換える
ゲートウェイを使ってサービスへのアクセスを定義し、ゲートウェイはサービスを呼び出す実際の実装とサービススタブを持ち、プラグインを使ってそれらを環境に応じてロードする
p.490 ゲートウェイを2段構えにする
外部リソースに対する最小限のオーバーレイとなるバックエンドとシンプルな操作を提供するフロントエンドに分割してゲートウェイを構成する手法もあり
p.491 ゲートウェイとほかパターンとの違い
- ファサード
- ファサードはAPIをシンプルにすることそのものを目的にするが、ゲートウェイは特定の目的を持つクライアントを起点にする
- ファサードは常に隠蔽されているインターフェースと考えられているが、ゲートウェイはテストのためにファサード全体を置換する
- アダプター
- アダプターは実装するインターフェースを変更し、別のインターフェースに適合させる
- ゲートウェイを使う場合は既存のインターフェースが存在しないため、アダプターはゲートウェイの実装の一分になる
- メディエータ
- メディエータは複数オブジェクトを分離する。複数オブジェクトは互いを認識しないが、メディエータのことは認識できる
- ゲートウェイでは2つのオブジェクトのみ(クライアントと外部サービス?)が存在し、ラップされたリソースはゲートウェイを認識していない
ファサードだけちょっとピンとこないかも?ファサードはサービススタブによるテストを簡易にする目的では作られていない、ってことかな
p.495 ゲートウェイのスタブ
ゲートウェイ自体をスタブするか、サービスをスタブを作成するか、それとも両方かを選択することができる
p.503 レジストリのスコープと実装
プロセス単位: 静的フィールド
スレッド単位(シングルスレッド): シングルトン、ディクショナリ
セッション単位: ディクショナリ
p.508 参照オブジェクトとバリューオブジェクトの違い
等価性の処理方法が違う
参照オブジェクト: オブジェクトIDで比較
バリューオブジェクト: クラス内のフィールド値で比較
p.511 マネーの分配
小数点のまま格納できない場合などに全体の金額が変わらないよう工夫する必要がある
allocate関数を作って比率を渡してマネークラス自体に処理させるのが良いとのこと
p.518 スペシャルケース
Nullチェックの回避のためにNullであるとはどういう意味か、を持ったサブクラスを提供する
(通常のCustomerに対する、Missing CustomerやUnknown Customerなど)
p.519 Flyweight
メモリの節約のためにインスタンスを共有するパターン
Singletonとの違いが気になったので調べた
Singletonは本当に1個だけだが、Flyweightは性質によって返されるインスタンスが違う可能性がある(インスタンスがマッピングで管理され、取得関数の引数によって違うインスタンスが変える可能性がある)
Flyweight https://refactoring.guru/ja/design-patterns/flyweight#:~:text=Singleton%20%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AF%E4%B8%80,%E3%81%82%E3%82%8B%E5%8F%AF%E8%83%BD%E6%80%A7%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82
p.521 マーカーインターフェースのタイプテスト?
マーカーインターフェース: メソッドを持たずオブジェクトが特定の特性を持っているかどうかのみ示すために使用されるインターフェース
instance of とかで条件分岐するのに使う
p.526 プラグイン
セパレートインターフェースとファクトリによって環境ごとに使用するクラスを変更できるが、ファクトリ自体が増えたとき条件分岐も複製され面倒になる
コンパイル時ではなくシステム構成(実行時)にクラスをリンクする
テキストファイルやリフレクションを使って、実行時環境に応じたクラスを使用するよう指示する
DIでセットするのかなと思ったら普通にクラス変数に入れてた
p.527 ゲートウェイとしてのインターフェース
サービススタブを作るときにゲートウェイクラスを作ってサービスを呼び出すみたいな形を取る以外に、interfaceとプラグインを使ってゲートウェイを実装できる(サービス自体がGatewayとしてのinterfaceを実装する)
p.530 レコードセットの性質
SQLの結果っぽく振る舞えることと、プログラム内から簡単に生成できること
普通ベンダーから提供されている
p.531 2ティア手法
クライアントが直接DBに接続しデータの読み書きを行う設計パターン
ほか記事で大事そうだとおもったところ
。ひとつの手続きから無節操に全ての ActiveRecord クラスを参照すると、それは実質、一本のデータベースコネクションで何でもやるスクリプトと同じ意味になってきます。
Domain Model や Table Module には、begin/commit/rollback といった RDBMS の関心を含まず、Service Layer にトランザクション境界を隠蔽するのが良い策です。
Active Record の凝集性を意識せずに設計すると、そのクライアントコードは実質的に Transaction Script になるという意味です。
Active Record は、広義ではオブジェクトリレーショナルマッパー (ORM) に分類されることもありますが、PoEAA の観点では Active Record を ORM みなしません。独立した Domain Model と、独立したリレーショナルデータベースがあり、それを外部からマッピングによって繋ぐアーキテクチャを、ORM と呼びます。
Query Object は可能な操作全て表現するもの、Repository は実際に要求されることにだけ端的に応えるもの、と、パターンを意識する状況が全く異なるのが、違いの本質です
DTO は、データ正規化や一意性管理に関係のあるエンティティではありません。