Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update decoupled-example with abstraction layer #770

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/examples/decoupled-packages.rst
Original file line number Diff line number Diff line change
@@ -55,6 +55,17 @@ file.

./
├── example/
│ ├── abstraction
│ │ ├── __init__.py
│ │ ├── analytics
│ │ │ ├── __init__.py
│ │ │ └── services.py
│ │ ├── photo
│ │ │ ├── __init__.py
│ │ │ └── repositories.py
│ │ └── user
│ │ ├── __init__.py
│ │ └── repositories.py
Comment on lines +58 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For such simple app example, I'd move everything into interfaces.py to keep app structure from bloating.

│ ├── analytics/
│ │ ├── __init__.py
│ │ ├── containers.py
@@ -75,6 +86,26 @@ file.
├── config.ini
└── requirements.txt


Abstraction
------------------

Listing of the ``example/abstraction/user/repositories.py``:

.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/abstraction/user/repositories.py
:language: python

Listing of the ``example/abstraction/photo/repositories.py``:

.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/abstraction/photo/repositories.py
:language: python

Listing of the ``example/abstraction/analytics/services.py``:

.. literalinclude:: ../../examples/miniapps/decoupled-packages/example/abstraction/analytics/services.py
:language: python


Package containers
------------------

15 changes: 9 additions & 6 deletions examples/miniapps/decoupled-packages/example/__main__.py
Original file line number Diff line number Diff line change
@@ -2,21 +2,22 @@

from dependency_injector.wiring import Provide, inject

from .user.repositories import UserRepository
from .photo.repositories import PhotoRepository
from .analytics.services import AggregationService
from .abstraction.user.repositories import UserRepositoryMeta
from .abstraction.photo.repositories import PhotoRepositoryMeta
from .abstraction.analytics.services import AggregationServiceMeta

from .containers import ApplicationContainer


@inject
def main(
user_repository: UserRepository = Provide[
user_repository: UserRepositoryMeta = Provide[
ApplicationContainer.user_package.user_repository
],
photo_repository: PhotoRepository = Provide[
photo_repository: PhotoRepositoryMeta = Provide[
ApplicationContainer.photo_package.photo_repository
],
aggregation_service: AggregationService = Provide[
aggregation_service: AggregationServiceMeta = Provide[
ApplicationContainer.analytics_package.aggregation_service
],
) -> None:
@@ -32,6 +33,8 @@ def main(
assert aggregation_service.photo_repository is photo_repository
print("Aggregate analytics from user and photo packages")

aggregation_service.call_user_photo()


if __name__ == "__main__":
application = ApplicationContainer()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""abstraction package."""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""abstraction analytics package."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Analytics services module."""

import abc


from ..photo.repositories import PhotoRepositoryMeta
from ..user.repositories import UserRepositoryMeta


class AggregationServiceMeta(metaclass=abc.ABCMeta):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class AggregationServiceMeta(metaclass=abc.ABCMeta):
class AggregationService(metaclass=abc.ABCMeta):

Meta suffix might give it a wrong impression that this is a metaclass. Let's avoid ambiguous naming.


def __init__(self, user_repository: UserRepositoryMeta, photo_repository: PhotoRepositoryMeta):
self.user_repository: UserRepositoryMeta = user_repository
self.photo_repository: PhotoRepositoryMeta = photo_repository
Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interfaces generally has no __init__, it's an implementation detail.

Suggested change
def __init__(self, user_repository: UserRepositoryMeta, photo_repository: PhotoRepositoryMeta):
self.user_repository: UserRepositoryMeta = user_repository
self.photo_repository: PhotoRepositoryMeta = photo_repository


@abc.abstractmethod
def call_user_photo(self):
"""Must be implemented in order to instantiate."""
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""abstraction photo package."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Photo repositories Meta module."""

import abc


class PhotoRepositoryMeta(metaclass=abc.ABCMeta):

@abc.abstractmethod
def get_photos(self, user_id):
"""Must be implemented in order to instantiate."""
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pass

Coverage report treats pass as a code. Which never executes for abstractmethods.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""abstraction user package."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""User repositories meta module."""


import abc


class UserRepositoryMeta(metaclass=abc.ABCMeta):

@abc.abstractmethod
def get(self, id):
"""Must be implemented in order to instantiate."""
pass
Original file line number Diff line number Diff line change
@@ -4,11 +4,14 @@

from . import services

from ..abstraction.photo.repositories import PhotoRepositoryMeta
from ..abstraction.user.repositories import UserRepositoryMeta


class AnalyticsContainer(containers.DeclarativeContainer):

user_repository = providers.Dependency()
photo_repository = providers.Dependency()
user_repository: UserRepositoryMeta = providers.Dependency()
photo_repository: PhotoRepositoryMeta = providers.Dependency()

aggregation_service = providers.Singleton(
services.AggregationService,
18 changes: 14 additions & 4 deletions examples/miniapps/decoupled-packages/example/analytics/services.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
"""Analytics services module."""


class AggregationService:
from ..abstraction.analytics.services import AggregationServiceMeta
from ..abstraction.photo.repositories import PhotoRepositoryMeta
from ..abstraction.user.repositories import UserRepositoryMeta

def __init__(self, user_repository, photo_repository):
self.user_repository = user_repository
self.photo_repository = photo_repository

class AggregationService(AggregationServiceMeta):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class AggregationService(AggregationServiceMeta):
class AggregationServiceImpl(AggregationService):

or

Suggested change
class AggregationService(AggregationServiceMeta):
class MemoryAggregationService(AggregationService):

See comment above. This way it would be much more clear about implementation specifics of the AggregationService interface.


def __init__(self, user_repository: UserRepositoryMeta, photo_repository: PhotoRepositoryMeta):
self.user_repository: UserRepositoryMeta = user_repository
self.photo_repository: PhotoRepositoryMeta = photo_repository

def call_user_photo(self):
user1 = self.user_repository.get(id=1)
user1_photos = self.photo_repository.get_photos(user1.id)
print(f"Retrieve user id={user1.id}, photos count={len(user1_photos)} from aggregation service.")
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Photo repositories module."""


class PhotoRepository:
from ..abstraction.photo.repositories import PhotoRepositoryMeta


class PhotoRepository(PhotoRepositoryMeta):

def __init__(self, entity_factory, fs, db):
self.entity_factory = entity_factory
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""User repositories module."""

from ..abstraction.user.repositories import UserRepositoryMeta

class UserRepository:

class UserRepository(UserRepositoryMeta):

def __init__(self, entity_factory, db):
self.entity_factory = entity_factory