Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

8,516 views

Published on

PyCon JP 2017 発表資料です。

Published in: Software
  • Be the first to comment

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

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

×