Application example (multiple containers)

This example shows how you can create an application using multiple declarative containers. Using multiple declarative containers is a good choice for a large application. For building a moderate or a small size application refer to Application example (single container).

We build an example micro application following the dependency injection principle. It consists of several services with a domain logic. The services have dependencies on database & AWS S3.

../_images/application.png

Start from the scratch or jump to the section:

You can find the source code and instructions for running on the Github.

Application structure

Application consists of an example package, a configuration file and a requirements.txt file.

./ ├── example/ │ ├── __init__.py │ ├── __main__.py │ ├── containers.py │ └── services.py ├── config.yml └── requirements.txt 

Containers

Listing of the example/containers.py:

"""Containers module.""" import logging.config import sqlite3 import boto3 from dependency_injector import containers, providers from . import services class Core(containers.DeclarativeContainer): config = providers.Configuration() logging = providers.Resource( logging.config.dictConfig, config=config.logging, ) class Gateways(containers.DeclarativeContainer): config = providers.Configuration() database_client = providers.Singleton( sqlite3.connect, config.database.dsn, ) s3_client = providers.Singleton( boto3.client, service_name="s3", aws_access_key_id=config.aws.access_key_id, aws_secret_access_key=config.aws.secret_access_key, ) class Services(containers.DeclarativeContainer): config = providers.Configuration() gateways = providers.DependenciesContainer() user = providers.Factory( services.UserService, db=gateways.database_client, ) auth = providers.Factory( services.AuthService, db=gateways.database_client, token_ttl=config.auth.token_ttl.as_int(), ) photo = providers.Factory( services.PhotoService, db=gateways.database_client, s3=gateways.s3_client, ) class Application(containers.DeclarativeContainer): config = providers.Configuration(yaml_files=["config.yml"]) core = providers.Container( Core, config=config.core, ) gateways = providers.Container( Gateways, config=config.gateways, ) services = providers.Container( Services, config=config.services, gateways=gateways, ) 

Main module

Listing of the example/__main__.py:

"""Main module.""" import sys from dependency_injector.wiring import Provide, inject from .services import UserService, AuthService, PhotoService from .containers import Application @inject def main( email: str, password: str, photo: str, user_service: UserService = Provide[Application.services.user], auth_service: AuthService = Provide[Application.services.auth], photo_service: PhotoService = Provide[Application.services.photo], ) -> None: user = user_service.get_user(email) auth_service.authenticate(user, password) photo_service.upload_photo(user, photo) if __name__ == "__main__": application = Application() application.core.init_resources() application.wire(modules=[__name__]) main(*sys.argv[1:]) 

Services

Listing of the example/services.py:

"""Services module.""" import logging import sqlite3 from typing import Dict from mypy_boto3_s3 import S3Client class BaseService: def __init__(self) -> None: self.logger = logging.getLogger( f"{__name__}.{self.__class__.__name__}", ) class UserService(BaseService): def __init__(self, db: sqlite3.Connection) -> None: self.db = db super().__init__() def get_user(self, email: str) -> Dict[str, str]: self.logger.debug("User %s has been found in database", email) return {"email": email, "password_hash": "..."} class AuthService(BaseService): def __init__(self, db: sqlite3.Connection, token_ttl: int) -> None: self.db = db self.token_ttl = token_ttl super().__init__() def authenticate(self, user: Dict[str, str], password: str) -> None: assert password is not None self.logger.debug( "User %s has been successfully authenticated", user["email"], ) class PhotoService(BaseService): def __init__(self, db: sqlite3.Connection, s3: S3Client) -> None: self.db = db self.s3 = s3 super().__init__() def upload_photo(self, user: Dict[str, str], photo_path: str) -> None: self.logger.debug( "Photo %s has been successfully uploaded by user %s", photo_path, user["email"], ) 

Configuration

Listing of the config.yml:

core:  logging:  version: 1  formatters:  formatter:  format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s"  handlers:  console:  class: "logging.StreamHandler"  level: "DEBUG"  formatter: "formatter"  stream: "ext://sys.stderr"  root:  level: "DEBUG"  handlers: ["console"] gateways:  database:  dsn: ":memory:"  aws:  access_key_id: "KEY"  secret_access_key: "SECRET" services:  auth:  token_ttl: 3600 

Run the application

You can find the source code and instructions for running on the Github.

Sponsor the project on GitHub: