Irbisa · Шпаргалка

System Design на Flutter

Раунд проектирования: ленты, чат, offline-first и конфликты, обновления в реальном времени, медиа, фоновые загрузки, server-driven UI

Senior-разработчик · 17 вопросов

  1. 01

    На мобильный system design дали 45 минут: на что их потратить и что на самом деле оценивает интервьюер?

    Сначала договоритесь о границах задачи и цифрах, нарисуйте слои клиента и нужный им API, а основное время отдайте одной-двум проблемам, которые делают этот продукт трудным.

  2. 02

    Какие мобильные ограничения вы поднимаете на system design (нестабильная сеть, смерть процесса, батарея, старые версии приложения) и как каждое меняет дизайн?

    Мобильное приложение живёт в сети, которая обрывается, в процессе, который ОС может убить в любой момент, на батарее и в версиях, которые уже не обновить.

  3. 03

    Главный экран Flutter-приложения ждёт пять эндпоинтов через Future.wait и пуст, пока не ответят все. Что вы поменяете в приложении и что попросите у бэкенда?

    Перестаньте ждать все пять разом: у каждой секции свой запрос и свои состояния загрузки, ошибки и данных, а первая отрисовка идёт из локального кэша.

  4. 04

    Поллинг, long polling, server-sent events, WebSocket или пуш: как выбрать транспорт для живых обновлений во Flutter-приложении?

    Выбирайте по направлению, частоте и состоянию приложения: поллинг для данных, меняющихся раз в несколько минут, SSE для одностороннего потока, WebSocket, когда обе стороны говорят часто, пуш для закрытого приложения.

  5. 05

    Как бы вы спроектировали offline-first Flutter-приложение: что читают экраны, куда уходит запись без сети и что показывает UI, пока сервер не подтвердил?

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

    // The row and its pending operation commit together or not at all.Future<void> renameNote(Database db, String id, String title) {  final payload = jsonEncode({'title': title});  return db.transaction((txn) async {    await txn.rawUpdate(      "UPDATE notes SET title = ?, sync_state = 'pending' WHERE id = ?",…
  6. 06

    Два устройства правят одну заметку офлайн и позже синхронизируются. Как разрешить конфликт (last write wins, слияние по полям, CRDT) и чего стоит каждый вариант?

    Обнаруживайте конфликт на сервере по версии, а затем берите самое дешёвое правило, которое допускают данные: last write wins там, где потеря одной правки приемлема, трёхстороннее слияние по полям для записей и CRDT только для текста или списков, которые люди действительно правят вместе.

    typedef Fields = Map<String, Object?>; /// Three-way merge of one record with scalar fields: a field changed on one/// side only takes that side's value; changed differently on both, a conflict.(Fields merged, Set<String> conflicts) mergeRecord({  required Fields base, // the version both edits started from…
  7. 07

    Приложение для торговых представителей должно искать офлайн по каталогу из 50 000 товаров. Как спроектировать первую загрузку, дельта-обновления и локальный поисковый индекс?

    Первую копию отдавайте одним готовым файлом SQLite, а не 50 000 строк JSON, поддерживайте её свежей лентой изменений и tombstone-записей после серверного курсора, а ищите через индекс FTS5 во встроенной сборке SQLite, вне UI-изолята.

  8. 08

    Спроектируйте ленту приложения вроде Instagram на Flutter: API и пагинация, локальный кэш, загрузка картинок и плавный скролл, который сохраняет позицию после перезапуска.

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

  9. 09

    Спроектируйте вертикальную видеоленту в стиле TikTok на Flutter: предзагрузка следующих роликов, пул плееров, ограничения памяти и поведение на медленной сети.

    Вертикальный PageView.builder поверх небольшого пула плееров: текущий ролик играет, следующие один-два инициализированы и буферизованы, но стоят на паузе, а всё дальше освобождено.

  10. 10

    Спроектируйте мессенджер вроде Telegram на Flutter: транспорт, статусы сообщений (отправляется, отправлено, прочитано), офлайн-очередь отправки и пагинация по истории за годы.

    Экран чата подписан на локальную базу, которую наполняет один сервис сокета вне дерева виджетов.

  11. 11

    Спроектируйте пуш-уведомления для мессенджера: что шлёт сервер, как не показывать пуш открытого чата, как группировать по чатам и убирать прочитанное на другом устройстве.

    Сервер решает, кому положен пуш, и собирает полезную нагрузку под каждую платформу; приложение само рисует уведомления чатов, где это возможно, пропускает чат, открытый на экране, держит одно уведомление на чат и снимает его, когда другое устройство сообщило о прочтении.

  12. 12

    Спроектируйте отслеживание курьера на карте: как обновления координат доходят до приложения клиента, как маркер движется плавно и что показывает экран, когда обновления пропали.

    Приложение курьера каждые несколько секунд шлёт координаты на сервер, а тот раздаёт их через сокет, пока у клиента открыт экран отслеживания, и через пуш и живое уведомление на экране блокировки, когда закрыт.

  13. 13

    Спроектируйте резервное копирование фото, которое загружает тысячи снимков в фоне, переживает убийство приложения и бережёт батарею и мобильный трафик.

    Очередь бэкапа держите в своей базе, по строке на фото, и отдавайте фоновому сервису передачи ОС только небольшое окно загрузок: оно продолжает работать, пока приложение усыплено.

  14. 14

    Спроектируйте ежедневные напоминания для трекера привычек: локальные уведомления по местному времени, которые переживают смену часового пояса, перезагрузку и недели без открытия приложения.

    Пусть повторяет ОС: zonedSchedule из flutter_local_notifications с DateTimeComponents.time срабатывает каждый день без запущенного приложения.

    // Runs after tz.initializeTimeZones(). Store the returned zone: on launch and// resume, reschedule every reminder if the device zone has changed.Future<String> scheduleDaily(  FlutterLocalNotificationsPlugin plugin, {  required int id,  required int hour,…
  15. 15

    Экран трейдинга получает тысячи обновлений цен в секунду по 500 инструментам. Как спроектировать путь от сокета до виджетов, чтобы UI держал 60 fps?

    Срезайте объём на каждом этапе: подписывайтесь только на инструменты на экране, декодируйте в долгоживущем фоновом изоляте, храните лишь последний тик по инструменту и обновляйте notifier строки не чаще десяти раз в секунду, чтобы тик перерисовывал одну строку, а не весь список.

    typedef Quote = ({String symbol, double price}); /// Thousands of ticks a second in, at most ten updates a second per row out.class QuoteBoard {  QuoteBoard(Stream<List<Quote>> batches) {    // a newer tick replaces one that nobody has seen yet…
  16. 16

    Как встроить аналитику и логирование в большое Flutter-приложение, чтобы события переживали офлайн, уважали согласие пользователя, а вендора можно было сменить, не трогая код фич?

    Фичи обращаются только к вашему типизированному классу Analytics, который сразу возвращает управление и не может уронить вызывающего: он проставляет контекст, проверяет согласие и пишет события в буфер на диске.

  17. 17

    Продукт хочет перекраивать главный экран без релиза в стор. Когда server-driven UI во Flutter окупается, как его построить и где он начинает вредить?

    Он окупается там, где раскладка меняется каждую неделю, а блоки остаются прежними: главная магазина, страницы акций, онбординг.