Python における
ドメイン駆動設計(戦術面)の
勘どころ
2017-09-09 @ PyCon JP 2017
林 淳哉
自己紹介
● 林 淳哉 (@loose_agilist)
● GROOVE X でロボット開発
● 「実践ドメイン駆動設計」 Reviewer
● Qiita に DDD の記事書いてます
今日おはなしすること
● ドメイン駆動設計とは
● ドメイン駆動設計の悩みどころ
● Todo List を例にしたドメイン駆動設計の実践
○ https://github.com/ledmonster/ddd-python-inject
$ git clone
https://github.com/ledmonster/ddd-python-inject
$ cd todolist
$ python setup.py develop
$ ./bin/todo --help
ドメイン駆動設計とは何か
● Eric Evans が提唱した設計手法
● モデリング技術 や オブジェクト指向設計 を抽象化し、
問題領域の抽出から、アーキテクチャ設計、モデリング、
実装に至る全工程を 一貫性のある体系 として整理したも
の
おすすめ書籍
エリック・エヴァンスのドメイン駆動設計 (2011)
“Domain Driven Design” Eric Evans (2003)
DDD 信者の聖書。その難解さから「鈍器」と呼ばれる。抽象度が高く、分厚いので
読み通すの大変だが、何回読んでも発見がある。ここで整理されている概念は、 10
年以上経った今も色褪せない。 
実践ドメイン駆動設計 (2015)
“Implementing Domain Driven Design” Vaughn
Vernon (2011)
Evans 本の中身は本質を突いているが、その実践は容易でない。本書は、 Evans
本以降の10年間に発表された新しい開発技法を取り込みながら、 DDD の考え方
を実践に移すにはどうすれば良いかを記述した指南書。
※ 読むのに挫折しても、鈍器としてなら使えます
※
※ 翻訳レビューに参加しました。
ドメイン駆動設計を構成する要素
● 「戦略」的側面
○ 将棋でいう「定跡」
○ プロダクト全体を俯瞰するための考え方
● 「戦術」的側面
○ 将棋でいう「手筋」
○ 個々の実装のための考え方
● ユビキタス言語
○ プロダクト全体で統一された言葉
を使おう
● コアドメイン、サブドメイン、汎用
ドメイン
○ 問題領域を整理しよう
● 境界付けられたコンテキスト
○ 実装するシステムを整理しよう
● コンテキストマップ
○ システム間の関係を整理しよう
ドメイン駆動設計の
「戦略」的側面
ドメイン駆動設計の
「戦術」的側面
● アーキテクチャ
○ MVC, MVVM などの実装パターン
● ドメインモデル
○ エンティティ
■ 識別子を持つもの
○ 値オブジェクト
■ 値に型を与えたもの
○ サービス
■ 振舞いだけを提供するもの
○ リポジトリ
■ 永続化の仕組み
○ ファクトリ
■ オブジェクトを生成する仕組み
○ 集約
■ 不変条件を管理する単位
○ ドメインイベント
■ エンティティの変化を表現したイベント
● アプリケーション
○ コンテキスト全体の調整役
ヘキサゴナルアーキテクチャとは
● 依存性逆転の原則(DIP)を用いて、
ドメインモデルから外部システム(DB・UIなど)への
依存を排除したアーキテクチャ手法
● 古い方法
○ 商品管理のロジックが Redis に依存する
● ヘキサゴナルアーキテクチャ
○ 商品管理ロジックにストレージのインタフェースを定義し、
インタフェースに合うように MySQL アダプタを実装する
CQRSとは
● Command and Query Responsibility Segration
○ コマンドクエリ責務分離
○ Greg Young (2010)
● クエリ(副作用なし)とコマンド(副作用あり)を分離する考え方
○ 一貫性
■ 「コマンド」では整合性が求められる
■ 「クエリ」ではあまり気にしない
○ ストレージ
■ 「コマンド」では正規化してデータを保存したい
■ 「クエリ」では非正規化して効率的にデータを取得したい
○ スケーラビリティ
■ 「コマンド」は負荷が大きくなりづらい
■ 「クエリ」は負荷が大きくなりやすい
● ドメイン駆動設計は、コマンドと相性が良い
いざ実践しようとすると悩む・・・
悩みどころ
● 参考になる Python 実装がない
● ソースコードが肥大化しがち
● リポジトリの実装が複雑になってしまう
● 値オブジェクトの不変性を表現できない
● ヘキサゴナルアーキテクチャの使い方が分からない
● CQRS を適用したいけど、分からない
● 名前の付け方で悩む
● OR Mapper を使って良いのか悩む
というわけで
サンプルを作りました
$ git clone
https://github.com/ledmonster/ddd-python-inject
$ cd todolist
$ python setup.py develop
$ ./bin/todo add --name 発表資料を作る
#1: 発表資料を作る
$ ./bin/todo list
[ ] #1: 発表資料を作る
$ ./bin/done 1
[x] #1: 発表資料を作る
Task
TaskRepository
TaskStatus
User
UserService
TaskRedisR
epository
TaskMemory
Repository
Redis
Memory
Simple
UserService
app/cliConsole (app/http)
Web
未実
装
Domain Model
Adapters
Read Model
TaskQuery
TaskDto
Todo List
考え方
● 名前を大切にする
● テストを書く
● ヘキサゴナルアーキテクチャを活用する
● DI コンテナを活用する
● DBアクセスライブラリを活用する
● CQRS で読み出し専用ロジックを分離する
● 値オブジェクトを活用する
名前を大切にしよう
● 違和感を感じたら直す
○ 始めから適切な名前がつけられるとは限らない
○ 良い名前を思いついたら、チームで合意して、直す
○ 適切な名前をつけると、モデルの中身も整理される
● 長過ぎる名前を避ける
○ 経験上、長すぎる名前は活用されづらい
○ 時には略称を活用する
■ 例: decision making → dm
● 曖昧な言葉は、ドメインに対する理解不足のサイ
ン
テストを書こう
● メンタルモデルとソースコードを合わせることが大切
● テストがないと、違和感のあるコードを修正できず、
ドメイン駆動設計の効果が半減する
ヘキサゴナルアーキテクチャを活用しよう
● ドメインモデルを中心に置くことで、
テストのしやすい、柔軟なシステムが構築できる
● アプリケーションは、「アダプタ」+「設定」
○ HTTP / CLI アダプタ
○ 各種設定
Task
TaskRepositor
y
TaskStatu
s
User
UserService
TaskRedis
Repository
TaskMemor
yRepository
Redis
Memor
y
Simple
UserService
app/cliConsole (app/http)
Web
未実装
Domain Model
Adapters
DI コンテナを活用しよう
DI コンテナを使わない場合
● コンストラクタで依存性を注入
● 依存関係が増えると悪夢
DI コンテナを活用しよう
DI コンテナ(inject)を利用
● https://pypi.python.org/pypi/Inject/3.1.1
● DI コンテナで依存関係を一元管理
DI コンテナを活用しよう
DI コンテナ(inject)を利用
● https://pypi.python.org/pypi/Inject/3.1.1
● DI コンテナで依存関係を一元管理
● コンストラクタが汚染されない
DB のアクセスライブラリを活用しよう
● リポジトリの実装に OR マッパーなどの
ライブラリを使っても構わない
● Todo List での実装
○ adapter/redis 以下にスキーマ定義
○ adapter/repo/redis でスキーマを利用
DB のアクセスライブラリを活用しよう
https://github.com/groove-x/gxredis
todolist.adapter.redis.task todolist.adapter.repo.task.redis
CQRS で読み出し専用ロジックを分離しよう
失敗談
● 検索要件が増えると、リポジトリが肥大化
● リポジトリの入出力はエンティティや値オブジェクトのため、
○ 入出力のオーバーヘッドが大きい
○ 書式の最適化がしづらい
○ ドメインモデルと整合性を取りながらメンテナンスするのが大変
○ DB 固有の最適化がしづらい
CQRS で読み出し専用ロジックを分離しよう
改善案
● 読み出し専用の処理は read_model として分離する
○ リポジトリをシンプルに保てる
○ 読み出し処理を最適化できる
値オブジェクトを活用しよう
● エンティティが増えがち
● コンテキスト内で属性を変更しないなら、
値オブジェクトにできる
● 例
○ 外部のコンテキストで管理している
オブジェクトを参照する場合
Task
TaskRepository
TaskStatus
User
UserService
TaskRedisR
epository
TaskMemory
Repository
Redis
Memory
Simple
UserService
app/cliConsole (app/http)
Web
未実
装
Domain Model
Adapters
Read Model
TaskQuery
TaskDto
Todo List
まとめ
● Python でドメイン駆動設計を実践しました
○ 名前の付け方を大切にしよう
○ テストを書こう
○ ヘキサゴナルアーキテクチャを使って
ドメインモデルと外部依存の実装を分離しよう
○ DIコンテナ(inject)で依存性を管理しよう
○ DB のアクセスライブラリを活用しよう
○ CQRS で読み出し専用ロジックを分離しよう
○ 値オブジェクトを活用しよう
Python におけるドメイン駆動設計(戦術面)の勘どころ

Python におけるドメイン駆動設計(戦術面)の勘どころ