Если у вас есть компьютер и вы используете его по назначению, то скорее всего вы так или иначе работали с приложениями на Electron (даже если об этом не знали).

Меня зовут Сергей Володин, я руковожу командой разработки VK WorkMail. Расскажу, как на основе Electron мы за две недели создали PoC кроссплатформенного настольного приложения Почты, что узнали о технологии и к каким выводам пришли.

Для начала немного контекста. Наша команда занимается продуктовой разработкой VK WorkMail — корпоративного почтового сервиса, доступного по модели SaaS и в виде On-Premise решения. Продукт существует как мобильное приложение для Android/iOS, в web-версии, а также как один из элементов суперприложения VK Teams. У нас единая кодовая база с Почтой Mail.ru и VK Почтой.

Мы работаем на B2B-рынке и время от времени получаем от наших клиентов запросы на настольное приложение. Это не что-то вроде «Хотим отдельную иконку, а не браузер», а запрос на вполне серьёзную функциональность: возможность работать оффлайн, локальную выгрузку или удаление писем с сервера. Другими словами — запрос на создание Thick Client, или толстого клиента.

Что значит толстый клиент?

Сначала нужно договориться о понятиях. Большинство web-приложений работают по принципу тонкого клиента (Thin Client). Подобный подход представляет из себя классическое клиент-серверное взаимодействие:

Клиент, то есть интерфейс или фронтенд, шлёт запросы на сервер и получает необходимые для работы данные.

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

Как мы пришли к идее завернуть наше web-приложение в Electron?

В компании уже проводили расчет трудозатрат на запуск полноценного сервиса с оффлайн-функциональностью. Мы рассматривали offline-first подход в web-версии клиента с применением IndexedDB в качестве хранилища и Service Worker как средство кеширования статики. Однако мы пришли к выводу, что потребуется проводить значительный рефакторинг всего клиентского кода с потенциальными трудозатратами в человеко-год.

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

Одна из главных функций настольного почтового сервиса — локальная архивация писем. Приложение должно уметь выгружать письма в виде архива или файла, а также читать и предоставлять пользователю полноценный доступ к ним. Формально можно загружать такие файлы в браузер, парсить и раскладывать в IndexDB. Но поскольку архивы могут быть большого размера, это заметно снизит производительность. Другое дело — иметь полноценный доступ к файловой системе.

Размышляя об этой задаче, мы решили посмотреть на неё с другой стороны: можно ли решить нашу проблему, вообще не трогая логику web-клиента? Ведь мы не меняем интерфейс, пользователь видит все те же кнопки и визуальные компоненты. Функциональность, которая нам требуется, это совсем не про UI, а про локальную, то есть не серверную, бизнес-логику. Для наглядности размышлений, посмотрим на схему толстого клиента. Отмечу, что речь именно про клиент, поэтому серверная часть будет показана в упрощенном виде:

Здесь мы разделяем клиент на две функциональные части:

  • UI. По-сути, просто наше web-приложение, тонкий клиент. То есть интерфейс, который отвечает за отрисовку и запрашивает необходимые данные через запросы.

  • Local business logic (далее LBL). Здесь уже интереснее. Если в тонком клиенте за данными мы ходим напрямую на сервер, то в толстом клиенте — в специальный модуль LBL. Это может быть специальный процесс или отдельный модуль программы. Важно то, что он имеет у себя данные и отвечает за их отправку в UI. Также он отвечает за синхронизацию с сервером.

Как видно на этой схеме, если сервер недоступен, то клиент всё ещё сможет спокойно работать с данными. Более того, это будет не просто режим «только чтение», клиент также сможет совершать и более сложные действия с точки зрения бизнес-логики. Например, написать новое письмо или создать фильтр для писем. И как только почтовый сервис получит доступ к серверу, действия будут синхронизированы.

Оперируя изложенной выше логикой, мы пришли к идее провести эксперимент по упаковке web-приложения в оболочку для толстого клиента. Мы решили проверить, сможем ли использовать технологии, подходы и компоненты из web-версии и серверной части для создания полноценной настольной версии VK WorkMail. Суть эксперимента — быстро проверить гипотезу. Поэтому мы выделили на него сравнительно мало ресурсов: две человеко-недели (одна фронтендера и одна бэкендера). В конечном итоге нам было необходимо получить PoC (Proof of Concept) или, если угодно, MVP настольного приложения VK WorkMail.

Проектирование и выбор технологий

Перед тем как приступать непосредственно к коду, мы провели сравнительный анализ нескольких подходов. Взяли Native UI, Qt Framework и Electron и занесли их в таблицу.

Технология

Примени-мость для web

Кроссплат-форменность

Потребление памяти

Занимает на диске

Производи-тельность

Время на разработку и сопро-вождение

Native UI

-

-

мало

мало

идеально

очень много

QT

-

+

средне (~ 130 Мб)

средне (~ 70,8 Мб)

хорошо

много

Electron

+

+

много (от 200 Мб до гигабайтов)

много(не меньше 150 Мб)

медленно

немного

Native UI, возможно, один из лучших вариантов с точки зрения пользовательского опыта. Но наш сервис по умолчанию должен быть кроссплатформенным, а Native UI предполагает разработку отдельной реализации для каждой ОС, то есть как минимум трёх: MacOS, Windows, Linux. Другая проблема заключается в том, что UI VK WorkMail — это довольно сложный с точки зрения дизайн-системы проект. Интерфейс содержит в себе много кастомных компонентов, которые придётся реализовать нативно, что также замедлит разработку.

Следующий вариант — Qt. На бумаге выглядит хорошим и сбалансированным решением для создания настольного приложения. Однако нам в любом случае пришлось бы создать с нуля ещё одну версию приложения и поддерживать её параллельно с web-версией. Очевидно, это не слишком вписывается в концепцию нашего эксперимента.

Поэтому наш взор неизбежно пал на Electron. Кроссплатформенный, с открытым исходным кодом и построенный на web-технологиях. А также медленный (в сравнении с нативными приложениями) и требовательный по ресурсам (в два и более раза по сравнению с тем же Qt). Даже беря во внимание возможные недостатки, в нашей ситуации это был фактически единственный вариант создать настольное приложение командой web-разработчиков.

Для более подробного погружения в тему выбора технологий для создания настольного приложения и более подробного их сравнения советую ознакомиться со статьёй «Почему Electron это необходимое зло» на английском языке.

Выбор технологии для LBL-компонента

Если с UI всё более менее понятно — там будут использованы технологии из нашей web-версии, — то с LBL всё интереснее. Это, без сомнений, сердце нашего технического решения. С одной стороны, Electron way — это реализация функциональности внутри процесса Electron, то есть де-факто в NodeJs. Однако в таком случае нам придётся писать всю необходимую логику (возможно, дублирующую её же с сервера) заново, силами JS-программистов.

Есть также другой подход, часто применяемый в настольной разработке: вынесение кроссплатформенной core-части, отвечающей за бизнес-логику (то, что я называю LBL в этой статье) в отдельный модуль и процесс. Такой путь актуален при разработке интерфейса с Native UI-подходом, поскольку в этом случае необходимо использовать свой фреймворк под каждую ОС. Часть, отвечающая за бизнес-логику, пишется один раз и в дальнейшем используется вновь.

Так вот к чему я это всё? У нас уже есть серверная часть Почты, написанная на Go. Поскольку перед нами сейчас стоит задача написать модуль, который будет выполнять различную бизнес-логику на клиенте, то было бы неплохо использовать код сервера.

Мы решили пойти по второму пути и писать LBL-компонент на Go. Принимая это решение, руководствовались следующим:

  • Нас привлекала потенциальная возможность использовать серверный код Почты. «Зачем переписывать бизнес-логику работы с письмами на JS, если она уже написана на сервере?»

  • Также это возможность распараллелить разработку, пока фронтендеры будут заниматься настройкой Electron, бэкендеры — писать LBL. С точки зрения использования ресурсов, это казалось более правильным путём в full-stack команде, которой я руковожу.

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

Проектирование UI-компонента

Итак, мы выбрали Electron ради использования наработок нашей web-версии Почты. Давайте посмотрим в общих чертах, как работает наше (а вообще говоря, практически любое) web-приложение:

Когда пользователь заходит на страницу, отправляется get-запрос, который принимает фронтенд-сервер. Внутри сервера есть SSR-сервис, который отвечает за генерирование HTML для пользователя, у нас это NodeJS. Для того, чтобы сгенерировать уникальный HTML с пользовательскими данными, необходимо получить серверные переменные, которые мы получаем в специальном сервисе Config. Далее возвращаем пользователю HTML, в котором вшиты необходимые пользовательские данные, конфиги и т.д. Там же уже вшиты ссылки на разные статические файлы, необходимые для работы приложения: наши любимые JS-бандлы, CSS, картинки и много чего ещё.

В контексте Почты необходимо упомянуть, что мы используем архитектуру микрофронтендов, то есть большинство кусков приложения подгружается по мере необходимости во время работы приложения. Другими словами, когда пользователь открывает VK WorkMail у него грузится необходимый код для отображения списка писем. Далее, если пользователь хочет написать письмо, создать папку или зайти в адресную книгу, то ему асинхронно подгружаются JS/CSS-бандлы из нашего CDN с необходимыми UI и функциональностью.

Собственно, нашей основной задачей на фронте было засунуть всё это великолепие в Electron. Статичные файлы нужно было положить внутрь приложения, локально, и тогда они начали бы подгружаться как прежде, но уже не из CDN, а из файловой системы. Но вот с генерированием HTML с серверными переменными — вопрос. Electron way — это сделать относительно статичный HTML и вынести логику получения всех необходимых данных на клиентскую часть. Но это означало бы значительный рефакторинг core-части приложения, и для PoC за пару недель это не подходит.

Немногим позже мы поняли, что, по сути, уже запускаем наше приложение локально со всем SSR и прочим, когда стартуем dev-сервер для разработки функциональности:

Разработчик запускает dev-server (у нас это webpack), который перед тем как исполнить серверный шаблон ходит за данными на сервер, а затем результат в виде HTML возвращается в браузер. Мы решили взять эту схему для Electron, но за серверными данными ходить уже в локальный Go-модуль. А всё остальное-то у нас уже есть!

Финальное техническое решение

Итак, объединив всё наше предыдущее проектирование мы пришли к следующей схеме:

У нас есть процесс Electron, он отвечает за запуск приложения, интеграцию с ОС и прочие вещи, важные для настольного приложения. Вместе с этим процессом запускается и NodeJS-сервер (SSR Service) на localhost, и который поднимается внутри Electron. Также во время старта он форкает процесс и запускает Go-сервис, который в свою очередь уже работает с локальной базой (SQL-lite) и синхронихируется с сервером по HTTP. Также на файловой системе у нас лежат разные статичные asset’ы, которые нужны для работы приложения и запросы за которыми будут проходить через NodeJS-сервер, поднятый внутри Electron.

Разработка PoC

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

Разработка основной части Electron

Разработка началась с создания чистого репозитория. Мы завели в нем директорию packages, в которую планировали положить все необходимые asset’ы для работы приложения: серверные шаблоны, статичные, собранные и минифицированные JS, CSS, изображения и так далее. Далее по README Electron:

npm i electron electron-builder

Затем создаём index.js и погнали (в примерах может быть не совсем валидный код ради удобочитаемости и подчёркивания главных моментов):

const {app, BrowserWindow, shell} = require('electron');
const config = require('./config');
const {spawn} = require('child_process');
const server = require('./server');
function createWindow() {
const mainWindow = new BrowserWindow({
  width: config.defaultWidth,
  height: config.defaultHeight,
  center: true,
});
// запускаем гошный локальный АПИ
spawn(`./${config.goModulePath}`, ['-config', `${config.goModulePathConfig}`], {stdio: 'inherit', cwd: __dirname})

// запускаем статик-сервер для генерации HTML и раздачи статики
server.run(config.port, () => {
	mainWindow.loadURL(`http://localhost:${config.port}/`);

	// открываем ссылки (например внутри писем), в дефолтном браузере, а не в электроне
	mainWindow.webContents.setWindowOpenHandler(({url}) => {
		if (url.startsWith(config.fileProtocol)) {
			return {action: 'allow'};
		}

		shell.openExternal(url);
		return {action: 'deny'};
	});
});

}
app.whenReady().then(createWindow);

В принципе, этого кода более чем достаточно, чтобы открыть окно на Electron и увидеть белый экран. В дальнейшем появятся нюансы открытия и закрытия окон в MacOS и прочие «доработки», сосредотачиваться на которых в этой статье нет смысла.

Авторизация

Как только мы запустили окно Electron, вскрылась проблема, которую мы не учли при проектировании — авторизация. В web-версии VK WorkMail используется классический вариант с куками, но в настольной версии это не будет работать. У нас есть мобильные приложения, которые работают на сессионных токенах, и мы решили использовать их. Однако есть нюанс: мобильные приложения имеют собственные формы логина. Мы не стали делать новую форму в PoC, а выбрали другое решение, которое оказалось даже более приятным с точки зрения пользовательского опыта: прокинули авторизацию из браузера в наше приложение через OAuth. Схема такая: пользователь нажимает в Electron кнопку авторизации, у него открывается окно браузера, и если он в нём уже был авторизован, пользователь просто нажимает кнопку «Войти» у нужного ящика, и приложение всё остальное делает за него. Получилось примерно вот так:

Вы можете задаться вопросом, как получилось пробросить информацию из браузера в настольное приложение? Основная хитрость тут в том, что redirect_url на который перенаправляет OAuth-провайдер, должен вести на localhost на нужный порт или на специальную схему, которую слушает, в нашем случае, LBL и уже принимает токен авторизации.

Также вам может стать интересно, как LBL сообщает UI, что ему нужно перезагрузить страницу и что авторизация уже есть. Для этого мы при открытии браузера открываем SSE-соединение (Server Side Events) с UI в LBL и ждём события, что авторизация получена. Обратить внимание на SSE-подход, потому что мало кто знает, что существуют альтернативы вебсокетам для получения запросов и событий с сервера. Вот код:

const link = document.querySelector('#auth-link');
link.addEventListener('click', (e) => {
	e.preventDefault();
    // генерируем случайную строку для OAuth state параметра
    const state = (Date.now() * Math.random()).toString(16);
    
    // открываем соединение с Go модулем LBL
    const eventSource = new window.EventSource(`http://localhost:${config.goModulePort}/subscribe_oauth?state=${state}`);
    
    // добавляем обработчики для работы с eventSource
    
    eventSource.addEventListener('message', (event) => {
    	console.log('eventSource: new message', event.data);
    
    	if (event.data === 'success') {
    		eventSource.close();
    		location.reload();
    	}
    });
    
    eventSource.addEventListener('error', (event) => {
    	console.log('error', event);
    });
    
    eventSource.addEventListener('open', () => {
    	console.log('eventSource: open connection');
    
    	window.open(link.href += state, '_blanc');
    });
});

Если вы хотите больше узнать про OAuth, то рекомендую почитать эту статью.

Сбор всех статичных asset’ов (JS, CSS и изображений) в файловую систему

После того, как вопросы с авторизацией и исполнением серверного шаблона были решены, потребовалось перенести все необходимые для работы приложения файлы на локальную файловую систему. Настроить Node Static Server, чтобы отдавать файлы с файловой системы, — это не проблема. Но собирать десятки файлов и класть их локально вручную — не самое интересное занятие. А лень, как известно, двигатель прогресса. Поэтому мы воспользовались концепцией, распространённой в web-приложениях с service worker’ом: загрузкой статичных asset’ов, когда их нет в кеше. Я добавил такой код в свой express-сервер:

app.use('/static', (req, res, next) => {
	//проверяем есть ли файл уже в файловой системе
	const fileName = `${STATIC_DIR}/${req.path}`;
	// синхронно ходить на fs плохо, но этот код вырезается из production сборки
	if (!fs.existsSync(fileName)) {
		log('file not exists', req.path);
    	const extname = path.extname(fileName);
    	proxy.request(
    		{
    			url: `https://${config.staticUrl}/${req.path}`,
    			encoding: binaryMap[extname] ? null : 'utf8', // важно не текстовые форматы правильно кодировать
    			req,
    		},
    		(err, result, body) => {
    			console.log(result);
    			fse.outputFileSync(fileName, body, binaryMap[extname] ? 'binary' : 'utf8');
    			next();
    		})
    } else {
    	return next();
    }
});
app.use('/static', express.static(STATIC_DIR));

За одно открытие VK WorkMail я собрал в файловой системе все необходимые файлы, а в production этот app.use уже не пошёл.

Адаптация интерфейса под работу в «одной вкладке»

Далее, когда мы наконец смогли авторизоваться и отрисовать наше приложение, важно понимать, что пользовательский опыт использования браузерного приложения сильно отличается от настольного. В браузере совершено нормально в процессе работы открывать новые вкладки, переходить на совершено другой сайт по ссылке из вашего приложения, оставаясь в той же вкладке. Когда же мы переносим Почту в настольное приложение, необходимо адаптировать интерфейс под работу «в одной вкладке». Мне повезло, что Почта уже умеет интегрироваться в суперприложение для рабочих коммуникаций VK Teams с помощью WebView, поэтому я просто использовал режим работы Почты оттуда. Подробнее узнать про эту адаптацию можно из доклада на VK Tech Talks моего коллеги Дениса Романюка.

Разработка LBL-модуля на Go

Для PoC было решено в LBL-модуле поддержать полностью read-режим работы, а также поддержать одно write-действие: локальное изменение флажков у писем и их синхронизацию с сервером при наличии сети. То есть мы не поддерживали все функции сервиса локально, а только полностью поддержали чтение и одно из действий, требующих изменение данных с сервера. Этого достаточно для проверки всех наших гипотез и проведения эксперимента.

В качестве базы для локального хранения писем мы взяли SQLite. В качестве веб-фреймворка — Echo, создали в нём отдельные обработчики для взаимодействия с UI Electron и один общий, который обрабатывает запросы к серверу Почты.

Результат разработки

Мы сделали PoC настольного почтового толстого клиента за две недели: одну неделю фронтендер создавал всю обвязку на Electron и в части с UI, и ещё неделю бэкендер писал модуль на Go.

У нас получилось повторно использовать интерфейс web-версии Почты, при этом с помощью общения с LBL в Go мы смогли в оффлайне предоставить чтение писем и изменение состояния флажков на письмах.

Выводы об R&D

Первоочередной целью для нас было проверить, возможно ли создать настольное приложение на существующей кодовой базе web-приложения. И мы в хорошем смысле поражены, как легко это получилось с помощью Electron. На вопрос: «Использовали бы вы Electron для создания production ready настольного приложения?» мы ответим — да. Эта технология определённо снижает порог входа в разработку настольных приложений.

Однако с написанием LBL-модуля на Go всё оказалось не так хорошо.

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

  • Во-вторых, отдельный процесс увеличивает риск возникновения потенциальных проблем и багов, которые будет сложно устранить. Что делать, если в Go-модуле произошла паника? Нужно в JS внутри Electron это обрабатывать и перезапускать процесс. Также есть разные нюансы с тем, как операционные системы поступают с процессом приложения при сворачивании или закрытии окна (особенно Mac OS), и вводя дополнительный процесс, которым нужно вручную управлять, мы умножаем сложность работы с этим.

  • Ну и наконец межпроцессное взаимодействие. Нам нужно общаться между UI, Electron (Node) и LBL. И реализация взаимодействия между Node и Go по любым сетевым протоколам может значительно влиять на производительность.

В итоге, если бы мы делали production ready-решение, то писали бы бизнес-логику и работу с локальной базой данных внутри NodeJS-процесса Electron.

Напоследок хочу сказать пару слов о фронтенд- и web-разработке. Технологии и требования к приложениям сменяются так быстро, что устойчивые и выверенные практики их создания не успевают появляться. Тем не менее я с предвкушением ожидаю будущее фронтенд- и web-разработки. 

Web-приложения запускаются как с браузеров на компьютере и телефоне, так и на Smart TV, чайниках, зубных щётках и телескопов. И Electron, безусловно, одна из технологий, которые вносят вклад во всё это.

Комментарии (31)


  1. AlexeyK77
    30.09.2022 10:44

    коммент не в тот пост..(удалено)


  1. SuperTEHb
    30.09.2022 10:45
    +8

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


    1. vileven Автор
      30.09.2022 11:15

      Никто на пользователей же ничего не запускал :)

      Цель была как раз проверить наши гипотезы в R&D и поделиться опытом.
      А теперь этот прототип пойдёт в UX тестирование, чтобы получить фидбек от тестовых групп, затем будут приниматься дальнейшие решения.


  1. hardtop
    30.09.2022 11:08

    А flutter рассматривали? Легче и быстрее электрона


    1. yroman
      30.09.2022 11:14
      +3

      Я так понимаю, что даже если писать на флаттере, UI часть все равно нужно писать с нуля, а этим ребятам важно побыстрее выкатить рабочий вариант. То, что оно будет кривое, косое и жручее, их не сильно волнует.


    1. vileven Автор
      30.09.2022 11:19
      +1

      Flutter рассматривали, да. Для нас он был в одной категории с Qt примерно. Переиспользовать существующий UI нельзя, но вариант привлекательный. Возможно, в дальнейшем попробуем провести R&D с ним уже :)


    1. RealFLYNN
      30.09.2022 11:25

      Если не охота ковырять Dart, то лучше посмотреть в сторону Tauri - пишем основной процесс на Rust/C++/чем угодно (для чего есть биндинги), фронтенд на любимом JS-стеке. На выходе получаем легкий бинарник, который вертит свой UI в нативном для системы WebView, без хромиума и V8


      1. vileven Автор
        30.09.2022 11:32

        Думали о решениях с нативным WebView тоже. Там нас немного отталкивали потенциальные проблемы с разными ОС и их версиями (WebView API не везде одинаковый, и там много раз нюансов есть).

        Взяли пока Электрон как раз по той причине, что казалось, что поскольку там хромиум внутри — мы уверены, что наше приложение точно будет работать везде и именно так, как мы ожидаем. НО да, расплата — его объём.

        В целом, в будущем, может быть погрузимся в эту тему глубже, спасибо за комментарий :)


  1. Sazonov
    30.09.2022 11:44
    +2

    70 метров Qt? Это у вас на 50 метров иконок? Рантайм Qt, если вам достаточно UI+сеть+SQLite ужимается до ~13-20 мегабайт.


    1. vileven Автор
      30.09.2022 16:39
      -1

      не эксперт в Qt и не буду спорить (скорее всего Вы правы!), информацию брал из открытых источников (может ошибся). В статье она нужна больше для сравнительного порядка, чем для точной цифры, поэтому тщательно не проверял :)


      1. Sazonov
        01.10.2022 00:11

        Ну да. 5-7 порядков. Если что, Qt уже достаточно хорошо умеет в WebAssembly :)

        Единственный минус - цена лицензий. Но для такой компании это не должно быть проблемой. Хотя вру, ещё есть санкции, но вполне можно заюзать LGPL версию.


        1. rapidstream
          01.10.2022 07:41
          +2

          5-7 порядков? Это в 100000-10000000 раз больше?


          1. Sazonov
            01.10.2022 15:20

            Да, конечно я имел в виду 5-7 раз. Ночью писал, спать хотелось уже


  1. AlexeyK77
    30.09.2022 11:50
    +7

    говорят, что для разработчиков на электроне сталелитейни в аду разработали особый проект котла. Он очень быстрый в проектировании отливке, быстро можно выкатить первый котел в продуктив по мере появления их востребованности. Но есть у котлов особенность, очень толстые стенки, из-за чего даже по окончанию срока прожарки и отключения подачи магмы и серы в систему нагрева, то за счет огромной теплоемкости котел еще будет долго-долго отдавать жар на максимуме теплоотдачи. ;)


    1. le2
      30.09.2022 13:09

      давно под Линуксом использую Visual Studio Code, который, как известно, на Электроне. Отлично работает (почти. Пару раз завешивал мне десктоп)


      1. AlexeyK77
        01.10.2022 10:59
        -1

        Вот и черти из R&D отдела тоже так рассудили. технология рабочая, хоть и тяжеловатая для юзера, но все формально соответствует ТЗ и главное выкатили в короткие сроки. Как никак у них там сейчас жуткий кранч под новые срочные задачи, связанные с необходимость обеспечить масштабирование сервисов из-за прогнозируемого возрастания загрузки.


  1. savostin
    30.09.2022 11:57

    А можно хоть немного подробностей как реализовывали синхронизацию SQLite с сервером?


    1. vileven Автор
      30.09.2022 16:44
      +1

      У нас было только одно write действие, поэтому мы сделали очень просто: при наличии интернета мы сразу идём на сервер, а при отсутсвии изменяем локально + добавляем в специальную очередь событий пользователя, которая процессится при появлении интернета. Мы не сильно беспокоились о всех возможных кейсах, так как делали просто прототип.

      Возможно вам интересен момент: "Что делать, если на сервере, пока на клиенте не было интернета, произошло что-то еще?" и всякие такие подобные колизии. Если кратко — то всегда доверять серверу, но лучше почитать непосредственно про подобные приложения отдельно, на том же хабре. Таких приложений много, особенно в мобильной и десктопной разработке.


      1. savostin
        30.09.2022 17:15

        Спасибо. Меня больше интересует "протокол". Т.е. на сервере есть API, которое дергается из клиента? Какие-то самописные команды или есть какой-то фреймворк/либа? Если просто флаг обновить, то возможно простого запроса к скриптику и хватит, а вот если много всяких разных данных, структур и пр? Как "взрослые" делают offline first синхронизацию с SQL(ite) локальным хранилищем?


        1. vileven Автор
          30.09.2022 17:51
          +1

          Да сервер, в данном случае тот же самый сервер, который отдаёт данные для web-версии и мобильных приложений. У нас аля REST API.

          По поводу синхронизации, попробуйте покопаться вглубь CQRS (Command and Query Responsibility Segregation) — обычно этот паттерн используется в offline first системах. Предупреждаю, это на самом деле все очень непросто :)


          1. savostin
            30.09.2022 18:12

            Спасибо. Вот и я понимаю, что простого решения нет. Каждый изобретает свой велосипед...


  1. avdosev
    30.09.2022 17:59

    Сколько в итоге потребляет это приложение? Интересно сколько получилось для такого исследовательского решения.


  1. RaymanOne
    01.10.2022 16:04

    Надо было просто сделать PWA, и опубликовать его в магазин MS если есть такая необходимость. А вообще скорее всего лучше бы подошел Tauri


    1. ZoomLS
      01.10.2022 16:10

      +1. Tauri - это то, каким должен быть электрон. А не это недоразумение, что сейчас.


      1. RaymanOne
        01.10.2022 17:01

        Не увидел специфических API, тем более на десктопе их куча для веба доступно. Подошло бы PWA. Одна кодовая база с браузерной версией, оффлайн режим, все тоже самое. Не пришлось бы городить какие-то electron обертки, а авторизация бы УЖЕ была, если юзер авторизован в браузере был.


        1. vileven Автор
          01.10.2022 18:46

          Я пишу, почему нельзя было сделать PWA в статье. Проблема именно в том, что один из основных запросов клиентов – выгрузка части писем локально (не в базу, а прям файлом) и взаимодействие с ними. Собственно поэтому и нужен толстый клиент и полноценная работа с файловой системой.


          1. RaymanOne
            01.10.2022 18:50

            На сколько у вас толстые письма? Не изучали возможности indexeddb? Вы знаете что на десктопе у веба практически не ограниченный размер там?


            1. vileven Автор
              01.10.2022 19:56

              Про это есть в статье) Речь же ещё была про попытку заюзать уже готовый северный код на Go.

              Про размер: гигабайты (возможно десятки их). Совершенно легко может быть кейс — выгрузить локально весь ящик за последние N лет со всеми аттачами и файлами в письме. Нам не нравилась история с загрузкой всего этого добра в браузер и перекладка в IndexedDB и обратно (Хотя, в статье я пишу, что это возможно конечно).

              Я сам готов бесконечно евангилировать за PWA и весь веб-стэк.

              Но наш эксперимент пошёл по-другому пути и я много уделяю внимания в тексте, чем мы руководствовались. Я вообще не претендую на то, что выбранные технологии — это самый лучший выбор из возможных. Это больше "вот мы взяли это, попробовали сделать PoC, вот что у нас получилось и к каким выводам мы пришли".


              1. RaymanOne
                01.10.2022 21:47

                Go скорее всего получилось бы использовать в Wasm модуле. Загрузку можно было организовать через воркер, в приложение уже отдавать готовые ответы сетевые. Жаль что для эксперимента не взяли тогда tauri, если в рамки PWA боялись не вписаться. Electron нужно активно хоронить, уже не одна хорошая альтернатива ему есть.


              1. RaymanOne
                01.10.2022 23:25

                Как вариант можно было бы попробовать поработать с https://web.dev/file-system-access/#opening-a-directory-and-enumerating-its-contents В общем не очень понял в чем в итоге был эксперимент...


  1. noodles
    01.10.2022 18:05

    На https://wails.io/ не смотрели?