# SELF_NOTES

## Какие варианты я рассматривал

Я смотрел два варианта управления flow.

Первый вариант- сделать состояния более самостоятельными. Например, чтобы `SplashState` сам запускал переход в `LoadState`, потом `LoadState` сам вел в `MenuState`, а `MenuState` возвращал flow обратно в `LoadState`.

От этого варианта я отказался. В таком подходе state machine слишком быстро начинает знать почти весь сценарий. Из-за этого код сложнее проверять, сложнее менять и проще случайно сломать.

Второй вариант- оставить states спокойными и без лишней логики, а сам порядок экранов держать отдельно, во внешнем `BootFlowService`. Этот вариант я и выбрал.

`StatesController<TEnum>` просто переключает состояния по понятному порядку `ExitAsync -> EnterAsync`. А `BootFlowService` уже решает, куда идти дальше `Splash -> Load -> Menu -> Load`.

Так state machine не смешивается с boot-сценарием и остается более общей.

Еще я думал запускать flow из `монобех.Start()`, но в итоге отказался. В проекте используется VContainer, поэтому старт сделан через entry point `BootstrapEntryPoint`. Так зависимости создаются контейнером, а не руками в сценовом компоненте.

## Почему я выбрал текущий вариант

Основная мысль простая, монобех остается View-слоем, а орекстрация живет в обычных C# классах.

- `GameLifetimeScope` собирает зависимости
- `BootstrapEntryPoint` запускает boot lifecycle
- `BootFlowService` ведет общий сценарий
- `StatesController<TEnum>` отвечает только за переходы между состояниями
- `SplashState`, `LoadState`, `MenuState` отвечают за вход и выход своего состояния
- `UIView` и наследники держат Unity-ссылки и биндинги
- ViewModel-классы не наследуются от `монобех`

Так код проще читать. Если нужно понять порядок экранов, я иду в `BootFlowService`. Если нужно понять UI-логику, смотрю View. Если нужно понять переходы между состояниями, смотрю `StatesController<TEnum>`.

## Что я писал и продумывал сам

Я сам выбрал основные границы ответственности. Вынес flow coordinator отдельно, сделал generic state controller, разделил View и ViewModel, а переходы убрал из View.

Для меня это важные места, потому что они показывают не просто набор классов, а именно архитектурное решение.

Также я отдельно продумывал повторные входы и очистку. `Release()` должен нормально вызываться повторно и ничего не ломать. Подписки в `LoadingUIView` должны чиститься через `CompositeDisposable`. А button listener в `MenuUIView` должен сниматься тем же method group, которым был добавлен.

Еще я исправил runtime bug с `MissingReferenceException`. При уничтожении scope Unity уже мог уничтожить View, но cancellation еще приводил к вызову `Release()`. Поэтому базовый `UIView` теперь проверяет destroyed Unity object перед обращением к геймобджект.

## Что мне понятно

Мне понятна логика `StatesController<TEnum>`. Он получает код состояния, выходит из текущего state, потом входит в новый. Он не знает ничего про boot flow, поэтому его можно использовать повторно.

Мне понятна логика `BootFlowService`. Splash выполняется один раз, потом идет loading, потом menu, а после restart запускается новый loading-cycle.

Мне понятна логика `LoadingUIView`. Она берет progress из ViewModel, подписывается на него через UniRx, двигает `Slider` и чистит подписки в `Release()`.

Мне понятна логика `MenuUIView`. Listener добавляется через `AddListener(OnRestartClicked)` и снимается через `RemoveListener(OnRestartClicked)`. Без lambda и без `RemoveAllListeners()`.

## Что пока требует дополнительного изучения

Не до конца прозрачен внутренний механизм VContainer registration вида

```csharp
builder.Register<Impl>(Lifetime.Singleton).As<IInterface>();
```

На практическом уровне я понимаю, что контейнер создает `Impl` как singleton и отдает его по интерфейсу. Но внутренние детали resolution pipeline, disposal order и build callbacks я пока глубоко не разбирал.

Также я понимаю, как использовать сервисный lifecycle

```csharp
UniTask InitializeAsync(CancellationToken ct)
UniTask ReleaseAsync(CancellationToken ct)
```

Но внутренности UniTask для меня пока остаются темой, которую нужно разобрать отдельно. На уровне API я это использую осознанно.

## Почему здесь сделано именно так

### Почему states не вызывают `EnterStateAsync` сами

Потому что тогда state начинает знать весь сценарий. Сейчас `SplashState` отвечает за splash, `LoadState` за loading, `MenuState` за ожидание restart, а порядок задает `BootFlowService`. Так связей между частями меньше.

### Почему ViewModel не монобех

Потому что ViewModel должна быть обычным C# объектом с логикой и данными для View. Unity-ссылки остаются во View, а runtime-данные передаются явно через `Bind(...)`. Так проще тестировать и меньше скрытой зависимости от сцены.

### Почему `RegisterInstance` используется только для settings

Потому что settings asset уже существует как данные сцены или проекта. А сервисы, контроллеры и states лучше создавать через контейнер, чтобы lifecycle и зависимости были управляемыми через DI.

### Почему restart идет через `MenuUIView -> MenuUIViewModel -> IMenuRestartSignal`

Потому что View не должна знать state machine. Кнопка сообщает ViewModel о действии пользователя, ViewModel дергает restart signal, `MenuState.EnterAsync` завершается, а внешний `BootFlowService` уже запускает следующий `LoadState`.
