Python で学ぶ実践的なドメイン駆動設計とレイヤードアーキテクチャ / DDD and Onion Architecture in Python
by
Takahiro Ikeuchi
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
© 2021 iktakahiro Python x DDD!!! Python で学ぶ実践的なドメイン駆動設計と レイヤード・アーキテクチャ 池内 孝啓 - Takahiro Ikeuchi PyCon JP 2021.10.14
Slide 2
Slide 2 text
Takahiro Ikeuchi - 池内 孝啓 株式会社Hakali 取締役CTO ● @iktakahiro ● https://github.com/iktakahiro おしごと : メンタルウェルネス・アプリ Awarefy の事業 & 開発責任者 Python, Dart(Flutter), Go, TypeScript 2
Slide 3
Slide 3 text
3 執筆書籍紹介
Slide 4
Slide 4 text
Agenda ● 自己紹介 & Agenda - 2分 ← イマココ ● コードアーキテクチャの前に - 2分 ● コードアーキテクチャ編 - 20分 ○ ドメイン層 / インフラ層 / ユースーケース層 ● Advanced Topics 編 5分 ○ CRQS / UoW / FastAPI ● まとめ - 1分 4
Slide 5
Slide 5 text
本日のベースとなるリポジトリ https://github.com/iktakahiro/dddpy 5 みなさまの力でぜひ ☆100 越えたい!!
Slide 6
Slide 6 text
用語の整理 ● DDD ○ ドメイン駆動設計。このセッションでは、某書籍 (エリック・エヴァン スのドメイン駆動設計) を前提に解説を進めます ● レイヤード・アーテキクチャ ○ このセッションでは、実質的に オニオン・アーキテクチャ を指す ● ドメイン ○ 事業やサービスが取り扱う世界のこと(用例: ドメイン知識 ● ドメインオブジェクト ○ ドメイン知識やビジネスロジックを持つクラス 6
Slide 7
Slide 7 text
Q1. DDD に取り組んでいますか? 7
Slide 8
Slide 8 text
今日達成したいこと ● DDD に取り組んでみたくなる ● ソフトウェア・アーキテクチャの設計や実践をしたくなる 参加者が ● 参考リポジトリ(※1)に対してのフィードバックを 獲得できる ※1 https://github.com/iktakahiro/dddpy 私が 8
Slide 9
Slide 9 text
コードアーキテクチャの前に 9
Slide 10
Slide 10 text
DDD に取り組む意義(個人の見解) ● チームが共通言語を獲得すること (境界付けられたコンテクスト内でのユビキタス言語) ● ドメインオブジェクトを独立させ、 ドメインが解決しようとしている 現実の課題に関心事を向けること ○○・アーキテクチャ = DDD ではない 10
Slide 11
Slide 11 text
DDD から得られるもの コラボレーション促進 明解な命名 クリーンな設計 ドメイン理解の深化 11
Slide 12
Slide 12 text
ユビキタス言語 実例 12
Slide 13
Slide 13 text
DDD から得た学び ● DDD の前に OOP !! ● ユースケースを中心に考える ● 設計(書)をおそろかにしない 13
Slide 14
Slide 14 text
急いでいるからこそ、設計が大事 14 ● 急がば回れ・手戻り減 ● 設計 = インターフェースを先に決める ○ 実装はあとでよい ● ユースケースに注目する ● 振る舞いに注目する
Slide 15
Slide 15 text
モデリングの話しも尽きませんが Python の話をします 15
Slide 16
Slide 16 text
コードアーキテクチャ編 16
Slide 17
Slide 17 text
もうちょっと抽象的な話 17
Slide 18
Slide 18 text
DDDの実践とレイヤード・アーキテクチャ DDD = ○○・アーキテクチャではないと言いつつも... ● ドメイン層をクリーンに保つ ● ユースケース層をはじめ、各レイヤーのテスタビリティを確保する 18 複雑性と不確実性に立ち向かうためにたいへん有用
Slide 19
Slide 19 text
オニオン・アーキテクチャのすゝめ ● 各レイヤの責務が明確 ○ 特定の部位が fat になりにくい(なるけど) ● 比較的学習コストが低く、チームで共有しやすい ● 某書籍を読んで挫折した人でも大丈夫 19
Slide 20
Slide 20 text
オニオン・アーキテクチャの概略図 【ルールは単純】 外側の人は内側の人を知ってる 内側の人は外側の人を知らない 依存性逆転の原則を用いてインフラを 最も外側に置いている ドメイン ユースケース プレゼンテーション(UI) インフラ テスト https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/ 20
Slide 21
Slide 21 text
インターフェースの重要性 ドメイン層 インフラ層 ユースケース層 ユースケース層は、インフラ層に依存しない ドメイン層(のインターフェース)を利用する ドメイン層のクラスを利用してユースケースを 実現する処理を実装 ドメインオブジェクトの永続化処理を実装 ドメインオブジェクトの実装や、 オブジェクト永続化処理のインターフェースを定義 21
Slide 22
Slide 22 text
ディレクトリ構成例 ※ ごく普通の構成です UseCase層 を application 層とする例も 22
Slide 23
Slide 23 text
ディレクトリ構成例 詳細 23
Slide 24
Slide 24 text
ドメイン層 ● Entity ● Value Object ● Repository Interface ドメイン ユースケース プレゼンテーション(UI) インフラ テスト 24
Slide 25
Slide 25 text
ドメイン層の構成要素 ● Entity ● Value Object ● Domain Service ○ Repository Interface ● First Class Collection など 25
Slide 26
Slide 26 text
Entity id(など)によって同一性を決定する ライフサイクル(= 各プロパティの変更)を持つ = id は不変。その他のプロパティはミュータブル 26
Slide 27
Slide 27 text
Entity の実装例 インスタンスの同一性を id プロパティのみで決定 27
Slide 28
Slide 28 text
Value Object(VO) Entity としてのライフサイクルを持たないオブジェクト = イミュータブル すべての値(プロパティ)で同一性を決定する プリミティブ型の代替として VO を定義することもある 28
Slide 29
Slide 29 text
Value Object の実装例 VO としての性質の多くは @dataclass デコレータを利用することで確保できる 29
Slide 30
Slide 30 text
Repository ドメインオブジェクト(など)の永続化処理を担うクラス ● インターフェースをドメイン層のクラスとして ● 実装をインフラ層のクラスとして定義 ● 利用はユースケース層から! 30
Slide 31
Slide 31 text
Repository Interface の実装例 ABCクラスを継承 @abstractmethod デコレータを利用 31
Slide 32
Slide 32 text
ダック・タイピングが可能なのに 何故インターフェースを定義するのか? 32
Slide 33
Slide 33 text
Python でインターフェースを定義する意義 ● 他の言語のコードとアーキテクチャを揃えるため ● 型を明示するため ○ Type hint、IDE、静的解析ツールなどの支援を受けやすくするため 33
Slide 34
Slide 34 text
オニオン・アーキテクチャにおける理由 ● レイヤーの依存方向をのルールを保つため ○ 疎結合を維持する ● モック化しやすい、実装を差し替えやすい ○ インターフェースが先。実装はあと ○ テスタビリティの確保 34
Slide 35
Slide 35 text
インフラ層 ● Repository Implementation ドメイン ユースケース プレゼンテーション(UI) インフラ テスト 35
Slide 36
Slide 36 text
Repository Implementation の実装例 RDBを操作する実装部分。SQLAlchemy を利用(後述) 36
Slide 37
Slide 37 text
DTOとは? 37
Slide 38
Slide 38 text
Data Transfer Object ● レイヤー間でデータを受け渡すために、オブジェクトの変換を 行うためのクラス ● (例えばMVCにおける)単一のモデルですべてを 管理するのではなく、レイヤーおよび役割に応じてクラスを定義する 38
Slide 39
Slide 39 text
DTO 実装例 - 1/3 RDBのスキーマに対応する定義を持つクラス。この場合DAOも兼ねる 39
Slide 40
Slide 40 text
DTO 実装例 - 2/3 BookDTO を Book(Entity)に変換 40
Slide 41
Slide 41 text
DTO 実装例 - 3/3 Book (Entity) を BookDTO に変換 41
Slide 42
Slide 42 text
Repository Implementation の実装例 - 1/3(再掲) RDBを操作する実装部分。SQLAlchemy を利用 42
Slide 43
Slide 43 text
Repository Implementation の実装例 - 2/3 43
Slide 44
Slide 44 text
リポジトリパターン ドメインオブジェクト(など)の永続化処理(具体的にはDBに対するCRUD)を インターフェースと実装に分離するパターン ドメイン層とインフラ層を用意することで自然とそうなる 44
Slide 45
Slide 45 text
リポジトリパターンへの反論 ● たんに冗長なのではないか? ○ ほとんど同じようなクラスをコピペしているだけでは? ● RDBMS(など)を変更することはそうあることではないので、 抽象化するメリットが薄いのでは? 45
Slide 46
Slide 46 text
リポジトリパターンへの反論への意見 冗長さについて → コード量が数十行増えることよりも、 レイヤーの責務を明確にすることを優先している RDBMS の変更可能性について → RDBMS の変更に備えている訳では無い。ドメインオブジェクトの隔離と、 テスタビリティの向上が目的。結果的に RDBMS の変更に強くなる。 46
Slide 47
Slide 47 text
隔離されたドメインオブジェクト 47 データとその振る舞いの定義 他のレイヤーに依存しない 他のレイヤーの関心事を持ち込まない
Slide 48
Slide 48 text
SQL Alchemy に依存した DTO 48
Slide 49
Slide 49 text
Entity と DTO を分ける意味 ● インフラ層の関心事がドメイン層に流出しない ○ ドメインオブジェクトが RDB の都合で変更されない ● データスキーマ駆動設計への抑止力になる ○ データベースではなく、ユーザーと対話しよう 49
Slide 50
Slide 50 text
ユースケース層 ● Use Case ドメイン ユースケース プレゼンテーション(UI) インフラ テスト 50
Slide 51
Slide 51 text
Use Case Interface 実装例 Use Case 層のクラスも、インターフェースと実装とを用意する。 51
Slide 52
Slide 52 text
Use Case Implementation 実装例 ドメイン層の BookQueryService (Interface) に依存 52
Slide 53
Slide 53 text
BookQueryService のインターフェース 53
Slide 54
Slide 54 text
BookQueryService の実装 54
Slide 55
Slide 55 text
Advanced Topics 編 55
Slide 56
Slide 56 text
Advanced Topics ● CQRS ● UoW ● 多言語から見る Python x DDD 56
Slide 57
Slide 57 text
CQRS とは ● Command Query Responsibility Segregation ● データストアに対する 参照系(Query)と更新系(Command)を 分離するパターン ● RESTFul API とも相性がよい 57
Slide 58
Slide 58 text
Query(参照用)モデル 58
Slide 59
Slide 59 text
Command(更新用)モデル 1/2 59
Slide 60
Slide 60 text
Command(更新用)モデル 1/2 60
Slide 61
Slide 61 text
ReadModel を扱う UseCase 61
Slide 62
Slide 62 text
CommandModel を扱う UseCase 62
Slide 63
Slide 63 text
突然のFastAPI Tips 63
Slide 64
Slide 64 text
API(エンドポイント)の定義とCQRS 64 更新用モデルと参照用モデルを RESTFul API の入出力の型として利用
Slide 65
Slide 65 text
API(エンドポイント)の定義とCQRS 65 API ドキュメントも自動生成!
Slide 66
Slide 66 text
UoW とは ● Unit of Works ● トランザクションの単位を 明示的に 管理するためのパターン ● 個人的には最難所 ○ 難しく考えずにミドルウェアに任せていいかなと思っている... 66
Slide 67
Slide 67 text
トランザクション管理用クラス(インターフェース) 67 ● リポジトリ(インターフェース)に依存 ● トランザクション管理用メソッドの インターフェースのみを実装 = RDB のライブラリに依存しない
Slide 68
Slide 68 text
トランザクション管理用クラス(実装) 68 ● RDBのセッションなど、インフラ層の関心事を持つ
Slide 69
Slide 69 text
UoW 利用例 69 ● ユースケースから利用する(単純な例)
Slide 70
Slide 70 text
UoW 所感 70 (1リクエストの単位を小さくして、フレームワークに委ねればいいかな....)
Slide 71
Slide 71 text
FastAPI と Dependency Injection 71 Depends を利用して依存関係を定義、オブジェクトの注入可能。
Slide 72
Slide 72 text
結局 Python x DDD ってどうよ? 72
Slide 73
Slide 73 text
Python と OOP と ABC(再掲) 73
Slide 74
Slide 74 text
他言語からみる Python - Dart インターフェースと実装の関係を 明示する 個人的には一番好きな仕様 見た目に分かりやすい 実装の不足も検知しやすい 74
Slide 75
Slide 75 text
他言語からみる Python - Go インターフェースと実装の関係を明示しないが、 構造的部分型 (Structural Subtyping) により型の一致・不一致を厳格に判断 75
Slide 76
Slide 76 text
チームで1年やってみてどうか? 76 ● Python, Dart (Flutter), Go, TypeScript (Next.js) でチーム実践 ● 共通認識を持ちやすい、レビューの観点を明確にできる ● フレームワークは薄く、レイヤーは厚く な技術スタックを形成できる ● 型は(強めに)あったほうがよい
Slide 77
Slide 77 text
まとめ 77
Slide 78
Slide 78 text
今日達成したいこと(再掲) ● DDD に取り組んでみたくなる ● レイヤード・アーキテクチャを実践したくなる 参加者が ● 参考リポジトリ(※1)に対してのフィードバックを 獲得できる ※1 https://github.com/iktakahiro/dddpy 私が 78
Slide 79
Slide 79 text
参考文献 79 ● エリック・エヴァンスのドメイン駆動設計(2011, 翔泳社) ● ユースケース駆動開発(2007, 翔泳社) ● Architecture Patterns with Python(2020, O'Reilly)
Slide 80
Slide 80 text
これから学ぶかたへ 80 ● ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 (2020, 翔泳社) ● 「実践ドメイン駆動設計」から学ぶDDDの実装入門 (2007, 翔泳社) ● オブジェクト指向でなぜつくるのか 第3版(2021, 日経BP) ● 現場で役立つシステム設計の原則(2017, 技術評論社) ● ダイアグラム別 UML徹底活用 第2版(2011, 翔泳社)
Slide 81
Slide 81 text
81
Slide 82
Slide 82 text
We’re Hiring!! ● UI デザイナ(採用強化中!) ● Web デザイナ ● アプリ開発エンジニア(Python, Flutter, Go, TypeScript) ● カスタマーサクセス https://hakali.co.jp/contact @iktakahiro 82
Slide 83
Slide 83 text
ありがとうございました!! ● @iktakahiro ● https://github.com/iktakahiro/dddpy 83 ● https://code-and-literature.connpass.com/event/228044/