Files
cherrypick/openspec/changes/cherrypick-system-spec/specs/di-runtime/spec.md
2026-02-27 14:33:30 +03:00

8.8 KiB
Raw Blame History

ADDED Requirements

Requirement: Сущности ядра DI

Ядро DI MUST определять сущности Scope, Module, Binding, BindingResolver, Disposable, CherryPickObserver и их роли.

Scenario: Декларация базовых сущностей

  • WHEN разработчик использует публичный API ядра
  • THEN сущности Scope, Module, Binding, Disposable, CherryPickObserver доступны и описывают контракт DI

Requirement: Жизненный цикл Scope

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

Scenario: Открытие root scope

  • WHEN вызывается открытие root scope
  • THEN создается корневой контейнер для регистраций и резолва

Scenario: Открытие named subscope

  • WHEN открывается именованный subscope
  • THEN subscope наследует bindings родителя и может переопределять зависимости

Scenario: Закрытие subscope

  • WHEN вызывается закрытие subscope
  • THEN subscope удаляется из дерева, а связанные ресурсы освобождаются

Scenario: Путь scope и разделитель

  • WHEN scope открывается по иерархическому пути с разделителем
  • THEN создается цепочка subscopes по каждому сегменту пути

Scenario: Пустой scopeName

  • WHEN scope открывается с пустым именем
  • THEN возвращается root scope

Requirement: Установка и удаление модулей

Scope MUST поддерживать установку и удаление Module, а их bindings MUST становиться доступными сразу после установки.

Scenario: Установка модуля

  • WHEN модуль установлен в scope
  • THEN его bindings доступны для resolve/tryResolve

Scenario: Сброс модулей

  • WHEN модули сброшены
  • THEN bindings модулей не доступны для резолва в этом scope

Requirement: Типы bindings

Binding MUST поддерживать прямые инстансы, синхронные провайдеры, асинхронные провайдеры и провайдеры с параметрами, а также именованные bindings.

Scenario: Именованные bindings

  • WHEN зарегистрированы два bindings одного типа с разными именами
  • THEN resolve с указанным именем возвращает соответствующую реализацию

Scenario: Параметризованный provider

  • WHEN binding зарегистрирован с провайдером, принимающим параметры
  • THEN resolve с параметрами использует переданные аргументы для создания экземпляра

Requirement: Семантика резолва и fallback

Резолв MUST искать зависимость в текущем scope и, при отсутствии, подниматься к родительским scope.

Scenario: Fallback к родителю

  • WHEN зависимость отсутствует в текущем scope, но есть в родительском
  • THEN резолв выполняется через родительский binding

Requirement: Синхронный и асинхронный резолв

Scope MUST предоставлять синхронный и асинхронный резолв и их nullableварианты.

Scenario: Resolve (sync)

  • WHEN вызывается resolve<T>() для существующей зависимости
  • THEN возвращается экземпляр или выбрасывается ошибка при отсутствии

Scenario: TryResolve (sync)

  • WHEN вызывается tryResolve<T>() для отсутствующей зависимости
  • THEN возвращается null без исключения

Scenario: Resolve (async)

  • WHEN вызывается resolveAsync<T>() для зависимости с asyncпровайдером
  • THEN возвращается Future<T> с экземпляром

Scenario: TryResolve (async)

  • WHEN вызывается tryResolveAsync<T>() для отсутствующей зависимости
  • THEN возвращается null без исключения

Requirement: Ошибки несоответствия sync/async

Резолв MUST выбрасывать ошибки при несоответствии sync/async режима.

Scenario: ResolveSync для asyncинстанса

  • WHEN binding зарегистрирован как asyncинстанс или asyncprovider, а вызывается resolveSync
  • THEN выбрасывается ошибка с указанием использовать asyncрезолв

Requirement: Управление Disposable

Scope MUST отслеживать экземпляры, реализующие Disposable, и вызывать dispose() при закрытии scope.

Scenario: Автоматическое освобождение

  • WHEN scope закрывается
  • THEN все Disposable в scope и дочерних scope освобождаются

Requirement: Детекция циклов зависимостей

Ядро DI MUST поддерживать локальную и глобальную детекцию циклов.

Scenario: Локальный цикл

  • WHEN локальная детекция включена и резолв обнаруживает цикл
  • THEN резолв завершается ошибкой цикла

Scenario: Глобальный цикл

  • WHEN глобальная детекция включена и цикл пересекает scope
  • THEN резолв завершается ошибкой цикла

Requirement: Наблюдатель событий (Observer)

Ядро DI MUST предоставлять наблюдателя, получающего события регистрации, резолва, ошибок, lifecycle и диагностики.

Scenario: События lifecycle

  • WHEN scope открывается, модули устанавливаются и зависимости резолвятся
  • THEN observer получает соответствующие уведомления

Requirement: Ошибки и сообщения об ошибках

При критических сбоях резолва ядро MUST выбрасывать ошибку с понятным сообщением, а для tryResolve MUST не бросать исключения.

Scenario: Ошибка отсутствующей зависимости

  • WHEN вызывается resolve<T>() для незарегистрированной зависимости
  • THEN выбрасывается ошибка с сообщением о невозможности резолва

Scenario: Ошибка цикла

  • WHEN обнаружен цикл зависимостей
  • THEN выбрасывается ошибка с указанием цепочки

Scenario: Отсутствующие параметры для параметризованного binding

  • WHEN binding зарегистрирован с toProvideWithParams, но резолв вызывается без params
  • THEN выбрасывается ошибка о необходимости передать параметры

Requirement: Точки расширения

Ядро DI MUST предоставлять точки расширения через пользовательский Module и CherryPickObserver.

Scenario: Пользовательский Module

  • WHEN разработчик создает свой Module
  • THEN он может зарегистрировать bindings и контролировать регистрацию

Scenario: Пользовательский Observer

  • WHEN разработчик передает кастомный observer
  • THEN он получает все DIсобытия и может интегрировать внешние логеры/метрики