Slide 1

Slide 1 text

Python でDDD をやってみた感想 1 Python でDDD をやってみた感想 バックエンドアーキテクチャ api ├─config ├─api │ ├─urls.py │ └─v1 │ ├─urls.py │ └─views │ └─post_view.py ├─post (Domain 毎にアプリケーションを作成) │ ├─__init__.py │ ├─admin.py │ ├─apps.py │ ├─commands.py │ ├─domain_services.py( 必要な場合に実装) │ ├─entities.py │ ├─exceptions.py │ ├─models.py │ ├─queries.py │ ├─repositories.py │ ├─schemas.py │ ├─tests.py │ └─use_cases.py ├─recommend │ ├─__init__.py │ ├─admin.py ・・・ (備考)Django でアプリケーションを作成する例 Django は疎結合・⾼凝集の設計思想で作成されています。 1 つのプロジェクトの中で複数のアプリケーションを作ることができ、また1 つのアプ リケーションを複数のプロジェクトを共有することができます。 ひと

Slide 2

Slide 2 text

Python でDDD をやってみた感想 2 python manage.py startapp post オニオンアーキテクチャ UserInterface/Presentation エンドポイントを提供する層。⼀般的なコントローラークラス。 views.py ApplicationService/DomainService/DomainModel の呼び出し・組み⽴てを ⾏い、レスポンスを返す ⼊⼒値のバリデーションチェックを⾏う バリエーションチェックはmarshmallow で⾏う class PostAPIView(APIView): use_case = PostUseCase def post(self, request): try: schema = PostWriteSchema(request.data) post_entity = use_case.write(schema)

Slide 3

Slide 3 text

Python でDDD をやってみた感想 3 return JsonResponse(post_entity.to_json()) except ValidationError as e: return JsonResponse({'error': f'{e.message}'}, status=404) except PostDoesNotExist: return JsonResponse({'error': f'Cannot find a post with this id({request_schem a.id}).'}, status=404) ApplicationService アプリケーション向けのデータ加⼯やDomainService/DomainModel の呼び出し を⾏う データの取得においてはquery を呼び出す user_cases.py(CQRS のCommand の部分) ドメインロジックの組み⽴てを⾏う。 class PostUseCase: # DI コンテナの注⼊ _repo = inject.attr(PostRepositoryImpl) @classmethod def write(cls, schema:) -> PostEntity: entity = PostEntity.from_schema(schema) self._repo.save(entity) @classmethod def update(cls, schema:) -> PostEntity: entity = PostEntity.from_schema(schema) self._repo.save(entity) queries.py (CQRS のQuery 部分) データを取得するだけのクラス(CQRS のQuery 部分) class PostQuery: @classmethod def find_by_id(cls, post_id: int) -> PostEntity: entity = self._repo.find_by_id(self, post_id) if entity is None: raise PostNotFoundError return entity @classmethod def get_all(cls, post_id: int) -> PostEntity:

Slide 4

Slide 4 text

Python でDDD をやってみた感想 4 entities = self._repo.get_all(self, post_id) return entities DomainService (複雑な場合に使⽤。現状は使⽤していない) ドメインロジックの組み⽴てを⾏う。 ApplicationService やDomainModel でも実装が難しい複雑なケースで使⽤する Infrastructure DB や外部サービス(File Access, Access, ORM, etc...) にアクセスして永続化を 担当する層。 repositories.py インフラ層 ドメインオブジェクトの永続化層の実装を伴うクラス Entity クラスを返す class PostRepository: @classmethod def save(cls, post: PostEntity) -> Optional[Post]: post_model = Post.objects.update_or_create( id=post.id, defalults={ "title": post.title, "content": post.content } ) return PostEntity.from_model(post_model) @classmethod def find_by_id(cls, post_id: int) -> Optional[PostDto]: post_model = Post.objects.get(id=post_id) return PostEntity.from_model(post_model) @classmethod def get_all(cls, post_id: int) -> Optional[PostDto]: post_entities = [] for post_model in Post.objects.all: entity = PostEntity.from_model(post_model)

Slide 5

Slide 5 text

Python でDDD をやってみた感想 5 post_entities.append(entity) return post_entities Tests テストコードモジュール。 UI の変更に伴い、テスト項⽬も変動する不安定な層。 実際にやったこと 既存アプリケーションのリプレイス 社内専⽤アプリはこんな感じ 在庫管理 CRM ヤフオク操作 ⾃動出品とか落札情報⾃動取得とか 既存の構成、問題点 現在はDjango のMTV (MVC )で作成 アーキテクチャもコーディング規約も存在せず、クラスの責任がバラバラ 書いた本⼈しかわからないコードだらけ え、こんなところでクエリ実⾏されてるの?ってのがザラにある ドメインモデリングも試してみた やってみて 良かったこと ⼩さく試す コード規約っぽいのが初めてできて、開発者同⼠で認識が揃った

Slide 6

Slide 6 text

Python でDDD をやってみた感想 6 最初は疑問に思われていたため、まず⼩さい機能でペアプロしたりコード レビューをしてもらったりして理解を深めてもらった その後実際に少し実装してもらうことで良さを実感してもらうことができ た バリューオブジェクトや集約はあえて最初は導⼊しなかった 簡単なところから浸透させ、徐々にレベルをあげていく⽅針 Django が思ったよりもDDD やりやすい設計思想になっていて⾯⽩かった このライブラリとかまさにそう payment アプリだけオーバーライドして実装、みたいなのが可能 https://github.com/django-oscar/django-oscar ドメインエキスパートにかなり喜ばれた SUDO 図は⼤感謝祭り 今までシステムの中⾝や会話が出来ず、何をしているかがわかってい なかった ⼀緒に議論したり、図で可視化することで共通認識が取れるように システムの動きが分かりやすくなったり、開発者と議論しやすくなり そうとのこと アーキテクチャの理解⼒が上がった 他社の事例、PyCon などの動画を⾒てDDD を徹底的にアーキテクチャの理 解が深まった 今後のやりたいこと バリューオブジェクトや集約など、DDD のテクニックを実践してみたい PyCon などで登壇したい Django の便利ライブラリ

Slide 7

Slide 7 text

Python でDDD をやってみた感想 7 django restframework api サーバーを⽴てるのに必要 dataclass デコレーター Entity を定義するのに便利、上書き禁⽌に出来る