Files
Mastermind/docs/architecture/swift-patterns.md
T

8.1 KiB

Swift Implementation Patterns Mastermind

Этот документ описывает нормативные паттерны реализации кода на Swift.

1. Concurrency & State Ownership

Problem: Состояние гонки (race conditions) и неопределённое поведение при многопоточности. Rule: Каждая stateful-возможность (Capability) или сервис принадлежат конкретному actor. Межмодульный обмен — только через Sendable неизменяемые типы (value types). MainActor используется исключительно для UI-проекций. Apply when: При проектировании хранилищ, сервисов и вью-моделей. Avoid: Использование lock, DispatchQueue для синхронизации состояния вручную; захват мутабельного состояния в замыканиях. Trade-offs: Требует понимания Swift Concurrency и использования await. Verification: Swift 6 Strict Concurrency не должен выдавать предупреждений и ошибок.

2. Bounded Async Streams

Problem: Неконтролируемое накопление событий в очередях (backpressure) приводит к утечкам памяти и задержкам. Rule: Все AsyncStream должны иметь ограниченный буфер (bounded) и явную политику обработки переполнения (dropOldest, dropNewest или coalesce). Apply when: Для потоков аудио-фреймов, скриншотов и событий UI. Avoid: Создание неограниченных потоков событий. Verification: Каждый стрим должен иметь тесты на поведение при переполнении.

3. Lightweight UDF (Unidirectional Data Flow)

Problem: Сложная двусторонняя синхронизация UI и бизнес-логики. Rule: Использование однонаправленного потока данных: Immutable State → View → Intent (Action) → Service/Reducer → New State. Без обязательной зависимости от тяжелых фреймворков (TCA). Apply when: В реализации Companion Island и экранов управления. Avoid: Прямая мутация состояния из View; использование Binding для бизнес-логики. Verification: View зависит только от State и отправляет Intents.

4. Boundary State Machines

Problem: Неявные переходы между состояниями (например, Collectors) приводят к трудновоспроизводимым багам. Rule: Использование явных конечных автоматов (State Machines) для жизненного циклаCollectors, сессий и миграций. Недопустимые переходы должны быть невозможны на уровне типов. Apply when: Управление жизненным циклом сложных компонентов. Avoid: Большое количество разрозненных Bool флагов для описания состояния. Verification: Unit-тесты покрывают матрицу переходов.

5. Validated Value Types

Problem: Проброс примитивов (String, Int) через все слои приводит к потере смысла и ошибкам валидации. Rule: Использование отдельных типов-обёрток для доменных понятий (ID, Timestamp, Confidence). Проверка инвариантов происходит при создании типа. Apply when: Все доменные сущности и параметры портов. Avoid: Использование String для ID или Double для Confidence без обёртки. Verification: Код компилируется только при передаче правильных типов; невозможны "пустые" или некорректные значения.

6. Manual Composition Root

Problem: Глобальные синглтоны и Service Locator делают зависимости неявными. Rule: Использование ручного внедрения зависимостей (Constructor Injection) в единственной точке входа (Composition Root). Глобальные мутабельные синглтоны запрещены. Apply when: Инициализация приложения в AppShell. Avoid: Использование shared instance для бизнес-логики. Verification: Все зависимости можно подменить (mock) в тестах без изменения кода модулей.

7. Workflow-Sized Services

Problem: Use cases, которые делают слишком мало (один метод) или слишком много (весь модуль). Rule: Application Service должен отражать осмысленный пользовательский или системный воркфлоу (например, AssistantSessionService) и оркестровать несколько портов. Avoid: Создание класса UseCase для каждой мелкой функции; "божественные" объекты-координаторы. Verification: Сервис покрывает логически связанную группу действий.

8. Typed Failure States

Problem: Обобщённые ошибки Swift.Error не дают понимания, как на них реагировать. Rule: Ожидаемые ошибки моделируются как типизированные состояния (Enum). Ошибки адаптеров переводятся в доменные ошибки на границе модуля. Apply when: Возврат результатов из портов и сервисов. Avoid: Проброс NSError или URLError в доменные слои. Verification: UI может точно отобразить причину сбоя на основе типа ошибки.

9. Structured Task Ownership

Problem: Утечки задач (detached tasks) и сложности с отменой (cancellation). Rule: Каждая долгоживущая Task принадлежит владельцу жизненного цикла и отменяется при его завершении. Task.detached запрещён, кроме системных воркеров. Apply when: Запуск Collectors и фоновой обработки. Avoid: "Fire-and-forget" задачи без сохранения ссылки на отмену. Verification: Deinit объекта приводит к остановке всех запущенных им задач.

10. Dedicated Executors for Blocking Work

Problem: Блокировка потока актора или MainActor тяжелыми вычислениями. Rule: Все блокирующие операции (SQLite, ML, PCM) выносятся на выделенные очереди или исполнители (Dedicated Executors/Queues). Оркестрация акторов не должна выполнять тяжелую работу. Apply when: I/O, обработка медиа, криптография. Avoid: Выполнение Data(contentsOf:) или сложных циклов на MainActor. Verification: Профилирование в Instruments не показывает блокировок UI потока.