594 lines
36 KiB
Markdown
594 lines
36 KiB
Markdown
# Rosetta Android — Architecture
|
||
|
||
> Документ отражает текущее состояние `rosetta-android` (ветка `dev`) по коду на 2026-04-19.
|
||
|
||
## 1. Архитектурный профиль
|
||
|
||
Приложение сейчас устроено как layered + service-oriented архитектура:
|
||
- UI: `MainActivity` + Compose-экраны + ViewModel.
|
||
- Chat feature orchestration: `ChatViewModel` (host-state) + feature-facade VM + coordinators.
|
||
- DI: Hilt (`@HiltAndroidApp`, `@AndroidEntryPoint`, модули в `di/AppContainer.kt`).
|
||
- Runtime orchestration: `ProtocolGateway`/`ProtocolRuntime` -> `RuntimeComposition` (+ legacy facade `ProtocolManager`), `CallManager`, `TransportManager`, `UpdateManager`.
|
||
- Session/Identity runtime state: `SessionStore`, `SessionReducer`, `IdentityStore`.
|
||
- Domain сценарии отправки чата: `domain/chats/usecase/*` (text/media/forward/voice/typing/read-receipt/attachments/upload).
|
||
- Data: `MessageRepository`, `GroupRepository`, `AccountManager`, `PreferencesManager`.
|
||
- Persistence: Room (`RosettaDatabase`) + DataStore/SharedPreferences.
|
||
|
||
Основная runtime-логика сети вынесена в `RuntimeComposition`, а DI-вход в runtime идет напрямую через `ProtocolRuntime`.
|
||
`ProtocolManager` переведен в минимальный legacy compatibility facade поверх `ProtocolRuntimeAccess`.
|
||
DI-вход в network core идет через `ProtocolRuntime` (Hilt singleton).
|
||
|
||
---
|
||
|
||
## 2. Слои и границы
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph ENTRY["Android Entry Points"]
|
||
E1["RosettaApplication"]
|
||
E2["MainActivity"]
|
||
E3["RosettaFirebaseMessagingService"]
|
||
E4["IncomingCallActivity / CallForegroundService"]
|
||
end
|
||
|
||
subgraph DI["Hilt Singleton Graph"]
|
||
D1["ProtocolGateway -> ProtocolRuntime"]
|
||
D2["SessionCoordinator"]
|
||
D3["IdentityGateway"]
|
||
D4["AccountManager / PreferencesManager"]
|
||
D5["MessageRepository / GroupRepository"]
|
||
end
|
||
|
||
subgraph CHAT_UI["Chat UI Orchestration"]
|
||
C1["ChatDetailScreen / ChatsListScreen"]
|
||
C2["ChatViewModel (host-state)"]
|
||
C3["Feature VM: Messages/Voice/Attachments/Typing"]
|
||
C4["Coordinators: Messages/Forward/Attachments"]
|
||
end
|
||
|
||
subgraph CHAT_DOMAIN["Chat Domain UseCases"]
|
||
U1["SendText / SendMedia / SendForward"]
|
||
U2["SendVoice / SendTyping / SendReadReceipt"]
|
||
U3["CreateAttachment / EncryptAndUpload / VideoCircle"]
|
||
end
|
||
|
||
subgraph SESSION["Session / Identity Runtime"]
|
||
S1["SessionStore / SessionReducer"]
|
||
S2["IdentityStore / AppSessionCoordinator"]
|
||
end
|
||
|
||
subgraph NET["Network Runtime"]
|
||
N0["ProtocolRuntime"]
|
||
N1["RuntimeComposition (wiring only)"]
|
||
N2["RuntimeConnectionControlFacade"]
|
||
N3["RuntimeDirectoryFacade"]
|
||
N4["RuntimePacketIoFacade"]
|
||
N5["Assemblies: Transport / Messaging / State / Routing"]
|
||
N6["ProtocolInstanceManager -> Protocol"]
|
||
N7["ProtocolManager (legacy compat)"]
|
||
end
|
||
|
||
subgraph DATA["Data + Persistence"]
|
||
R1["MessageRepository / GroupRepository"]
|
||
R2["Room: RosettaDatabase"]
|
||
end
|
||
|
||
ENTRY --> DI
|
||
DI --> SESSION
|
||
DI --> DATA
|
||
DI --> CHAT_UI
|
||
DI --> N0
|
||
CHAT_UI --> CHAT_DOMAIN
|
||
CHAT_UI --> R1
|
||
CHAT_DOMAIN --> D1
|
||
D1 --> N0
|
||
N0 --> N1
|
||
N1 --> N2
|
||
N1 --> N3
|
||
N1 --> N4
|
||
N1 --> N5
|
||
N5 --> N6
|
||
N7 --> N0
|
||
SESSION --> N0
|
||
R1 --> N0
|
||
R1 --> R2
|
||
```
|
||
|
||
---
|
||
|
||
## 3. DI и composition root
|
||
|
||
### 3.1 Hilt
|
||
- `RosettaApplication` помечен `@HiltAndroidApp`.
|
||
- Entry points уровня Android-компонентов: `MainActivity`, `IncomingCallActivity`, `CallForegroundService`, `RosettaFirebaseMessagingService`.
|
||
- Основные модули:
|
||
- `AppDataModule`: `AccountManager`, `PreferencesManager`.
|
||
- `AppGatewayModule`: биндинги `ProtocolGateway`, `SessionCoordinator`, `IdentityGateway`, `ProtocolClient`.
|
||
- `ProtocolGateway` теперь биндится напрямую на `ProtocolRuntime` (без отдельного `ProtocolGatewayImpl` proxy-класса).
|
||
- `ProtocolClientImpl` остается узким техническим adapter-слоем для repository (`send/sendWithRetry/addLog/wait/unwait`) и делегирует в `ProtocolRuntime` через `Provider<ProtocolRuntime>`.
|
||
|
||
### 3.2 UI bridge для composable-слоя
|
||
UI-композаблы больше не получают runtime-зависимости через `UiEntryPoint`/`EntryPointAccessors`.
|
||
`UiDependencyAccess.get(...)` из `ui/*` удален (DoD: 0 вхождений).
|
||
|
||
Для non-Hilt `object`-ов (`CallManager`, `TransportManager`, `UpdateManager`, utils)
|
||
используется `ProtocolRuntimeAccess` + `ProtocolRuntimePort`:
|
||
- runtime ставится в `RosettaApplication` через `ProtocolRuntimeAccess.install(protocolRuntime)`;
|
||
- доступ до install запрещен (fail-fast), чтобы не было тихого отката в legacy facade.
|
||
|
||
### 3.3 Разрыв DI-cycle (Hilt)
|
||
После перехода на `ProtocolRuntime` был закрыт цикл зависимостей:
|
||
`MessageRepository -> ProtocolClient -> ProtocolRuntime -> MessageRepository`.
|
||
|
||
Текущее решение:
|
||
- `ProtocolClientImpl` получает `Provider<ProtocolRuntime>` (ленивая резолюция).
|
||
- `ProtocolRuntime` остается singleton-композицией для `MessageRepository/GroupRepository/AccountManager`.
|
||
- На `assembleDebug/assembleRelease` больше нет `Dagger/DependencyCycle`.
|
||
|
||
---
|
||
|
||
## 4. Session lifecycle: единый source of truth
|
||
|
||
### 4.1 Модель состояния
|
||
`SessionState`:
|
||
- `LoggedOut`
|
||
- `AuthInProgress(publicKey?, reason)`
|
||
- `Ready(account, reason)`
|
||
|
||
### 4.2 Модель событий
|
||
`SessionAction`:
|
||
- `LoggedOut`
|
||
- `AuthInProgress`
|
||
- `Ready`
|
||
- `SyncFromCachedAccount`
|
||
|
||
### 4.3 Контур изменения состояния
|
||
- Только `SessionStore` владеет `MutableStateFlow<SessionState>`.
|
||
- Только `SessionReducer` вычисляет next-state.
|
||
- `SessionCoordinator`/`AppSessionCoordinator` больше не мутируют состояние напрямую, а делают `dispatch(action)`.
|
||
- `SessionStore.dispatch(...)` синхронно обновляет `IdentityStore` для консистентности account/profile/auth-runtime.
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A["AuthFlow / MainActivity / Unlock / SetPassword"] --> B["SessionCoordinator.dispatch(action)"]
|
||
B --> C["SessionStore.dispatch(action)"]
|
||
C --> D["SessionReducer.reduce(current, action)"]
|
||
D --> E["StateFlow<SessionState>"]
|
||
C --> F["IdentityStore sync"]
|
||
```
|
||
|
||
### 4.4 State machine
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> LoggedOut
|
||
LoggedOut --> AuthInProgress: dispatch(AuthInProgress)
|
||
AuthInProgress --> Ready: dispatch(Ready)
|
||
AuthInProgress --> LoggedOut: dispatch(LoggedOut)
|
||
Ready --> LoggedOut: dispatch(LoggedOut)
|
||
Ready --> Ready: dispatch(SyncFromCachedAccount(account))
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Network orchestration после декомпозиции
|
||
|
||
`ProtocolRuntime` — DI-фасад runtime слоя и реализация `ProtocolGateway`/`ProtocolRuntimePort`.
|
||
`RuntimeComposition` — composition-root runtime слоя (сборка service graph + orchestration wiring) и делегирует отдельные зоны ответственности:
|
||
- Публичные runtime API proxy-методы (connect/auth/directory/packet I/O) убраны из `RuntimeComposition`; публичный runtime surface теперь удерживается в `ProtocolRuntime` + `Runtime*Facade`.
|
||
- `RuntimeTransportAssembly`: отдельный assembly-блок transport/network wiring (`NetworkReconnectWatcher`, `NetworkConnectivityFacade`, `ProtocolInstanceManager`, `PacketSubscriptionRegistry/Facade`).
|
||
- `RuntimeMessagingAssembly`: отдельный assembly-блок packet/message/sync wiring (`PacketRouter`, `OutgoingMessagePipelineService`, `PresenceTypingService`, `SyncCoordinator`, `CallSignalBridge`, `InboundPacketHandlerRegistrar`).
|
||
- `RuntimeStateAssembly`: отдельный assembly-блок connection-state wiring (`ReadyPacketGate`, `BootstrapCoordinator`, `RuntimeLifecycleStateMachine`, `OwnProfileFallbackTimerService`, `ProtocolLifecycleStateStoreImpl`).
|
||
- `RuntimeRoutingAssembly`: отдельный assembly-блок event-routing wiring (`ConnectionEventRouter` + `ProtocolConnectionSupervisor` как единый orchestration-шаг).
|
||
- `RuntimeConnectionControlFacade`: high-level connection/session control API (`initialize*`, `connect/reconnect/sync/auth`, `disconnect/destroy`, auth/connect checks).
|
||
- `RuntimeDirectoryFacade`: directory/device/typing API (`resolve/search user`, cached user lookup, own-profile signal, device accept/decline, typing snapshot by dialog).
|
||
- `RuntimePacketIoFacade`: packet I/O API (`send/sendWithRetry/resolveRetry`, call/webrtc/ice bridge, `wait/unwait/packetFlow`).
|
||
- `ProtocolInstanceManager`: singleton lifecycle `Protocol` (create/state/lastError/disconnect/destroy/isAuthenticated/isConnected).
|
||
- `RuntimeLifecycleStateMachine`: runtime lifecycle state (`ConnectionLifecycleState` + `ConnectionBootstrapContext`) и пересчет transition-логики через `BootstrapCoordinator`.
|
||
- `RuntimeInitializationCoordinator`: one-time bootstrap runtime (`initialize`, регистрация packet handlers, старт state monitoring, проверка bound DI dependencies).
|
||
- `ProtocolLifecycleStateStoreImpl`: отдельное lifecycle-state хранилище (`bootstrapContext`, `sessionGeneration`, last-subscribed-token clear hooks, own-profile fallback timer hooks).
|
||
- `OwnProfileFallbackTimerService`: управление таймером own-profile fallback (`schedule/cancel`) с генерацией timeout-события.
|
||
- `AuthRestoreService`: восстановление auth-handshake credentials из локального кеша аккаунта (`preferredPublicKey`/fallback + validation + authenticate trigger).
|
||
- `RuntimeShutdownCoordinator`: централизованный graceful runtime shutdown (`stop watcher`, `destroy subscriptions/protocol`, `clear runtime state/services`, `cancel scope`).
|
||
- `ConnectionEventRouter`: маршрутизация `ConnectionEvent` к соответствующим coordinator/service handlers без `when(event)` внутри core.
|
||
- `NetworkConnectivityFacade`: единая обертка network-availability/wait/stop policy поверх `NetworkReconnectWatcher`.
|
||
- `ConnectionOrchestrator`: connect/reconnect/authenticate + network-aware поведение.
|
||
- `ProtocolLifecycleCoordinator`: lifecycle/auth/bootstrap transitions (`ProtocolStateChanged`, `SyncCompleted`, own-profile resolved/fallback).
|
||
- `ProtocolAccountSessionCoordinator`: account-bound transitions (`InitializeAccount`, `Disconnect`) и reset account/session state.
|
||
- `ReadyPacketDispatchCoordinator`: обработка `SendPacket` через ready-gate (`bypass/enqueue/flush trigger + reconnect policy`).
|
||
- `ProtocolPostAuthBootstrapCoordinator`: post-auth orchestration (`canRun/tryRun bootstrap`, own profile fetch, push subscribe, post-sync retry/missing-user-info).
|
||
- `BootstrapCoordinator`: пересчет lifecycle (`AUTHENTICATED`/`BOOTSTRAPPING`/`READY`) и работа с `ReadyPacketGate`.
|
||
- `SyncCoordinator`: sync state machine (request/timeout, BATCH_START/BATCH_END/NOT_NEEDED, foreground/manual sync).
|
||
- `PresenceTypingService`: in-memory typing presence с TTL и snapshot `StateFlow`.
|
||
- `PacketRouter`: user/search cache + resolve/search continuation routing.
|
||
- `OwnProfileSyncService`: применение собственного профиля из search и синхронизация `IdentityStore`.
|
||
- `RetryQueueService`: retry очереди отправки `PacketMessage`.
|
||
- `AuthBootstrapCoordinator`: session-aware post-auth bootstrap (transport/update/profile/sync/push).
|
||
- `NetworkReconnectWatcher`: единый watcher ожидания сети и fast-reconnect триггеры.
|
||
- `DeviceVerificationService`: состояние списка устройств + pending verification + resolve packets.
|
||
- `DeviceRuntimeService`: device-id/handshake device + device verification orchestration.
|
||
- `CallSignalBridge`: call/webrtc/ice signal send+subscribe bridge.
|
||
- `PacketSubscriptionFacade`: thin bridge `waitPacket/unwaitPacket/packetFlow` API поверх `PacketSubscriptionRegistry`.
|
||
- `PacketSubscriptionRegistry`: централизованные подписки на пакеты и fan-out.
|
||
- `InboundPacketHandlerRegistrar`: централизованная регистрация inbound packet handlers (`0x03/0x05/0x06/0x07/0x08/0x09/0x0B/0x0F/0x14/0x17/0x19`) и делегирование в sync/repository/device/typing/profile сервисы.
|
||
- `InboundTaskQueueService`: sequential inbound task queue (`enqueue` + `whenTasksFinish`) для Desktop parity (`dialogQueue` semantics).
|
||
- `OutgoingMessagePipelineService`: отправка `PacketMessage` с retry/error policy.
|
||
- `ProtocolDebugLogService`: буферизация UI-логов, throttle flush и персистентный protocol trace.
|
||
|
||
На hot-path `ProtocolRuntime` берет runtime API (`RuntimeConnectionControlFacade`/`RuntimeDirectoryFacade`/`RuntimePacketIoFacade`) напрямую из `RuntimeComposition`, поэтому лишний proxy-hop через публичные методы composition не используется.
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
PR["ProtocolRuntime (ProtocolGateway impl)"] --> RC["RuntimeComposition"]
|
||
RC --> RCC["RuntimeConnectionControlFacade"]
|
||
RC --> RDF["RuntimeDirectoryFacade"]
|
||
RC --> RPF["RuntimePacketIoFacade"]
|
||
|
||
RC --> RTA["RuntimeTransportAssembly"]
|
||
RC --> RMA["RuntimeMessagingAssembly"]
|
||
RC --> RSA["RuntimeStateAssembly"]
|
||
RC --> RRA["RuntimeRoutingAssembly"]
|
||
|
||
RTA --> PIM["ProtocolInstanceManager"]
|
||
RTA --> PSF["PacketSubscriptionFacade"]
|
||
RTA --> NCF["NetworkConnectivityFacade"]
|
||
|
||
RMA --> SC["SyncCoordinator"]
|
||
RMA --> PROUTER["PacketRouter"]
|
||
RMA --> OMPS["OutgoingMessagePipelineService"]
|
||
RMA --> CSB["CallSignalBridge"]
|
||
RMA --> IPR["InboundPacketHandlerRegistrar"]
|
||
|
||
RSA --> RLSM["RuntimeLifecycleStateMachine"]
|
||
RSA --> BC["BootstrapCoordinator"]
|
||
RSA --> RPG["ReadyPacketGate"]
|
||
RSA --> PLSS["ProtocolLifecycleStateStoreImpl"]
|
||
|
||
RRA --> SUP["ProtocolConnectionSupervisor"]
|
||
RRA --> CER["ConnectionEventRouter"]
|
||
|
||
CER --> CO["ConnectionOrchestrator"]
|
||
CER --> PLC["ProtocolLifecycleCoordinator"]
|
||
CER --> PAC["ProtocolAccountSessionCoordinator"]
|
||
CER --> RPDC["ReadyPacketDispatchCoordinator"]
|
||
|
||
PIM --> P["Protocol (WebSocket + packet codec)"]
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Централизация packet-subscriptions
|
||
|
||
Проблема дублирующихся low-level подписок закрыта через `PacketSubscriptionRegistry`:
|
||
- На каждый `packetId` создается один bus и один bridge на `Protocol.waitPacket(...)`.
|
||
- Дальше packet fan-out идет в:
|
||
- callback API (`waitPacket/unwaitPacket`),
|
||
- `SharedFlow` (`packetFlow(packetId)`).
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Feature as Feature/Service
|
||
participant PR as ProtocolRuntime
|
||
participant RPF as RuntimePacketIoFacade
|
||
participant PSF as PacketSubscriptionFacade
|
||
participant REG as PacketSubscriptionRegistry
|
||
participant P as Protocol
|
||
|
||
Feature->>PR: waitPacket(0x03, callback)
|
||
PR->>RPF: waitPacket(0x03, callback)
|
||
RPF->>PSF: waitPacket(0x03, callback)
|
||
PSF->>REG: addCallback(0x03, callback)
|
||
REG->>P: waitPacket(0x03, protocolBridge) [once per packetId]
|
||
|
||
P-->>REG: Packet(0x03)
|
||
REG-->>Feature: callback(packet)
|
||
REG-->>Feature: packetFlow(0x03).emit(packet)
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Чат-модуль: декомпозиция и message pipeline
|
||
|
||
### 7.1 Domain слой для сценариев отправки
|
||
|
||
Use-case слой вынесен из UI-пакета в `domain/chats/usecase`:
|
||
- `SendTextMessageUseCase`
|
||
- `SendMediaMessageUseCase`
|
||
- `SendForwardUseCase`
|
||
- `SendVoiceMessageUseCase`
|
||
- `SendTypingIndicatorUseCase`
|
||
- `SendReadReceiptUseCase`
|
||
- `CreateFileAttachmentUseCase`
|
||
- `CreateAvatarAttachmentUseCase`
|
||
- `CreateVideoCircleAttachmentUseCase`
|
||
- `EncryptAndUploadAttachmentUseCase`
|
||
|
||
Роли use-case слоя:
|
||
- `SendTextMessageUseCase`/`SendMediaMessageUseCase`: сборка `PacketMessage` + dispatch через `ProtocolGateway` (с учетом `isSavedMessages`).
|
||
- `SendForwardUseCase`: сборка forward-reply JSON, сборка forward attachment и dispatch.
|
||
- `SendVoiceMessageUseCase`/`SendTypingIndicatorUseCase`: normalization/decision логика (preview waveform, throttle/guard).
|
||
- `SendReadReceiptUseCase`: отдельный сценарий отправки `PacketRead`.
|
||
- `Create*AttachmentUseCase`: типобезопасная сборка attachment-моделей.
|
||
- `EncryptAndUploadAttachmentUseCase`: общий шаг `encrypt + upload` с возвратом `transportTag/transportServer`.
|
||
|
||
Текущий поток отправки:
|
||
1. Feature VM/Coordinator через `ChatViewModel`-host формирует command + encryption context.
|
||
2. UseCase строит payload/decision (`PacketMessage` или typed decision model).
|
||
3. `ProtocolGateway.sendMessageWithRetry(...)` уводит пакет в network runtime.
|
||
4. `RuntimeComposition` (через `ProtocolRuntime`) регистрирует пакет в `RetryQueueService` и отправляет в сеть.
|
||
5. До `READY` пакет буферизуется через `ReadyPacketGate`, затем flush.
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
FVM["Feature ViewModel"] --> CVM["ChatViewModel (host)"]
|
||
CVM --> COORD["Messages/Forward/Attachments Coordinator"]
|
||
CVM --> UC["domain/chats/usecase/*"]
|
||
COORD --> UC
|
||
UC --> GW["ProtocolGateway.send / sendMessageWithRetry"]
|
||
GW --> PR["ProtocolRuntime"]
|
||
PR --> RPF["RuntimePacketIoFacade"]
|
||
RPF --> OMP["OutgoingMessagePipelineService"]
|
||
OMP --> RQ["RetryQueueService"]
|
||
OMP --> RR["RuntimeRoutingAssembly"]
|
||
RR --> RG["ReadyPacketGate / ReadyPacketDispatchCoordinator"]
|
||
RG --> P["Protocol.sendPacket"]
|
||
```
|
||
|
||
### 7.2 Декомпозиция ChatViewModel (host + feature/coordinator слой)
|
||
|
||
Для UI-слоя введены feature-facade viewmodel-классы:
|
||
- `MessagesViewModel`
|
||
- `VoiceRecordingViewModel`
|
||
- `AttachmentsViewModel`
|
||
- `TypingViewModel`
|
||
|
||
Они живут в `ui/chats/ChatFeatureViewModels.kt` и компонуются внутри `ChatViewModel`.
|
||
Текущий статус:
|
||
- `VoiceRecordingViewModel` содержит реальный send-pipeline голосовых сообщений.
|
||
- `TypingViewModel` содержит реальную отправку typing indicator (throttle + packet send).
|
||
- `MessagesViewModel` содержит orchestration-level entrypoint (`sendMessage`, `retryMessage`), а core text send pipeline вынесен в `MessagesCoordinator` (pending recovery/throttle + reply/forward packet assembly).
|
||
- `ForwardCoordinator` вынесен из `ChatViewModel`: `sendForwardDirectly` + forward rewrite/re-upload helper-ветка (включая payload resolve из cache/download).
|
||
- `AttachmentsCoordinator` вынесен из `ChatViewModel`: `updateOptimisticImageMessage`, `sendImageMessageInternal`, `sendVideoCircleMessageInternal` + local cache/update (`localUri` cleanup после отправки).
|
||
- `AttachmentsFeatureCoordinator` вынесен из `AttachmentsViewModel`: high-level media orchestration для `sendImageGroup*`, `sendFileMessage`, `sendVideoCircleFromUri`, `sendAvatarMessage`.
|
||
- `AttachmentsViewModel` теперь концентрируется на facade-методах и `sendImageFromUri`/`sendImageMessage`, делегируя крупные media-ветки в coordinator-слой.
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
CD["ChatDetailScreen"] --> MVM["MessagesViewModel"]
|
||
CD --> TVM["TypingViewModel"]
|
||
CD --> VVM["VoiceRecordingViewModel"]
|
||
CD --> AVM["AttachmentsViewModel"]
|
||
MVM --> CVM["ChatViewModel (host-state)"]
|
||
TVM --> CVM
|
||
VVM --> CVM
|
||
AVM --> CVM
|
||
CVM --> MCO["MessagesCoordinator"]
|
||
CVM --> FCO["ForwardCoordinator"]
|
||
CVM --> ACO["AttachmentsCoordinator"]
|
||
AVM --> AFCO["AttachmentsFeatureCoordinator"]
|
||
CVM --> U["domain/chats/usecase/*"]
|
||
MCO --> U
|
||
FCO --> U
|
||
ACO --> U
|
||
AFCO --> U
|
||
```
|
||
|
||
Важно: после вынесения `MessagesCoordinator`, `ForwardCoordinator` и `AttachmentsCoordinator` `ChatViewModel` выступает как host-state и bridge для feature/coordinator подсистем.
|
||
|
||
### 7.3 Декомпозиция ChatsListScreen
|
||
|
||
Из `ChatsListScreen.kt` вынесены отдельные composable-секции:
|
||
- `ChatItem` -> `ChatsListChatItem.kt`
|
||
- `RequestsSection` -> `ChatsListRequestsSection.kt`
|
||
- `DrawerContent` -> `ChatsListDrawerContent.kt`
|
||
|
||
Результат:
|
||
- основной файл экрана меньше и проще для навигации;
|
||
- повторно используемые куски UI имеют явные file boundaries;
|
||
- дальнейший рефакторинг drawer/request/chat list можно делать независимо.
|
||
|
||
---
|
||
|
||
## 8. Auth/bootstrap: фактический runtime flow
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant UI as Auth UI (SetPassword/Unlock)
|
||
participant SC as SessionCoordinatorImpl
|
||
participant SS as SessionStore
|
||
participant PG as ProtocolGateway
|
||
participant PR as ProtocolRuntime
|
||
participant RCC as RuntimeConnectionControlFacade
|
||
participant RRA as RuntimeRoutingAssembly
|
||
participant RSA as RuntimeStateAssembly
|
||
participant AM as AccountManager
|
||
|
||
UI->>SC: bootstrapAuthenticatedSession(account, reason)
|
||
SC->>SS: dispatch(AuthInProgress)
|
||
SC->>PG: initializeAccount(public, private)
|
||
SC->>PG: connect()
|
||
SC->>PG: authenticate(public, privateHash)
|
||
SC->>PG: reconnectNowIfNeeded(...)
|
||
SC->>AM: setCurrentAccount(public)
|
||
SC->>SS: dispatch(Ready)
|
||
|
||
PG->>PR: runtime API calls
|
||
PR->>RCC: connection/auth commands
|
||
RCC->>RRA: post(ConnectionEvent.*)
|
||
RRA-->>RRA: Supervisor + Router route events
|
||
RRA-->>RSA: apply lifecycle transitions
|
||
RSA-->>RSA: AUTHENTICATED -> BOOTSTRAPPING -> READY
|
||
```
|
||
|
||
Важно: `SessionState.Ready` (app-session готова) и `connectionLifecycleState = READY` (сеть готова) — это разные state-модели.
|
||
|
||
---
|
||
|
||
## 9. Состояния соединения (network lifecycle)
|
||
|
||
`RuntimeComposition.connectionLifecycleState`:
|
||
- `DISCONNECTED`
|
||
- `CONNECTING`
|
||
- `HANDSHAKING`
|
||
- `AUTHENTICATED`
|
||
- `BOOTSTRAPPING`
|
||
- `READY`
|
||
- `DEVICE_VERIFICATION_REQUIRED`
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> DISCONNECTED
|
||
DISCONNECTED --> CONNECTING
|
||
CONNECTING --> HANDSHAKING
|
||
HANDSHAKING --> DEVICE_VERIFICATION_REQUIRED
|
||
HANDSHAKING --> AUTHENTICATED
|
||
AUTHENTICATED --> BOOTSTRAPPING
|
||
BOOTSTRAPPING --> READY
|
||
READY --> HANDSHAKING
|
||
AUTHENTICATED --> DISCONNECTED
|
||
BOOTSTRAPPING --> DISCONNECTED
|
||
READY --> DISCONNECTED
|
||
DEVICE_VERIFICATION_REQUIRED --> CONNECTING
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Ключевые файлы новой архитектуры
|
||
|
||
- `app/src/main/java/com/rosetta/messenger/di/AppContainer.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolRuntime.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeComposition.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeTransportAssembly.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeMessagingAssembly.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeStateAssembly.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeRoutingAssembly.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeConnectionControlFacade.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimeDirectoryFacade.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/RuntimePacketIoFacade.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolClient.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolRuntimeAccess.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/session/AppSessionCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/session/SessionStore.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/session/SessionReducer.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/session/SessionAction.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/session/IdentityStore.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/PacketSubscriptionRegistry.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolConnectionModels.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ProtocolConnectionSupervisor.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/ReadyPacketGate.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ConnectionOrchestrator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolInstanceManager.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/RuntimeLifecycleStateMachine.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/RuntimeInitializationCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolLifecycleStateStoreImpl.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/OwnProfileFallbackTimerService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/AuthRestoreService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/RuntimeShutdownCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ConnectionEventRouter.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/NetworkConnectivityFacade.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/PacketSubscriptionFacade.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolLifecycleCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolAccountSessionCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ReadyPacketDispatchCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolPostAuthBootstrapCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/BootstrapCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/SyncCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/PresenceTypingService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/AuthBootstrapCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/NetworkReconnectWatcher.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/DeviceVerificationService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/DeviceRuntimeService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/CallSignalBridge.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/InboundPacketHandlerRegistrar.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/InboundTaskQueueService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/OutgoingMessagePipelineService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/ProtocolDebugLogService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/PacketRouter.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/OwnProfileSyncService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/network/connection/RetryQueueService.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatViewModel.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatFeatureViewModels.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/MessagesCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ForwardCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/AttachmentsCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/AttachmentsFeatureCoordinator.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/OutgoingEncryptionContext.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/OutgoingSendContext.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListChatItem.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListRequestsSection.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListDrawerContent.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListDrawerSections.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListRequestsScreen.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendTextMessageUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendMediaMessageUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendForwardUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendVoiceMessageUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendTypingIndicatorUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/SendReadReceiptUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/CreateAttachmentUseCases.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/EncryptAndUploadAttachmentUseCase.kt`
|
||
- `app/src/main/java/com/rosetta/messenger/domain/chats/usecase/VideoCircleMediaUseCases.kt`
|
||
|
||
---
|
||
|
||
## 11. Что осталось как технический долг
|
||
|
||
Актуальные открытые хвосты:
|
||
- `RuntimeComposition` остается composition-root (около 501 строки): публичные proxy-методы уже убраны, но внутри все еще смешаны wiring и часть helper-логики (`setupStateMonitoring`, event-bridge, log helpers). Следующий шаг: вынести эти helper-блоки в отдельные adapters/services.
|
||
- `ProtocolRuntime` + `ProtocolRuntimePort` все еще имеют широкий API surface (connection + directory + packet IO + call signaling + debug). Нужен audit и сужение публичных контрактов по use-case группам.
|
||
- `ChatViewModel` остается очень крупным host-классом (около 4391 строки) с большим bridge/proxy surface к feature/coordinator/use-case слоям.
|
||
- `AttachmentsFeatureCoordinator` остается крупным (около 761 строки): high-level media сценарии стоит резать на более узкие upload/transform/packet-assembly сервисы.
|
||
- Тестовое покрытие архитектурно-критичных слоев недостаточно: `app/src/test` = 7, `app/src/androidTest` = 1; не покрыты runtime-routing/lifecycle компоненты (`RuntimeRoutingAssembly`, `ConnectionEventRouter`, `ProtocolLifecycle*`, `ReadyPacketDispatchCoordinator`) и chat coordinators (`Messages/Forward/Attachments*`).
|
||
- В runtime все еще несколько точек входа (`ProtocolRuntime`, `ProtocolRuntimeAccess`, `ProtocolManager` legacy), что повышает cognitive load; целевой шаг — дальнейшее сокращение legacy/static call-sites.
|
||
|
||
Уже закрыто и больше не считается техдолгом:
|
||
- `UiDependencyAccess.get(...)` удален из `ui/*`.
|
||
- `UiEntryPoint`/`EntryPointAccessors` убраны из UI-экранов (явная передача зависимостей через `MainActivity`/`ViewModel`).
|
||
- DI-cycle `MessageRepository -> ProtocolClient -> ProtocolRuntime -> MessageRepository` закрыт через `Provider<ProtocolRuntime>`.
|
||
- `ProtocolManager` переведен в минимальный legacy compatibility API (тонкие прокси к `ProtocolRuntimeAccess`).
|
||
|
||
---
|
||
|
||
## 12. Guardrails против переусложнения
|
||
|
||
Чтобы декомпозиция не превращалась в «архитектуру ради архитектуры», применяются следующие правила:
|
||
|
||
1. Лимит глубины runtime-цепочки вызова: не более 3 логических слоев после DI-entry (`ProtocolRuntime -> Runtime*Facade -> service`; `RuntimeComposition` остается composition-root/wiring-слоем, а не обязательным proxy-hop).
|
||
2. Новый слой/класс допускается только если он дает измеримый выигрыш:
|
||
- убирает минимум 80-120 строк связанной orchestration-логики из текущего класса, или
|
||
- убирает минимум 2 внешние зависимости из текущего класса.
|
||
3. Каждый шаг рефакторинга считается завершенным только после: `compileDebugKotlin` + минимум одного smoke-сценария по затронутому флоу + обновления `Architecture.md`.
|
||
4. Если после выноса сложность чтения/изменения не снизилась (по факту код не стал проще), такой вынос считается кандидатом на откат/консолидацию.
|
||
5. Для event-driven runtime-chain (`ProtocolConnectionSupervisor` + `ConnectionEventRouter`) эти два элемента считаются одним orchestration-этапом при анализе hop-depth.
|
||
6. `ProtocolClientImpl` трактуется как инфраструктурный DI-adapter и учитывается отдельно от business-flow hop budget.
|
||
|
||
---
|
||
|
||
## 13. Плюсы и минусы текущей архитектуры
|
||
|
||
### 13.1 Плюсы
|
||
- Четко выделены слои: UI, domain use-cases, network runtime, session/identity, data/persistence.
|
||
- DI через Hilt и `ProtocolGateway`/`SessionCoordinator` снижает прямую связанность между UI и transport/runtime.
|
||
- Убраны `UiEntryPoint`/`EntryPointAccessors` из UI-экранов, что улучшило явность зависимостей.
|
||
- Закрыт критичный DI-cycle `MessageRepository -> ProtocolClient -> ProtocolRuntime -> MessageRepository` через `Provider<ProtocolRuntime>`.
|
||
- Network runtime декомпозирован на отдельные сервисы/coordinator-ы с более узкими зонами ответственности.
|
||
- Сокращен DI runtime path: `ProtocolGateway` биндится напрямую на `ProtocolRuntime`, runtime работает напрямую с `RuntimeComposition`.
|
||
- Централизован packet subscription fan-out (`PacketSubscriptionRegistry` + `PacketSubscriptionFacade`), что снижает риск дублирующих low-level подписок.
|
||
- В chat-модуле выделен domain use-case слой и вынесены крупные сценарии в coordinators.
|
||
|
||
### 13.2 Минусы
|
||
- `RuntimeComposition` и `ChatViewModel` остаются очень крупными hotspot-классами и концентрируют много связей.
|
||
- Runtime API-слой пока широкий: много proxy-методов усложняют контроль границ и эволюцию surface API.
|
||
- В части chat/media orchestration (`AttachmentsFeatureCoordinator`, `MessagesCoordinator`, `ForwardCoordinator`) сохраняются большие high-level сценарии.
|
||
- Мало unit/integration тестов на архитектурно-критичные runtime/chat orchestration компоненты.
|
||
- В проекте остаются несколько точек доступа к runtime (`ProtocolRuntime`, `ProtocolRuntimePort`, `ProtocolManager` legacy), что повышает cognitive load для новых разработчиков.
|
||
- Стоимость входа в кодовую базу выросла: для трассировки одного бизнес-флоу нужно проходить больше слоев, чем раньше.
|
||
|
||
### 13.3 Итог оценки
|
||
- Текущая архитектура стала заметно лучше по управляемости зависимостей и изоляции ответственности.
|
||
- Главные риски сместились из “монолитного класса” в “размер composition/API surface и недотестированность orchestration”.
|
||
- При соблюдении guardrails (секция 12) и фокусе на тестах/дальнейшей локальной декомпозиции архитектура остается управляемой и не уходит в избыточную сложность.
|