Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

概要

やや古い記事だけど、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行程度の小さなライブラリですが、とても便利です。

groove-x
世界のどこにもない、心を満たすロボットを。私たちGROOVE Xは2015年に設立したスタートアップです。私たちの考えるロボットは、人々の生活に潤いを与える存在として、人が一緒にいたくなるような新世代の家庭用ロボットです。便利さや機能性ではなく、家族や伴侶として愛される存在になれるかどうか。 GROOVE Xが追求するのはあなたの感性の領域に訴えるロボットです。
https://groove-x.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away