Внедрение зависимостей

Внедрение зависимостей (Dependency Injection, DI) — это паттерн проектирования, который помогает писать слабосвязанный, легко тестируемый и расширяемый код. Вместо того чтобы обработчики сами создавали нужные им объекты (зависимости), они получают их извне.

Argenta использует библиотеку dishka для реализации DI, что позволяет декларативно объявлять зависимости прямо в сигнатурах ваших обработчиков. Подробнее о DI, IoC и API для создания провайдеров можно прочитать в официальной документации dishka.


Основная идея

Представьте, что вашему обработчику для работы нужен доступ к базе данных. Вместо импорта и инициализации соединения внутри функции, вы просто объявляете его как аргумент с аннотацией типа:

Примечание

argenta.di.FromDishka является алиасом для dishka.FromDishka, и они полностью взаимозаменяемы.

Пример использования:

1from sqlite3 import Connection
2from argenta import Response, Router
3from argenta.di import FromDishka
4
5router = Router()
6
7@router.command("connect")
8def connect_handler(response: Response, connection: FromDishka[Connection]):
9    connection.execute("...")

Argenta с помощью dishka разрешит зависимость по типу Connection и внедрит её. Но прежде чем использовать зависимость, её необходимо объявить в провайдере:

Пример использования:

 1import sqlite3
 2from sqlite3 import Connection
 3from typing import Iterable
 4
 5from dishka import Provider, Scope, provide
 6
 7
 8class ConnectionProvider(Provider):
 9    @provide(scope=Scope.REQUEST)
10    def new_connection(self) -> Iterable[Connection]:
11        conn = sqlite3.connect(":memory:")
12        yield conn
13        conn.close()

После создания провайдера его необходимо зарегистрировать в оркестраторе.

Примечание

Провайдеры регистрируются в Orchestrator, а не в App, так как оркестратор отвечает за настройку DI-контейнера на уровне всего приложения. Вы можете передать список из нескольких провайдеров через параметр custom_providers.

Пример использования:

1from argenta import Orchestrator
2
3orchestrator = Orchestrator(custom_providers=[ConnectionProvider()])

Как это работает?

В основе DI в Argenta лежат провайдеры и контейнер.

  • Провайдер (Provider) — это «рецепт», который объясняет, как создавать и настраивать ту или иную зависимость (например, подключение к БД, API-клиент или любой другой сервис).

  • Контейнер (IoC Container) — это «фабрика», которая хранит все рецепты (провайдеры) и по запросу создаёт и выдаёт готовые зависимости.


Встроенные провайдеры

Argenta поставляется со встроенным провайдером, который даёт доступ к важным системным зависимостям без дополнительной настройки. Например, вы можете получить объект ArgSpace, который содержит аргументы командной строки, переданные при запуске приложения.

Пример использования:

1from argenta import Response, Router
2from argenta.di import FromDishka
3from argenta.orchestrator.argparser import ArgSpace
4
5router = Router()
6
7@router.command("info")
8def connect_handler(response: Response, argspace: FromDishka[ArgSpace]):
9    print(argspace.get_by_name("type"))

Обмен данными между обработчиками

Помимо DI, обработчики могут обмениваться данными в рамках сессии через объект контекста. В Argenta эту роль выполняет объект DataBridge.

Каждый обработчик может записывать в него данные, а также читать, обновлять и удалять их.

См. также

Подробнее об этом можно прочитать в разделе DataBridge.