Bundles mini application example

“Bundles” is an example mini application that is intented to demonstrate the power of dependency injection for creation of re-usable application components (“bundles”) with 100% transparency of their dependencies.

Example application

“Bundles” mini application has next structure:

bundles/
    bundles/               <-- Bundles package
        photos/            <-- Photos bundle
            __init__.py    <-- Photos bundle dependency injection container
            entities.py
            repositories.py
        users/             <-- Users bundle
            __init__.py    <-- Users bundle dependency injection container
            entities.py
            repositories.py
    run.py                 <-- Entrypoint

IoC containers

Next two listings show DeclarativeContainer’s for “users” and “photos” bundles.

Listing of bundeles/users/__init__.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
"""Users bundle."""

from dependency_injector import containers
from dependency_injector import providers

from . import entities
from . import repositories


class Users(containers.DeclarativeContainer):
    """Users bundle container."""

    database = providers.Dependency()

    user = providers.Factory(entities.User)
    user_repository = providers.Singleton(repositories.UserRepository,
                                          object_factory=user.provider,
                                          db=database)

Note

  • Users container has dependency on database.

Listing of bundeles/photos/__init__.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
"""Photos bundle."""

from dependency_injector import containers
from dependency_injector import providers

from . import entities
from . import repositories


class Photos(containers.DeclarativeContainer):
    """Photos bundle container."""

    database = providers.Dependency()
    file_storage = providers.Dependency()

    photo = providers.Factory(entities.Photo)
    photo_repository = providers.Singleton(repositories.PhotoRepository,
                                           object_factory=photo.provider,
                                           fs=file_storage,
                                           db=database)

Note

  • Photos container has dependencies on database and file storage.

Run application

Finally, both “bundles” are initialized by providing needed dependencies. Initialization of dependencies happens right in the runtime, not earlier. Generally, it means, that any part of any bundle could be overridden on the fly.

Listing of run.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
"""Run 'Bundles' example application."""

import sqlite3
import boto3

from dependency_injector import containers
from dependency_injector import providers

from bundles.users import Users
from bundles.photos import Photos


class Core(containers.DeclarativeContainer):
    """Core container."""

    config = providers.Configuration('config')
    sqlite = providers.Singleton(sqlite3.connect, config.database.dsn)
    s3 = providers.Singleton(
        boto3.client, 's3',
        aws_access_key_id=config.aws.access_key_id,
        aws_secret_access_key=config.aws.secret_access_key)


if __name__ == '__main__':
    # Initializing containers
    core = Core(config={'database': {'dsn': ':memory:'},
                        'aws': {'access_key_id': 'KEY',
                                'secret_access_key': 'SECRET'}})
    users = Users(database=core.sqlite)
    photos = Photos(database=core.sqlite, file_storage=core.s3)

    # Fetching few users
    user_repository = users.user_repository()
    user1 = user_repository.get(id=1)
    user2 = user_repository.get(id=2)

    # Making some checks
    assert user1.id == 1
    assert user2.id == 2
    assert user_repository.db is core.sqlite()