Hexagonal Architecture の実践には zope.interface が便利

### 追記: 2017-02-12

実は、自分のプロジェクトでは最近、この方法を利用していません。inject による紐付けが出来ていて、必要なメソッドが定義されていれば（duck typing）、必ずしもドメイン層のモデルとインフラ層の実装が継承関係になくても良い、というゆるい考えです。

- http://qiita.com/ledmonster/items/3b108be6f0967bfe1093

また、最近の自分のプロジェクトでは、インフラ層を置く代わりに、port, adapter のモジュールを定義しています。adapter には、ドメイン層または port で定義されたインタフェースの実装を書く感じです。ドメインモデルにある概念はドメイン層に置き、外部シストムとの連携インタフェースは port に置く感じです。

時間のあるときにまた記事を書こうと思います。


### 概要

ドメイン駆動開発に[Hexagonal Architecture](http://fideloper.com/hexagonal-architecture)を適用した場合、リポジトリやドメインイベントのインタフェースをドメイン層におきますが、[zope.interface](http://docs.zope.org/zope.interface/)を活用することで、多重継承を回避した分かりやすいコードを書くことができます。



### ドメイン層

    from abc import ABCMeta
    from zope.interface import Interface
    
    
    class Entity(object):
        """ Base class for Entity """
        __metaclass__ = ABCMeta
    
    
    class IRepository(Interface):
        """ Interafce for Repository """
    
    
    class IDomainService(Interface):
        """ Interface for Domain Service """
    
    
    class Blog(Entity):
        def __init__(self, identity, title):
            self._identity = identity
            self._title = title
    
    
    class IBlogRepository(IRepository):
        """ Blog repository Interface """
    
        def add_entity(blog):
            """ add blog """
    
        def get_by(identity):
            """ get blog by identity """
    
    
    class IBlogProvisioningService(IDomainService):
        """ Blog provisioning service interface """
    
        def provision_blog(title):
            """ provision blog with title. returns Blog object """

### インフラ層
    import uuid
    from abc import ABCMeta
    from zope.interface import implementer
    from domain import Blog, IBlogRepository, IBlogProvisioningService
    
    
    class MySQLRepository(object):
        """ MySQL based repository """
        __metaclass__ = ABCMeta
    
    
    class MemoryRepository(object):
        """ memory based repository """
        __metaclass__ = ABCMeta
    
    
    @implementer(IBlogRepository)
    class BlogRepository(MySQLRepository):
    
        def add_entity(self, blog):
            # do some stuff
            pass
    
        def get_by(self, identity):
            # do some stuff
            return Blog(identity, "some stored title")
    
    
    @implementer(IBlogRepository)
    class BlogMemoryRepository(MemoryRepository):
    
        def add_entity(self, blog):
            # do some stuff
            pass
    
        def get_by(self, identity):
            # do some stuff
            return Blog(identity, "some stored title")
    
    
    @implementer(IBlogProvisioningService)
    class BlogProvisioningService(object):
    
        def __init__(self, repo):
            self._repo = repo
    
        def provision_blog(self, title):
            entity = Blog(uuid.uuid4().hex, title)
            self._repo.add_entity(entity)

### テスト
    import pytest
    from zope.interface.verify import verifyClass
    from domain import Blog, IBlogRepository, IBlogProvisioningService
    from infra import BlogRepository, BlogMemoryRepository, BlogProvisioningService
    
    
    def test_class_interface():
        assert verifyClass(IBlogRepository, BlogRepository)
        assert verifyClass(IBlogRepository, BlogMemoryRepository)
        assert verifyClass(IBlogProvisioningService, BlogProvisioningService)

### ドメイン駆動開発シリーズ

* [DDDで設計するならCQRSの利用を検討すべき](http://qiita.com/ledmonster/items/22b00c65208dffeff7e4)
* [ValueObject implementation in Python](http://qiita.com/ledmonster/items/2f16d52cdf58d1a73685)
* [Hexagonal Architecture の実践には zope.interface が便利](http://qiita.com/ledmonster/items/9de40b30040326a7dba8)