Save 37% off PRO during our Black Friday Sale! »

Python で学ぶ実践的なドメイン駆動設計とレイヤードアーキテクチャ / DDD and Onion Architecture in Python

Python で学ぶ実践的なドメイン駆動設計とレイヤードアーキテクチャ / DDD and Onion Architecture in Python

2021年10月に開催された PyCon JP 2021 Day 1 の登壇資料です。

CfP より抜粋

ドメイン駆動設計(DDD)やクリーンアーキテクチャといった話題は定期的に盛り上がりを見せますが、Pythonにおける実践的な解説はいまだ多くありません。このセッションでは、分かりやすく具体的なコード例を織り交ぜながら、これらについて解説を行います。聴衆は、他の言語の仕様や実例と比較したうえで、フラットな目線で Python で DDD に取り組むことの良い点と課題点を見つけることができるでしょう。

4681d7bd0f8c32b6e3e582891938e577?s=128

Takahiro Ikeuchi

October 16, 2021
Tweet

Transcript

  1. © 2021 iktakahiro Python x DDD!!! Python で学ぶ実践的なドメイン駆動設計と レイヤード・アーキテクチャ 池内

    孝啓 - Takahiro Ikeuchi PyCon JP 2021.10.14
  2. Takahiro Ikeuchi - 池内 孝啓 株式会社Hakali 取締役CTO • @iktakahiro •

    https://github.com/iktakahiro おしごと : メンタルウェルネス・アプリ Awarefy の事業 & 開発責任者 Python, Dart(Flutter), Go, TypeScript 2
  3. 3 執筆書籍紹介

  4. Agenda • 自己紹介 & Agenda - 2分 ← イマココ •

    コードアーキテクチャの前に - 2分 • コードアーキテクチャ編 - 20分 ◦ ドメイン層 / インフラ層 / ユースーケース層 • Advanced Topics 編 5分 ◦ CRQS / UoW / FastAPI • まとめ - 1分 4
  5. 本日のベースとなるリポジトリ https://github.com/iktakahiro/dddpy 5 みなさまの力でぜひ ☆100 越えたい!!

  6. 用語の整理 • DDD ◦ ドメイン駆動設計。このセッションでは、某書籍 (エリック・エヴァン スのドメイン駆動設計) を前提に解説を進めます • レイヤード・アーテキクチャ

    ◦ このセッションでは、実質的に オニオン・アーキテクチャ を指す • ドメイン ◦ 事業やサービスが取り扱う世界のこと(用例: ドメイン知識 • ドメインオブジェクト ◦ ドメイン知識やビジネスロジックを持つクラス 6
  7. Q1. DDD に取り組んでいますか? 7

  8. 今日達成したいこと • DDD に取り組んでみたくなる • ソフトウェア・アーキテクチャの設計や実践をしたくなる 参加者が • 参考リポジトリ(※1)に対してのフィードバックを 獲得できる

    ※1 https://github.com/iktakahiro/dddpy 私が 8
  9. コードアーキテクチャの前に 9

  10. DDD に取り組む意義(個人の見解) • チームが共通言語を獲得すること (境界付けられたコンテクスト内でのユビキタス言語) • ドメインオブジェクトを独立させ、 ドメインが解決しようとしている 現実の課題に関心事を向けること ◦◦・アーキテクチャ

    = DDD ではない 10
  11. DDD から得られるもの コラボレーション促進 明解な命名 クリーンな設計 ドメイン理解の深化 11

  12. ユビキタス言語 実例 12

  13. DDD から得た学び • DDD の前に OOP !! • ユースケースを中心に考える •

    設計(書)をおそろかにしない 13
  14. 急いでいるからこそ、設計が大事 14 • 急がば回れ・手戻り減 • 設計 = インターフェースを先に決める ◦ 実装はあとでよい

    • ユースケースに注目する • 振る舞いに注目する
  15. モデリングの話しも尽きませんが Python の話をします 15

  16. コードアーキテクチャ編 16

  17. もうちょっと抽象的な話 17

  18. DDDの実践とレイヤード・アーキテクチャ DDD = ◦◦・アーキテクチャではないと言いつつも... • ドメイン層をクリーンに保つ • ユースケース層をはじめ、各レイヤーのテスタビリティを確保する 18 複雑性と不確実性に立ち向かうためにたいへん有用

  19. オニオン・アーキテクチャのすゝめ • 各レイヤの責務が明確 ◦ 特定の部位が fat になりにくい(なるけど) • 比較的学習コストが低く、チームで共有しやすい •

    某書籍を読んで挫折した人でも大丈夫 19
  20. オニオン・アーキテクチャの概略図 【ルールは単純】 外側の人は内側の人を知ってる 内側の人は外側の人を知らない 依存性逆転の原則を用いてインフラを 最も外側に置いている ドメイン ユースケース プレゼンテーション(UI) インフラ

    テスト https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/ 20
  21. インターフェースの重要性 ドメイン層 インフラ層 ユースケース層 ユースケース層は、インフラ層に依存しない ドメイン層(のインターフェース)を利用する ドメイン層のクラスを利用してユースケースを 実現する処理を実装 ドメインオブジェクトの永続化処理を実装 ドメインオブジェクトの実装や、

    オブジェクト永続化処理のインターフェースを定義 21
  22. ディレクトリ構成例 ※ ごく普通の構成です UseCase層 を application 層とする例も 22

  23. ディレクトリ構成例 詳細 23

  24. ドメイン層 • Entity • Value Object • Repository Interface ドメイン

    ユースケース プレゼンテーション(UI) インフラ テスト 24
  25. ドメイン層の構成要素 • Entity • Value Object • Domain Service ◦

    Repository Interface • First Class Collection など 25
  26. Entity id(など)によって同一性を決定する ライフサイクル(= 各プロパティの変更)を持つ = id は不変。その他のプロパティはミュータブル 26

  27. Entity の実装例 インスタンスの同一性を id プロパティのみで決定 27

  28. Value Object(VO) Entity としてのライフサイクルを持たないオブジェクト = イミュータブル すべての値(プロパティ)で同一性を決定する プリミティブ型の代替として VO を定義することもある

    28
  29. Value Object の実装例 VO としての性質の多くは @dataclass デコレータを利用することで確保できる 29

  30. Repository ドメインオブジェクト(など)の永続化処理を担うクラス • インターフェースをドメイン層のクラスとして • 実装をインフラ層のクラスとして定義 • 利用はユースケース層から! 30

  31. Repository Interface の実装例 ABCクラスを継承 @abstractmethod デコレータを利用 31

  32. ダック・タイピングが可能なのに 何故インターフェースを定義するのか? 32

  33. Python でインターフェースを定義する意義 • 他の言語のコードとアーキテクチャを揃えるため • 型を明示するため ◦ Type hint、IDE、静的解析ツールなどの支援を受けやすくするため 33

  34. オニオン・アーキテクチャにおける理由 • レイヤーの依存方向をのルールを保つため ◦ 疎結合を維持する • モック化しやすい、実装を差し替えやすい ◦ インターフェースが先。実装はあと ◦

    テスタビリティの確保 34
  35. インフラ層 • Repository Implementation ドメイン ユースケース プレゼンテーション(UI) インフラ テスト 35

  36. Repository Implementation の実装例 RDBを操作する実装部分。SQLAlchemy を利用(後述) 36

  37. DTOとは? 37

  38. Data Transfer Object • レイヤー間でデータを受け渡すために、オブジェクトの変換を 行うためのクラス • (例えばMVCにおける)単一のモデルですべてを 管理するのではなく、レイヤーおよび役割に応じてクラスを定義する 38

  39. DTO 実装例 - 1/3 RDBのスキーマに対応する定義を持つクラス。この場合DAOも兼ねる 39

  40. DTO 実装例 - 2/3 BookDTO を Book(Entity)に変換 40

  41. DTO 実装例 - 3/3 Book (Entity) を BookDTO に変換 41

  42. Repository Implementation の実装例 - 1/3(再掲) RDBを操作する実装部分。SQLAlchemy を利用 42

  43. Repository Implementation の実装例 - 2/3 43

  44. リポジトリパターン ドメインオブジェクト(など)の永続化処理(具体的にはDBに対するCRUD)を インターフェースと実装に分離するパターン ドメイン層とインフラ層を用意することで自然とそうなる 44

  45. リポジトリパターンへの反論 • たんに冗長なのではないか? ◦ ほとんど同じようなクラスをコピペしているだけでは? • RDBMS(など)を変更することはそうあることではないので、 抽象化するメリットが薄いのでは? 45

  46. リポジトリパターンへの反論への意見 冗長さについて → コード量が数十行増えることよりも、 レイヤーの責務を明確にすることを優先している RDBMS の変更可能性について → RDBMS の変更に備えている訳では無い。ドメインオブジェクトの隔離と、

    テスタビリティの向上が目的。結果的に RDBMS の変更に強くなる。 46
  47. 隔離されたドメインオブジェクト 47 データとその振る舞いの定義 他のレイヤーに依存しない 他のレイヤーの関心事を持ち込まない

  48. SQL Alchemy に依存した DTO 48

  49. Entity と DTO を分ける意味 • インフラ層の関心事がドメイン層に流出しない ◦ ドメインオブジェクトが RDB の都合で変更されない

    • データスキーマ駆動設計への抑止力になる ◦ データベースではなく、ユーザーと対話しよう 49
  50. ユースケース層 • Use Case ドメイン ユースケース プレゼンテーション(UI) インフラ テスト 50

  51. Use Case Interface 実装例 Use Case 層のクラスも、インターフェースと実装とを用意する。 51

  52. Use Case Implementation 実装例 ドメイン層の BookQueryService (Interface) に依存 52

  53. BookQueryService のインターフェース 53

  54. BookQueryService の実装 54

  55. Advanced Topics 編 55

  56. Advanced Topics • CQRS • UoW • 多言語から見る Python x

    DDD 56
  57. CQRS とは • Command Query Responsibility Segregation • データストアに対する 参照系(Query)と更新系(Command)を

    分離するパターン • RESTFul API とも相性がよい 57
  58. Query(参照用)モデル 58

  59. Command(更新用)モデル 1/2 59

  60. Command(更新用)モデル 1/2 60

  61. ReadModel を扱う UseCase 61

  62. CommandModel を扱う UseCase 62

  63. 突然のFastAPI Tips 63

  64. API(エンドポイント)の定義とCQRS 64 更新用モデルと参照用モデルを RESTFul API の入出力の型として利用

  65. API(エンドポイント)の定義とCQRS 65 API ドキュメントも自動生成!

  66. UoW とは • Unit of Works • トランザクションの単位を 明示的に 管理するためのパターン

    • 個人的には最難所 ◦ 難しく考えずにミドルウェアに任せていいかなと思っている... 66
  67. トランザクション管理用クラス(インターフェース) 67 • リポジトリ(インターフェース)に依存 • トランザクション管理用メソッドの インターフェースのみを実装 = RDB のライブラリに依存しない

  68. トランザクション管理用クラス(実装) 68 • RDBのセッションなど、インフラ層の関心事を持つ

  69. UoW 利用例 69 • ユースケースから利用する(単純な例)

  70. UoW 所感 70 (1リクエストの単位を小さくして、フレームワークに委ねればいいかな....)

  71. FastAPI と Dependency Injection 71 Depends を利用して依存関係を定義、オブジェクトの注入可能。

  72. 結局 Python x DDD ってどうよ? 72

  73. Python と OOP と ABC(再掲) 73

  74. 他言語からみる Python - Dart インターフェースと実装の関係を 明示する 個人的には一番好きな仕様 見た目に分かりやすい 実装の不足も検知しやすい 74

  75. 他言語からみる Python - Go インターフェースと実装の関係を明示しないが、 構造的部分型 (Structural Subtyping) により型の一致・不一致を厳格に判断 75

  76. チームで1年やってみてどうか? 76 • Python, Dart (Flutter), Go, TypeScript (Next.js) でチーム実践

    • 共通認識を持ちやすい、レビューの観点を明確にできる • フレームワークは薄く、レイヤーは厚く な技術スタックを形成できる • 型は(強めに)あったほうがよい
  77. まとめ 77

  78. 今日達成したいこと(再掲) • DDD に取り組んでみたくなる • レイヤード・アーキテクチャを実践したくなる 参加者が • 参考リポジトリ(※1)に対してのフィードバックを 獲得できる

    ※1 https://github.com/iktakahiro/dddpy 私が 78
  79. 参考文献 79 • エリック・エヴァンスのドメイン駆動設計(2011, 翔泳社) • ユースケース駆動開発(2007, 翔泳社) • Architecture

    Patterns with Python(2020, O'Reilly)
  80. これから学ぶかたへ 80 • ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 (2020, 翔泳社) • 「実践ドメイン駆動設計」から学ぶDDDの実装入門 (2007,

    翔泳社) • オブジェクト指向でなぜつくるのか 第3版(2021, 日経BP) • 現場で役立つシステム設計の原則(2017, 技術評論社) • ダイアグラム別 UML徹底活用 第2版(2011, 翔泳社)
  81. 81

  82. We’re Hiring!! • UI デザイナ(採用強化中!) • Web デザイナ • アプリ開発エンジニア(Python,

    Flutter, Go, TypeScript) • カスタマーサクセス https://hakali.co.jp/contact @iktakahiro 82
  83. ありがとうございました!! • @iktakahiro • https://github.com/iktakahiro/dddpy 83 • https://code-and-literature.connpass.com/event/228044/