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/