ViewModel — це не місце для вашої логіки

20 лютого 2023 р.2 хв читання
Одна з найпоширеніших помилок, яку я бачу в Android-розробці, — це перевантажені ViewModels.
ViewModel є — і повинна залишатися — тримачем UI-стану (state), і нічим більше. Її відповідальність — експозити (expose) стейт і трансформувати його. Все, що не стосується стейту, наприклад, навігація або I/O, там не місце.

Чому ніякої навігації?

Розгляньмо застосунок, спочатку розроблений тільки для телефонів. Навігація проста: ви передаєте навігатор і пушите (push) екрани в стек. Тепер уявіть адаптацію цього ж застосунку для планшетів або десктопів.
Раптом навігація перестає бути лінійним стеком. Екран може потребувати відображення поруч з іншим, у панелі (pane) або в зовсім іншому лейауті. Якщо логіка навігації живе всередині ViewModel, ви застрягнете на переписуванні або розгалуженні цієї логіки для кожного форм-фактора.
Навіть якщо ми ігноруємо Single Responsibility Principle, вбудовування навігації у ViewModels тісно пов'язує (tightly couples) їх з конкретною структурою UI — і це не масштабується за межі одного розміру екрана.

Чому ніяких I/O операцій?

ViewModel, що виконує низькорівневе I/O — це прямий удар по тестуємості та підтримуваності. Ці питання належать до дата-леєру (data layer).
Як тільки I/O прокрадається у ViewModel, тести починають розмивати відповідальність.
UI-логіка, бізнес-логіка та доступ до даних тестуються разом — або не тестуються зовсім. Саме тут зазвичай з'являються @VisibleForTesting, рефлексія або просто відсутні тести.
У цей момент ViewModel більше не є передбачуваним контейнером стейту, а неявним оркестратором всього — недоторканним, як головний герой у фільмі.

Емпіричне правило (The rule of thumb)

  • UI: обробляє навігацію та реагує на інтеракції користувача
  • ViewModel: тримає та трансформує стейт
  • Data / Application / Domain layers: обробляють I/O та бізнес-правила
Ось і все. Тримайте ViewModels нудними — і ваша архітектура залишиться гнучкою.