Edited at

PythonでボトムアップDDD 【エンティティ】

ボトムアップドメイン駆動設計エンティティについて、Pythonで書きました。

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

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

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

筆者が訳した「pythonでボトムアップDDD」

PythonでボトムアップDDD 【値オブジェクト】

PythonでボトムアップDDD 【エンティティ】

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

PythonでボトムアップDDD【値オブジェクト・エンティティ・ドメインサービスを利用する】

PythonでボトムアップDDD 【リポジトリ】

PythonでボトムアップDDD 【テスト用のリポジトリ】

PythonでボトムアップDDD 【ユーザの登録・変更・削除・取得など】


バージョン


  • Python 3.7.0


目次


エンティティとは

詳しいエンティティの説明は、参考文献を参照してください。

ここでは、エンティティの3つの特徴だけおさえておきます。


  • 可変である

  • 同じ属性でも区別される

  • 同一性を持つ


可変である

import dataclasses

import unittest

@dataclasses.dataclass(frozen=True)
class FullName:
full_name: str

@dataclasses.dataclass
class User:
def __init__(self, full_name: FullName):
self.full_name = full_name

def change_full_name(self, new_name: FullName) -> None:
if new_name is None:
raise ValueError(f"{new_name} is invalid argument")

self.full_name = new_name

class TestUserEntity1(unittest.TestCase):
def test_属性を変更できる(self):
user = User(FullName("matsuoka kota"))
user.change_full_name(FullName("tanaka satoshi"))

self.assertEqual(FullName("tanaka satoshi"), user.full_name)

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


同じ属性でも区別される

import dataclasses

import unittest
import uuid

@dataclasses.dataclass(frozen=True)
class FullName:
full_name: str

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

@dataclasses.dataclass
class User:
full_name: FullName
id: UserId

class TestUserEntity2(unittest.TestCase):
def test_同じ属性でも区別される(self):
tom1 = User(FullName("Tom Cruise"), UserId(str(uuid.uuid4())))
tom2 = User(FullName("Tom Cruise"), UserId(str(uuid.uuid4())))

self.assertNotEqual(tom1, tom2)

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


同一性を持つ

from __future__ import annotations

import dataclasses
import uuid
import unittest

@dataclasses.dataclass(frozen=True)
class FullName:
full_name: str

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

@dataclasses.dataclass
class User:
full_name: FullName
id: UserId

def change_full_name(self, new_name: FullName) -> None:
if new_name is None:
raise ValueError(f"{new_name} is invalid argument")

self.full_name = new_name

def __eq__(self, other: User):
# idのみで比較する
return isinstance(other, User) and (self.id == other.id)

class TestUserEntity3(unittest.TestCase):
def test_オブジェクトの属性が変化しても同一性が保証される(self):
tom = User(FullName("Tom Cruise"), UserId(str(uuid.uuid4())))

before_tom = tom

tom.change_full_name(FullName("Tom Hanks"))

self.assertEqual(before_tom, tom)

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


参考文献


おまけ

参考文献に載せたブログ記事にはいつも助けられています。ぜひ、みなさんも読みながら、ご自身が使い慣れている言語で写経してみてください。

Renttleというサービスを開発中です。ぜひ、使ってみて、レビューをください。