Closed15

『現場で役立つシステム設計の原則』内容要約、意見や疑問点メモ

ピン留めされたアイテム
susansusan

『現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法』
(ISBN-10: 477419087X) は極めて良書である。
これを読みながら、自分に役立ちそうな要点をまとめたり、意見や疑問をメモしていく。

私はDjango使いなのでDjango(Django REST Framework)で応用できるか、応用すべきかという観点で読んでいく。
この本のサンプルコードはJava(Spring)で書かれているが、適宜Django(DRF)やPythonに置き換えて考えていく。

ピン留めされたアイテム
susansusan

一冊通して主要内容まとめ

CHAPTER 5 アーキテクチャ

  • 三層アーキテクチャ(プレゼンテーション層、アプリケーション層、データソース層)とする
  • 業務ロジックはドメインオブジェクトに任せ、画面やDB入出力の都合を切り離して設計する
  • 登録系サービスと参照系サービスを分ける(CQRS)
  • アプリケーション層を「シナリオクラス群」と「基本サービスクラス群」の2層構造にする
  • シナリオクラスで基本サービスクラスを組み合わせて画面ごとのユースケースを実現する
  • 「契約による設計」を行う: nullを渡さず/返さず、事前条件は利用側が確認し、異常時は例外で通知して防御的プログラミングを避ける
  • DB操作はリポジトリを介して行い、メソッド名・引数・返り値はすべて業務用語で表現し、インターフェースと実装クラスを明確に分離する

CHAPTER 4 ドメインモデルで設計

  • 業務分析と設計は同一人物が担当し、業務用語をクラス名・メソッド名に反映する
  • 小さなドメインオブジェクトをボトムアップで組み立てつつ全体像はパッケージ図や業務フロー図で把握する。重要度順に実装・テストする
  • ドメインオブジェクトは「ヒト/モノ/コト」の視点で関係性や時間軸、妥当性ルールを洗い出しながら発見し、部品を組み合わせながら不足を補う
  • 基本パターン(値オブジェクト/コレクション/区分オブジェクト/状態遷移)や業務関心パターン(口座/期日/方針/状態)を使い分ける
  • 専門家との対話を通じて、ドメインモデルを洗練させる

CHAPTER 3 業務ロジックの整理方法

  • メソッドを必ずインスタンス変数を使う形でクラスにまとめる。肥大化したら同じ変数を使うメソッド群ごとに分割し、パッケージ階層で整理
  • 受注日や単価判定などの小ドメインオブジェクトを組み合わせて、大きなドメインモデルを構築する。三層の各層は業務判断や計算をすべてドメインオブジェクトに委ねる
  • 増えたクラスはパッケージやサブパッケージで整理する。深い階層や少数クラスのパッケージ構成をいとわない

CHAPTER 6 データベース設計

  • カラムはすべて NOT NULL 制約を付ける。どうしても NULL が必要な場合は別テーブルに分割
  • DBは「コトを記録する」場所として、既存データを UPDATE せず、元データ・取り消しデータ・新データを INSERT だけで残す
  • 後から新しいデータ項目を追加したい場合は既存テーブルに NULL 許容カラムを増やすのではなく、新テーブルを作成して外部キーで紐付ける
  • 「記録」用テーブルとは別に「状態」用テーブルを用意し、DELETE/INSERT の対で毎回新しい状態行を作成
  • ドメインオブジェクト設計とテーブル設計は別々に進めて明示的にマッピングし、オブジェクト側にDB操作やフレームワークの都合を持ち込まない
ピン留めされたアイテム
susansusan

本書の内容に対する私の意見や疑問点まとめ

意見

  • システムの規模にもよるが、提供できる機能が同じならば、ディレクトリ階層やファイル数はより少ない方が好ましく感じる。そのほうが修正対象を探しやすく認知的ストレスが小さい
    • 深いパッケージ階層や膨大なファイル数は、IDEでのナビゲーションや修正対象を探すのを困難にする
    • 例えばDjangoではApp単位でviews.pyやservices.py(やrepositories.py)と、それぞれ1ファイルにまとまる位の分量が好ましく感じる。階層化するとしても2階層くらいまでが好ましい
    • Java使いとPython使いの感覚や文化の違いかもしれない
  • 業務ロジックを「すべて」ドメインモデル側に任せると、SQL側で計算・集計するほうが高速な場合について、パフォーマンスを犠牲にする恐れがある
  • ドメインモデルとデータモデルを対立させ、データモデルを完全に棄却する議論には違和感がある。両者は相互に分析・改善しながら進めるべきではないか
    • データモデルが未確定の段階でビジネスロジックの実装を始めるのは、手戻りが多くなり、現実的に難しい

疑問

  • 最後まで読んでも「業務ロジック」と「それ以外のロジック」の定義や判断基準がはっきりしなかった
  • 特にリポジトリ層の実装について、サンプルコードが少なく、複雑な画面仕様等に基づく複雑なクエリ(集計や多数テーブルの結合一覧表示など)をどう表現すべきか、といった疑問に答えが出ない。リポジトリ層の実装パターンの実例がほしい
    • 経験上、リポジトリ層で処理重複定義を避けようとして既存ロジックを使いまわし、SQLの性能が悪化している場合が多い。こういうパターンについても解決の示唆が欲しかった
Hidden comment
Hidden comment
susansusan

「CHAPTER 3 業務ロジックをわかりやすく整理する」 要点まとめ

要約

  • メソッドを必ずインスタンス変数を使う形でクラスにまとめる。肥大化したら同じ変数を使うメソッド群ごとに分割し、パッケージ階層で整理
  • 受注日や単価判定などの小ドメインオブジェクトを組み合わせて大きなドメインモデルを構築する。三層各層は業務判断や計算をすべてドメインオブジェクトに委ねる
  • 増えたクラスはパッケージやサブパッケージで整理する。深い階層や少数クラスのパッケージ構成をいとわない

以下、詳細

用語定義

  • データクラス: ロジックを持たないデータを格納するだけのクラス。以下が本書の著者による例
    • DTO: サブシステム間でデータをやり取りする
    • Entity: DBとデータをやり取りする入れ物クラス
    • Form: 画面とデータをやり取りする入れ物クラス
  • 三層アーキテクチャ: プレゼンテーション層、アプリケーション層、データソース層
  • 手続き型の設計: データクラスとそれを使う機能クラスを分ける設計
  • 共通ライブラリ方式: 共通で使いたいロジックを集める方式。いわゆるUtilとかCommon
  • ドメインオブジェクト: 関連する業務データと業務ロジックを1つにまとめたクラス
  • ドメインモデル: ドメインオブジェクトを集め整理したもの
  • 三層+ドメインモデル方式: 三層の各層が、ドメインモデルを利用する

「手続き型の設計」批判

  • データクラスを使うロジックは三層のどこにも書けてしまい、業務ロジックが重複しがち。
    • アプリケーション層に業務ロジックを集めても、どこに何が書いてあるかの見通しが悪くなりがち
      • 「画面と機能クラスを1対1で関連づける設計」もその一例
  • 共通ライブラリ方式の失敗2パターン
    • 1: 汎用化:
      • ニーズが微妙に異なるために、引数にフラグやオプション引数を増やした結果、使う側が理解するのが大変になり、各自が自作するようになり、同じロジックがあちこちに書かれる
    • 2: 細分化:
      • 共通ライブラリのメソッド数が膨張し、自分のニーズに合ったメソッドを探したり、微妙な違いを理解し使い分けるのが大変になり、各自が自作するようになり、同じロジックがあちこちに書かれる

「手続き型の設計」への対案の方針2つ

1.データとロジックを一体にして業務ロジックを整理する
2.三層のそれぞれの関心事と業務ロジックの分離を徹底する

1.データとロジックを一体にして業務ロジックを整理する

  • メソッドは必ずインスタンス変数を使う
  • クラスが肥大化したら小さく分ける(同じインスタンス変数を使うメソッドグループで分ける)

「データとロジックを一体にして業務ロジックを整理」していくと、クラスが増える。どこに何が書いてあるか見つけにくくなるという批判に対して

  • パッケージでクラスを整理すればよい
  • 関連性の強いクラスは同じパッケージに集める。スコープはなるべくパッケージプライベートにする
  • 更にクラス数が増えたらサブパッケージに分ける
  • 階層が深くなったり、1つのパッケージに1つか2つのクラスになってしまうが、それでもよい

2.三層のそれぞれの関心事と業務ロジックの分離を徹底する

  • 小さい単位の業務データと業務ロジックからドメインオブジェクトを作っていく
    • 例: 受注日に関係するOrderAcceptDate、単価の計算や判定はUnitPrice、etc.
    • こういう小さなドメインオブジェクトを組み合わせて「顧客」「商品」「注文」等大きなクラスを作っていき、整理したものがドメインモデル
  • このように整理するとクラス数が膨大になる。どこに何が書いてあるか見つけにくくなる。この場合も、パッケージに分けて整理すればよい
  • 「三層+ドメインモデル方式」で、すべての業務ロジックをドメインモデルに集め、三層の各層は、業務上の判断/加工/計算のロジックをドメインオブジェクトに任せる
susansusan

「CHAPTER 3 業務ロジックをわかりやすく整理する」 私の意見・疑問

本章の内容に対して、私の意見や疑問を書く。

ディレクトリ階層やファイル数は少ない方が好ましい

「データとロジックを一体にして業務ロジックを整理」していくと、クラスが増える。どこに何が書いてあるか見つけにくくなるという批判に対して

  • パッケージでクラスを整理すればよい
  • 関連性の強いクラスは同じパッケージに集める。スコープはなるべくパッケージプライベートにする
  • 更にクラス数が増えたらサブパッケージに分ける
  • 階層が深くなったり、1つのパッケージに1つか2つのクラスになってしまうが、それでもよい

階層が深くなったり、クラス数(≒ファイル数)が増殖しても良いとの主張だが、
私の経験では、システムの規模にもよるが、提供できる機能が同じならば、ディレクトリ階層やファイル数はより少ない方が好ましい。そのほうが修正対象を探しやすく認知的ストレスが小さい。

※もちろん、Javaでは1ファイルにpublicなクラスは1つ、Pythonのようにクラスに属さないトップレベル関数は定義できない、従って、ディレクトリ階層が深く、ファイル数が多くなりがち、といった制限があることは理解している。
※Java使いとPython使いの感覚の違いというだけの話かもしれない。

大量のディレクトリ階層やファイルを辿って、1つずつファイルを開けつつ修正対象を見つけるのは辛い。IDEのエクスプローラーペインで目的のファイルまでネストを把握しながらスクロールするのは時間がかかり、結構なストレスを感じる。

それよりも、例えばDjangoで言えば、1つのDjango Appごとに1つのviews.pyや1つのservices.py(及びrepositories.pyなど)が存在し、それをIDEで開いて、テキスト検索やシンボルのアウトラインを手がかりに修正対象を探すという方式のほうが認知的なストレスは小さい。
逆に言うとそれが可能なくらいの分量でDjango Appやview、サービス等が切られていたほうが好ましい。ディレクトリを階層化するとしてもレイヤー毎に2階層くらいまでが好ましい。

「すべての業務ロジックをドメインモデルに集め」るのは性能に懸念が出そう

  • 「三層+ドメインモデル方式」で、すべての業務ロジックをドメインモデルに集め、三層の各層は、業務上の判断/加工/計算のロジックをドメインオブジェクトに任せる

まず、「業務上の判断/加工/計算のロジック」が何を指すのか判然としない。
「業務上の判断/加工/計算のロジック」と「それ以外の判断/加工/計算のロジック」があるということだろうか? そうするとその違いは何なのか?

それはともかく、パフォーマンスを優先する場合、SQLクエリ上で判断/加工/計算/集計をすることも多い。
すなわち、DBからデータを取得した後にプログラム側でこねくり回すよりも、SQLクエリ上で判断/加工/計算/集計を済ませてから取得したほうが高速なことは多い。
そうなると、「業務上の判断/加工/計算」を「すべて」「ドメインモデル」側に任せるというのはパフォーマンスを犠牲にすることもありそうだ。

(私が誤解しているかもしれない。後の章でこの辺語られるかもしれないが、現時点での考え)

susansusan

「CHAPTER 4 ドメインモデルの考え方で設計する」 要点まとめ

要約

  • 業務分析と設計は同一人物が担当し、業務用語をクラス名・メソッド名に反映する
  • 小さなドメインオブジェクトをボトムアップで組み立てつつ全体像はパッケージ図や業務フロー図で把握する。重要度順に実装・テストする
  • ドメインオブジェクトの発見は「ヒト/モノ/コト」の視点で関係性や時間軸、妥当性ルールを洗い出し、部品を組み合わせながら不足を補う
  • 基本パターン(値オブジェクト/コレクション/区分オブジェクト/状態遷移)や業務関心パターン(口座/期日/方針/状態)を使い分ける
  • 専門家との対話を通じて、ドメインモデルを洗練させる

以下、詳細

ドメインモデルの考え方を理解する

  • 業務を分析しながら設計クラスを発見していくために、分析と設計を、同じ人が担当するべきだ
  • 現実の業務で使われている用語をクラス名やメソッド名にする
  • ドメインモデルとデータモデルの違い: ドメインモデルは業務、データモデルはデータが主役
    • 例: 「年齢」が業務の関心事なら、ドメインモデルの「年齢」クラスは、生年月日をインスタンス変数に持ち、年齢を計算するロジックをメソッドに持つ。他方、データモデルのテーブルでは、生年月日だけを記録する。テーブル設計の段階では「年齢」という業務の関心事が消えてしまう
  • データモデルを中心に設計を進めると、データクラスと機能クラスに分ける手続き型の設計になる

ドメインモデルをどうやって作っていくか

  • ボトムアップで、部分から全体を組み立てていく
  • ボトムアップ方式だとしても、全体像は意識する。パッケージ図や業務フロー図で全体を俯瞰する
  • 全体を俯瞰したら、重要な部分から作る。
    • 重要だと判断できた段階で、実際に作る。データと関連するロジックがひとかたまりになっているので、単体として動作しテストができる
  • 重要な順に、ドメインオブジェクトを作っていく
  • 部品であるドメインオブジェクトを組み合わせて機能を実現するのはアプリケーション層の役割

ドメインオブジェクトの見つけ方

  • 業務をヒト/モノ/コトに分類する
  • コトに注目し、ヒトとモノの関係や、時間軸の前後関係を明確化する
  • 業務における妥当性のルール(約束事)を特定し、そのルールを実現するデータとロジックを考える
  • ある程度の部品が揃ってきたら、組み合わせてみながら、個々の部品を調整したり、不足している部品を追加することで、網羅性のあるドメインオブジェクトが整っていく

業務の関心事の基本パターン

  • ドメインオブジェクトの基本4パターン
  • 業務の関心事の4パターン
    • 口座(Account)パターン: 現在高を表現し、妥当性を管理
    • 期日(DueDate)パターン: 約束の期日と判断を表現
    • 方針(Policy)パターン: ルールが複合する複雑な業務ロジックを表現
    • 状態(State)パターン: 状態遷移のできる/できないを表現

「期日(DueDate)パターン」を例に

出荷期日と支払期日という業務ルールがある場合には、それぞれ異なる約束事が存在するはず。
扱うデータや計算が似ていても、ShippingDueDateとPaymentDueDateクラスは分けるべき。
本当に共通のロジックはDueDateクラスを2つのクラスから部品として使い再利用する。
1つのクラスの中で出荷期日と支払期日のルールをif文で書き分けるような設計はNG。

ドメインオブジェクトの設計を段階的に改善する

  • 業務の言葉とコードを一致させる
  • 業務を学び、分析して得た業務知識や言葉を、パッケージ名/クラス名/メソッド名/変数名にする。このようにソースコードで業務の要求仕様を実現することを自己文書化という。

業務の理解がドメインモデルを洗練させる

  • 業務の経験者・専門家と会話し、メモ書きや絵でアウトプットして確認してもらう。そのやり取りの過程で重要な言葉を見極めていく
  • 形式的・網羅的な詳細ドキュメントよりも、重要な言葉と基本構造を理解することのほうが重要
  • 基本知識の取得: 業務のマニュアルを読む → 一般的な書籍があれば読む → 業務で使っている画面などを見る → 経験者と会話する。この順番にやるのがよい。
susansusan

「CHAPTER 4 ドメインモデルの考え方で設計する」 私の意見・疑問

本章の内容に対して、私の意見、疑問を書く。

ドメインモデルとデータモデルを対立させて論じている点に違和感

  • ドメインモデルとデータモデルの違い: ドメインモデルは業務、データモデルはデータが主役
    • 例: 「年齢」が業務の関心事なら、ドメインモデルの「年齢」クラスは、生年月日をインスタンス変数に持ち、年齢を計算するロジックをメソッドに持つ。他方、データモデルのテーブルでは、生年月日だけを記録する。テーブル設計の段階では「年齢」という業務の関心事が消えてしまう
  • データモデルを中心に設計を進めると、データクラスと機能クラスに分ける手続き型の設計になる

本章で、(図式の単純化のためもあろうが)ドメインモデルとデータモデルを対立させているが、2つは対立するものだろうか? むしろ、相互に分析し改善していくのが良いのではないか?

「データモデルを中心に設計を進めると、データクラスと機能クラスに分ける手続き型の設計になる」との主張だが、必ずしもそうではない。
例えば「年齢」の例について考える。
実装観点では、Djangoなら生年月日を持つModelに年齢を導出する@propertyを定義すればよい。
設計観点でも、データモデル図なり設計書なりに導出フィールドである旨を記載した上で「年齢」フィールドを書いておけばよい。

また本章の記述からは、「データモデルを中心に設計してはならず、ドメインモデルから設計や実装を始めなければならない」ように読み取れる。
しかし、データモデル(テーブル定義やカラムの型、制約、リレーションなど)が定まってない中で、具体的な実装を書いてくのは大変ではないだろうか? もし書けたとしても、手戻りが多くなりそうだ。

業務の理解に時間をかけられる風土が必要

業務の理解がドメインモデルを洗練させる

  • 基本知識の取得: 業務のマニュアルを読む → 一般的な書籍があれば読む → 業務で使っている画面などを見る → 経験者と会話する。この順番にやるのがよい。

特に納期がシビアな現場や中途参画の場合は、業務理解の時間を多くは確保させてもらえないことも多い。そういう現場では、経験上、上記引用のような「勉強」を業務時間内にやらせてもらうことは難しく、業務時間外でやることになる。
※もちろん、業務理解(=業務の勉強会や勉強用の動画視聴、等)の時間を確保してくれる、寛容な現場も多くある。

そういったことも含め、本書の「ドメインモデル方式」を適用する現場では、本書内容やDDDを共通理解とした上で、業務理解もエンジニアの職務内容の一部として認めてくれる風土が必要そうだ。

susansusan

「CHAPTER 5 アプリケーション機能を組み立てる」 要点まとめ

要約

  • 業務ロジックはドメインオブジェクトに任せ、画面やDB入出力の都合を切り離して設計する
  • 登録系サービスと参照系サービスをCQRSで分ける
  • アプリケーション層を「基本サービスクラス群」と「シナリオクラス群」の2層構造で整理する
  • シナリオクラスで基本サービスクラスを組み合わせて画面ごとのユースケースを実現する
  • 契約による設計を採用し、nullを渡さず/返さず、事前条件は利用側が確認し、異常時は例外で通知して防御的プログラミングを避ける
  • DB操作はリポジトリを介して行い、メソッド名・引数・返り値はすべて業務用語で表現し、インターフェースと実装クラスを明確に分離する

以下、詳細

用語定義

  • サービスクラス: 三層アーキテクチャのアプリケーション層のクラスのこと
  • 契約による設計: サービスを使う側が事前に必要条件を確認する設計
  • 防御的プログラミング: 「契約による設計」の対極。サービスを提供する側が、利用する側を不可知として様々な防御的ロジックを書く設計
  • リポジトリ: ドメインオブジェクトをメモリ上に保管して取り出しができる収納場所

ドメインオブジェクトを使って機能を実現する

  • 本書での「サービスクラス」の定義: 三層アーキテクチャのアプリケーション層のクラスのこと
  • サービスクラスの設計方針
    • 業務ロジックはサービスクラスに書かずにドメインオブジェクトに任せる
    • 画面の複雑さをサービスクラスに持ち込まない
    • DBの入出力の都合からサービスクラスを独立させる

画面の多様な要求を小さく分けて整理する

  • 登録系サービスと参照系サービスを分ける(CQRS)
  • アプリケーション層を、「基本サービスクラス」群と、それを組み合わせて使う「シナリオクラス」群の2層構造にする
  • 「契約による設計」を行う。「防御的プログラミング」を避ける。以下がそのための約束事
    • null を渡さない/返さない
    • 状態(事前条件)に依存する場合、使う側が事前に確認する
    • 約束を守った上で更に異常が起きた場合、例外で通知する

DBの都合から分離する

  • サービスからはリポジトリを利用して業務データの記録や参照を行う
  • リポジトリのメソッド名/引数/返す値は、すべて業務の用語で表現する
  • リポジトリのインターフェースと実装クラスを分離する。実装クラスでデータベース操作を行う
susansusan

「CHAPTER 5 アプリケーション機能を組み立てる」私の意見・疑問

本章の内容に対して、私の意見や疑問を書く。

「業務の用語」とは?

リポジトリのメソッド名/引数/返す値は、すべて業務の用語で表現する

この本における「業務の用語」の定義が判然としない為、私が誤解しているかもしれない。
だが私の経験では、ほとんどのプロジェクトにおいて、データベース操作は複雑なREAD系クエリ(複雑なテーブル結合や集計が絡む)の比重が大きい。
こういった複雑なクエリは、大抵、画面仕様の都合で要請されるものだが、「業務の用語」として表現できるのだろうか? 画面仕様も含めて「業務」と考えろということだろうか?
だが、そもそも本書の主張自体が、業務の関心事とそれ以外を分離しよう、ということだったと思うのだが。。。

「リポジトリ: ドメインオブジェクトをメモリ上に保管して取り出しができる収納場所」とは?

リポジトリ: ドメインオブジェクトをメモリ上に保管して取り出しができる収納場所

とのことだが、リポジトリからの取得結果はすべて「ドメインオブジェクト」にせよということだろうか? つまり、リポジトリから値を返却する際には、DBから取得したデータを「ドメインオブジェクト」に詰め替えろ、ということだろうか? そしてそれが上述の「業務の用語」となるようにせよ、ということだろうか?

リポジトリパターンについて

経験上、リポジトリパターンの実装で、テーブル単位(Djangoで言えばModel単位)でリポジトリクラスが切られていることがよくある。
この場合、複数テーブルが絡む複雑なクエリをどう扱うかについて実装方針に開発者間のブレが生じやすい。

また、処理の重複を避けようとして、既存リポジトリクラスのメソッドを使おう(使わせよう)とするあまり、性能に問題があるクエリになることも多い。
例としては、既存メソッドを使って一度クエリを発行して、その結果を使って更に別のクエリを発行するなど。このような場合サブクエリを使ってクエリ発行を一回で済ませるほうが早い事も多い。

現時点で、本書のサンプルコードに出てくるリポジトリクラスやクエリは単純なものがほとんどで、こういった複雑な場合にどうするべきかという観点についても言及が欲しかった。

susansusan

「CHAPTER 6 データベースの設計とドメインオブジェクト」要点まとめ

要約

  • カラムはすべて NOT NULL 制約を付ける。どうしても NULL が必要な場合は別テーブルに分割
  • DBは「コトを記録する」場所として、既存データを UPDATE せず、元データ・取り消しデータ・新データを INSERT だけで残す
  • 後から新しいデータ項目を追加したい場合は既存テーブルに NULL 許容カラムを増やすのではなく、新テーブルを作成して外部キーで紐付ける
  • 「記録」用テーブルとは別に「状態」用テーブルを用意し、DELETE/INSERT の対で毎回新しい状態行を作成
  • ドメインオブジェクト設計とテーブル設計は別々に進めて明示的にマッピングし、オブジェクト側にDB操作やフレームワークの都合を持ち込まない

以下、詳細

データベース設計をすっきりさせる

  • 自然に正規化を行う方法: カラムはすべてNOT NULL制約にする。 それでもNULL値がどうしても必要なカラムを見つけたら、別のテーブルに分ける

コトに注目するデータベース設計

  • データベースの本質はコトを正しく「記録」すること
  • 「記録」なので、UPDATE文を使うべきではなく、元データ、取り消しデータ、新データの3つが残るようにINSERT文だけを使う
  • 今まで記録していなかったデータを記録する場合、既存テーブルへのカラム追加は好ましくない。追加するカラムに過去データがないため、NULL許容にするか、NOT NULL制約を逃れるためのダミーデータを登録することになる。
    • このような場合の対処方法は、テーブルを追加することである。追加するデータ項目をカラムに持つテーブルを新しく作る。追加したテーブルから元のテーブルに外部キー制約を宣言する

参照をわかりやすくする工夫

  • 理論的には「コト(=記録)」から「状態」を導出可能だが、性能上そうはいかないことがある
  • 「状態」を参照する際に有用なパターン: コトの記録のテーブルとは別に、コトの記録のたびに状態を更新するテーブルも用意する
    • 例: 口座に入金があったら入金テーブルに記録。そして、残高テーブルの口座残高も増やす
    • この際の「状態」「残高」テーブルはUPDATE文ではなく、DELETE/INSERT文の対で毎回新しい残高を作成する
    • 「記録」と「残高の更新」をトランザクションとして処理しなくてもよい。例えば非同期メッセージングで残高更新を行うことも検討可能

オブジェクトの設計とテーブルの設計

  • ドメインオブジェクトとテーブルは別物として設計と改善を進め、オブジェクトとテーブルのマッピングは両方の設計の進度に合わせて明示的に定義するようにする
  • ドメインオブジェクトに、データベース操作やFWの都合を持ち込まない
    • MyBatis SQL Mapper を推奨
    • JPA(Java Persistence API)は非推奨
susansusan

MyBatis SQL MapperとJPAについて補足

MyBatis SQL MapperとJPA(Java Persistence API)がどういうものか知らないのでChatGPTに聞いてみたところ以下の回答を得た。
Django標準のORM ≒ JPA である様子。
ただ、エンティティ(DjangoではModelに相当)は純粋なPOJOで扱えるという点が違うか。

項目 MyBatis JPA
マッピング定義 - XML ファイルまたは注釈で SQL+結果マッピング
- 明示的に <select>, <insert> 等を定義
- エンティティクラスに @Entity, @Column 等を注釈
- persistence.xmlspring-boot の自動設定
開発の流れ 1. SQL を書く
2. パラメータ/結果マッピングを書く
3. インターフェース呼び出し
1. エンティティクラスを書く
2. Repository(DAO)インターフェースを定義
3. ※SQL は基本書かない
学習コスト - SQL が得意ならすぐ始めやすい
- マッピング設定の書き方を覚える必要あり
- ORM の概念・ライフサイクル(永続化コンテキストなど)を理解する必要あり

MyBatis

長所

SQL をフルコントロールできる → 複雑な結合やチューニングがしやすい
「SQL が最適化しやすい」「DB 層に権限を集中させやすい」

短所

マッピング定義が煩雑になりやすい
SQL の変更に伴う XML 更新が手間

JPA

長所

CRUD 開発が爆速(Django ORM と同様)
エンティティは純粋な POJO(Plain Old Java Object)で扱える
トランザクション+キャッシュで最適化されやすい

短所

ブラックボックス感が強く、複雑クエリやパフォーマンスチューニングが難しい
N+1 問題など ORM 特有の落とし穴あり

susansusan

「CHAPTER 7 画面とドメインオブジェクトの設計を連動させる」要点まとめ

DRF使いとしては、バックエンドサーバーでHTMLを生成して返すという構成をもはやほとんど触ることがない(Django Adminくらい)。そのため画面との連動を論じるこの章に関しては流し読みだが、役立ちそうな箇所をまとめる。

画面の関心事を小さく分けて独立させる

  • タスクベースのUI: 用途を特定した小さな単位に分けた画面を提供する
    • 例: 注文画面で、注文者情報・注文内容・決済方法・配送手段・連絡先を1つの「何でも画面」で入力させるのではなく、それぞれの情報を独立して登録・変更できる画面を提供する
  • それでも「何でも画面」がいい、という場合でも、内部設計はタスクベースに分けておくべき

ドメインオブジェクトに書くべきロジック

  • 場合ごとの表示の違いのロジックも、ドメインオブジェクトに置くべき(例: 検索結果が「見つかりませんでした」or「n件見つかりました」)
    • 理由: 変更を楽にする。情報の文字列表現は利用者の関心事そのものだから
  • この他にも、カンマ編集や千円単位の表示などもドメインオブジェクトが持つべき
susansusan

「CHAPTER 7 画面とドメインオブジェクトの設計を連動させる」私の意見

本章の内容に対して、私の意見を書く。

SPA + REST APIバックエンドでは、画面表示ロジックはフロントエンド側で持つ方式で統一したほうが良い

ドメインオブジェクトに書くべきロジック

  • 場合ごとの表示の違いのロジックも、ドメインオブジェクトに置くべき(例: 検索結果が「見つかりませんでした」or「n件見つかりました」)
    • 理由: 変更を楽にする。情報の文字列表現は利用者の関心事そのものだから
  • この他にも、カンマ編集や千円単位の表示などもドメインオブジェクトが持つべき

バックエンドサーバーでHTMLを生成して返す場合は上記方式で良いかも知れないが、「SPA + REST APIバックエンド」の構成では、画面表示に関するロジックはフロントエンド側で持つ方が良さそう。
理由:

  • フロントとバックでチームや作業者が別れている場合、フロント側だけで変更を完遂できる
  • webとスマホ版で表示仕様が変わる場合などに対応できる
  • 表示形式はUI側の文脈に依存する
  • 国際化(i18n)やローカライズ(l10n)対応もフロント側が柔軟
このスクラップは2ヶ月前にクローズされました