📚

ドメイン駆動設計 モデリング/実践ガイド 読書録

2022/12/05に公開

この記事は積ん読消化 Advent Calendar 2022の12/5の記事で、書籍ドメイン駆動設計 モデリング/実践ガイドの感想文です。

https://booth.pm/ja/items/1835632

書籍ドメイン駆動設計 モデリング/実践ガイドは今年読んだ本で1番良かったかな。

この本を読むことになったきっかけ

何度かBOOTHで見かけていて興味があったのがきっかけ。もちろんエリック・エヴァンスのDDD本は読んでいるし、無料のDomain Driven Design Quicklyも読んだことがある。

https://gihyo.jp/dev/clip/01/orangenews/vol52/0001

でも、まあDDDっていうのは日本のSIerの要件定義や用語定義とかから始まるやり方と根本には似ているかなと思っていて、さらにそれプラス技巧的なことかなと思ってた感じはありますね。

心に残ったこと

まず章の構成が基礎からのボトムアップじゃないということ。もちろん第1章はDDD概要から始まるんだけど、著者が言いたいことは前半に寄せていてそれを理解するためにも概要の第1章がある。もっとも重要なのは第2章の「モデリングから実装まで」の章じゃないかなと。もちろん第1章も「モデル」などのあまり説明されることのないコモンセンスになってしまった基礎用語をしっかりと解説してくれるのが良い。

読む上で必要になる前提知識

前提知識はなくていいけども、DDDを解説しているブログとか読んでおくといいかもしれない。さらにCQRSとかググって軽く見とくといいかも。なぜかというと大抵ブログで書かれてることは真をくってないというか、核心的なことに触れていないので、この本を読んだときに何が核心なのかということに気付かされる。

各章ごとの感想

第1章 DDD概要

書籍では「問題解決のために物事の特定の側面を抽象化したもの」と定義されその説明があるのが良かった。

そもそも古くからプログラミングをやってると、なんとなくモデリングというプロセスを経てモデルというものを分かった気になってしまうからこれを最初に言語化してて偉い。

他にもレイヤー化の意義について、フレームワーク固有部分からドメイン層を分離し、ドメインモデルをコードと近づけるという説明も良い。

第2章 モデリングから実装まで

2章はモデリングというプロセスのための説明が書かれている。UMLはもう20年くらいあるからなんとなく分かってる感がしてるけどDDDのために読み飛ばさずさくっと復習できてよかった。

この章のというかこの本の私が一番重要だと思うのは、ドメイン知識をUseCaseに書くんじゃないという例をToDo管理アプリで説明してくれてる。

ざっくりと

  • Task作成のTaskCreateUseCaseがある
    • これにTaskの初期状態などを書いて初期化してしまうとドメイン知識をUseCaseが知ってることになる
      • そのためにTaskのイニシャライザで初期化を書く

Swiftで表現してみると次のような感じ(書籍には書いてない私の理解が含まれています)

class Task {
    typealias ID = UUID
    private let id: ID
    private var name: String
    private var dueDate: Date
    private var status: TaskStatus
    private var postponeCount: Int // 期限を伸ばす回数
    
    init(name: String, dueDate: Date) {
        self.name = name
        self.dueDate = dueDate
        // Taskのドメイン知識
        status = .undone    // Taskは未完了から始まる
        postpponeCount = 0  // Taskの延期回数は0から始まる
        id = UUID()
    }
    
    ...
}
// TaskCreateUseCaseには`createTask`メソッドがあるがそこからルールに関するコードを除外
struct TaskCreateUseCase {
    private let taskRepository: TaskRepository
    
    func createTask(name: Sting, dueDate: Date) {
        let task = Task(name: name, dueDate: dueDate)
        // 以下はドメイン知識なのでUseCaseから除外したい ----
//        task.set(status: .done)     // × 完了状態でタスク生成で不整合
//        task.set(postponeCount: -1);  // × カウントがまさかのマイナスで不整合
        // ------------
        taskRepository.save(task)
    }
}
  • UseCaseはユースケース図からユーザの要求に対するシステムの振る舞いを書く
    • ドメイン知識をUseCaseには書かない
      • ドメイン知識はドメインモデルに書く

第3章 DDD固有のモデリング手法

集約とは「必ず守りたい強い整合性を持ったオブジェクトのまとまり」としている。そして集約ルートとは何かが説明されている。

The Aggregateの他にもコンテキストを境界づける必要性なども納得感ある。

第4章 設計の基本原則

凝集度や結合度などの品質を表す用語の説明もある。なので前提知識があんまりいらないように読める。

ちなみに凝集度は型の責務、データや振る舞いなどの関連の強さや弱さ。蛇足だけどSwiftはactor型があるからさらにスレッドまで決められるから責務がさらに型自身の明確さが上がり凝集度を補強できる。逆に凝集度の弱さというのはクラス名や変数名が曖昧だったりすること、または型に関係ないメソッドがあったりすること。これが凝集度。そういう名前があったのね。

結合度については単に型同士の依存度の高さや低さについて。最近iOSアプリでもモジュールを分割する方法が"流行ってしまっていて"、困ったなと思う。たとえば機能を機能ごとにモジュール分割するのってナンセンスだと思う。私の場合課題を解決するためにモジュール分割するし、必然性に従ってレイヤーを分割する。それは具体的に言語化できてるんで良いんだけど、盲目的に分割する必要のないものまで分割してしまおうとするとテクニックが追いつかなくて困るんじゃないかと思う。

第5章 アーキテクチャ

オニオンアーキテクチャとは何か、ヘキサゴルアーキテクチャとは何かについて書かれてる。しかしこの章はこの本になくても調べられるんで著者も書くのが筆が進まなかったんじゃないかな。

ただ、ドメイン層という説明は重要だし、(DDDでの?)リポジトリの説明も重要。たとえばチーム内で「リポジトリ」のコンセンサスを取るのにもかなり良いかなと思う。

他にもドメインサービス(ドメインをオブジェクトだけで表現すると無理があるもの)の必要性についても書いてある。

これもまた自分のバイアスだけど、プログラミングでの用語にサービスって付くとマネージャー感がありシンプルな用途に収まらないかなと思う。ふわっとしていて、やらんでもいいこと実装してしまいそう。

ドメインサービスの実際の実装をたくさん見てみたいなと感じた。

第6章 ドメイン層の実装

この章は5章の補足になってる感じ。詳しいい実装は省略しても良いけどコードがあったほうがいいのでは?と思った。

第7章 ユースケース層の実装

第6章にうってかわって結構コードと図がある。UseCaseの粒度もまあそうなるよなあと思う。

さらにUseCaseで共通してる処理があるとき、UseCaseの使いまわしが問題になるんじゃないかなと思うんだけどそれはコンバーターとして実装すると書かれてる。実際のコードが読みたい。

第8章 CQRS

CQRSが話題に登ることがあると軽くググるんだけど、そのたびに「参照DBと更新DBを分ける」「データソーシング」について書かれてて自分にはあんまり関係ないなと思ってしまってた。がそれはCQRSの本質ではなかったことがわかる。

この章が良いのは、CQRSが必要になる課題についてまず書かれていること。その課題の解決策としてCQRSがあるよねということ。

めちゃくちゃ平易に書くと、複数The Aggregateが絡んできてそれを一覧として参照する際にはQueryServiceという型から取り出せるようにし、Repositoryを使わない別の方法を提供することで、可読性、パフォーマンス、ページングなどのデータ取得をやりやすいようにできるよということ。

第9章 プレゼンテーション層

プレゼンテーション層はクライアントアプリで表示する際に求める形に出力を整えるもの、逆に入力も整える。といった感じ。

重要なのはユースケース層ではそれらを整えないということでしょう。

第10章 アークテクチャ全般・ライブラリなど

例外処理やバリデーションについて書かれてる。

バリデーションは例えばUI側でリアルタイムに行いたい場合もあるから、そうなるとドメイン層のバリデーションと重複することもある、とか書かれてる。

DDD向きの言語については、静的型付けでかつ制約を表現できてオブジェクト指向の型で表現するほうが向いてるような感じで書いてありRubyが向いてないという感じで書かれてる。

向いている条件として、静的型付け言語であることです。DDD では、モデルのルール/ 制約をオブジェクト指向の型で表現します。動的型付け言語の場合、表現した制約を容易に破れてしまうため、効果を十分に得にくいです。ただし、動的型付け言語の場合でも、 タイプヒントと静的解析の組み合わせで補完することはできます。
この条件を考えると、Java、Scala は向いている、Python、PHP、JavaScript でもできる、Rubyは現時点では不向き、ということが言えるでしょう。

オブジェクト指向の型っていう部分はあんまりというかほぼ私の同意はなくて、関数型プログラミング的な代数的データ構造が表現できるほうがドメインを表現するのに適切かなと思った。

今後アクションしてみたいことリスト

  • ドメイン知識がUseCaseに書かれてしまってない?というコミュニケーションをしてみたい
  • Web上の記事で、これってドメインロジックとUseCase(アプリケーションロジック)を混同していない?と疑ってメモしたり
  • ドメインサービスの実際のコードを読んでみたい

Discussion