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

138 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 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события и может интегрировать внешние логеры/метрики