LoginSignup
72
53

More than 5 years have passed since last update.

Python で DDD するなら Inject がオススメ

Posted at

概要

やや古い記事だけど、InfoQ のドメイン駆動設計・開発の実践には次のように書かれています。

ドメイン・クラスがData Access Object(DAO:データ・アクセス・オブジェクト)クラスに依存し、サービス・クラスがドメイン・クラスに依存するという設計上の依存関係がDDDによる実装に際してDIを"なくてはならない"ものにしています。

Python の場合、DI(依存性の注入)の実現には Inject が便利です。

利用例

ざっと100行程度でサンプルを書くと、このような形になります。

# -*- coding: utf-8 -*-
import uuid
from abc import ABCMeta, abstractmethod

import inject


def config(binder):
    binder.bind(UserRepository, UserMemoryRepository())


class User(object):

    def __init__(self, identity, name):
        self.identity = identity
        self.name = name


class UserRepository(object):
    u""" Base class of User Repository"""
    __metaclass__ = ABCMeta

    @abstractmethod
    def store(self, user):
        raise NotImplementedError

    @abstractmethod
    def find_by_identity(self, identity):
        raise NotImplementedError


class UserMemoryRepository(UserRepository):
    u""" User Repository on memory"""

    def __init__(self):
        self._users = {}

    def store(self, user):
        if not isinstance(user, User):
            raise TypeError
        self._users[user.identity] = user

    def find_by_identity(self, identity):
        return self._users[identity]


class UserRedisRepository(UserRepository):
    u""" User Repository on Redis """

    def store(self, user):
        # TODO: write code here
        pass

    def find_by_identity(self, identity):
        # TODO: write code here
        pass


class UserService(object):
    u""" User Service on Application Layer"""
    repo = inject.attr(UserRepository)

    def create_user(self, name):
        user = User(uuid.uuid4(), name)
        self.repo.store(user)
        return user

    def find_by_identity(self, identity):
        return self.repo.find_by_identity(identity)


if __name__ == "__main__":
    # Call once on application start
    inject.configure(config)

    user_service = UserService()
    created_user = user_service.create_user('foo')
    stored_user = user_service.find_by_identity(created_user.identity)

    assert created_user == stored_user

inject を使わずに同様のことを実現するためには、UserService の引数に repo を渡す必要があり、依存関係が増えるたびに UserService のコンストラクタが肥大化してしまいますが、inject を利用するとスッキリと書けます。@inject.params デコレータを利用すれば、コンストラクタのデフォルト引数に依存性を注入することもできます。

わずか300行程度の小さなライブラリですが、とても便利です。

72
53
0

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
72
53