6
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

kota matsuokaの1人アドベントカレンダー ~Pythonで0からサービスを開発~ Advent Calendar 2018

Day 11

PythonでボトムアップDDD 【ドメインサービス】

Last updated at Posted at 2018-12-11

ボトムアップドメイン駆動設計ドメインサービスについて、Pythonで書いてみた。

C#で書かれているコードをPythonに翻訳したので、厳密に再現できていない箇所もあります。また、それぞれの呼び出しをテストコードで表し、ソースコードレベルで目的を明確にしました。

用語の説明やコードの詳細説明に関して、下記の参考文献を参照ください。

参考文献: ボトムアップドメイン駆動設計

筆者が訳した「pythonでボトムアップDDD」
PythonでボトムアップDDD 【値オブジェクト】
PythonでボトムアップDDD 【エンティティ】
PythonでボトムアップDDD 【ドメインサービス】
PythonでボトムアップDDD【値オブジェクト・エンティティ・ドメインサービスを利用する】
PythonでボトムアップDDD 【リポジトリ】
PythonでボトムアップDDD 【テスト用のリポジトリ】
PythonでボトムアップDDD 【ユーザの登録・変更・削除・取得など】

バージョン

  • Python 3.7.0

目次

  • ドメインサービスとは
  • エンティティ自身に横断的な知識を実装
  • ドメインサービスに横断的な知識を実装
  • 使う場面によっては別の案があるかも

ドメインサービスとは

ドメインサービスは、値オブジェクトとエンティティの横断的な知識を実装することが許されている。

エンティティ自身に横断的な知識を実装

エンティティに横断的な知識を持たせるように実装してみる。
ユーザ自身に他のユーザとの重複を判断させるのは、確かに違和感がある。

from __future__ import annotations
import unittest
import uuid
from dataclasses import dataclass

@dataclass(frozen=True)
class UserId:
    value: str

@dataclass(frozen=True)
class Username:
    username: str

@dataclass
class User:
    user_id: UserId
    username: Username

    def is_duplicated(self, user: User) -> bool:
        # 自分以外のユーザを引数に渡せる
        return user.username in USER_LIST

# DB代わりのリスト
USER_LIST = [Username("松岡"), Username("松田"), Username("松井")]

class TestUser(unittest.TestCase):
    def test_ユーザ自身が渡されたユーザのユーザ名の重複判断ができる(self):
        matsuoka = User(UserId(str(uuid.uuid4())), Username("松岡"))

        self.assertTrue(matsuoka.is_duplicated(matsuoka))

if __name__ == "__main__":
    unittest.main()

ドメインサービスに横断的な知識を実装

from __future__ import annotations
import unittest
import uuid
from dataclasses import dataclass


@dataclass(frozen=True)
class UserId:
    value: str


@dataclass(frozen=True)
class Username:
    username: str


@dataclass
class User:
    user_id: UserId
    username: Username


class UserService:
    def is_duplicated(self, user: User) -> bool:
        # 自らのユーザのみ重複判断できる
        return user.username in USER_LIST


# DB代わりのリスト
USER_LIST = [Username("松岡"), Username("松田"), Username("松井")]


class TestUserService(unittest.TestCase):
    def test_ユーザーが重複しているかをドメインサービスに任せる(self):
        matsuoka = User(UserId(str(uuid.uuid4())), Username("松岡"))

        self.assertTrue(UserService().is_duplicated(matsuoka))


if __name__ == "__main__":
    unittest.main()

このようにエンティティに関するロジックだが、エンティティ自身に持たせることが不自然なロジックはドメインサービスに実装する。

使う場面によっては別の案があるかも

自分自身のusernameが他のユーザのusernameと重複しているかを確かめるだけならば、下のようにUserエンティティにis_deplicated()を実装しても良いと思おう。

上の**「エンティティ自身に横断的な知識を実装」**との違いは、自分自身のusernameしか、重複しているかの判断ができないことだ。

from __future__ import annotations
import unittest
import uuid
from dataclasses import dataclass


@dataclass(frozen=True)
class UserId:
    value: str


@dataclass(frozen=True)
class Username:
    username: str


@dataclass
class User:
    user_id: UserId
    username: Username

    def is_duplicated(self) -> bool:
        # 自らのユーザのみ重複判断できる
        return self.username in USER_LIST


# DB代わりのリスト
USER_LIST = [Username("松岡"), Username("松田"), Username("松井")]


class TestDomainService(unittest.TestCase):
    def test_ユーザが自らのユーザ名の重複判断ができる(self):
        matsuoka = User(UserId(str(uuid.uuid4())), Username("松岡"))

        self.assertTrue(matsuoka.is_duplicated())


if __name__ == "__main__":
    unittest.main()

レビューお待ちしてます。

参考文献

6
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?