Архитектура «Р7-Офис»: почему сам редактор и интерфейсы надо обязательно разносить?

image

Поскольку мы писали офис уже в тот момент, когда Интернет был не только в каждом доме, но и в чайниках, и в холодильниках, у нас была возможность сесть и продумать архитектуру редактора.

С одной стороны, мы, конечно же, оказались в сравнении с MS в ситуации сильно догоняющих. С другой стороны, они сидели с огромным монолитом и кучей легаси, поддерживали множество субверсий продукта и так далее. А мы могли сразу выбрать архитектуру, удобную для разработки. Более того, нам не нужно было копировать фичи MS: наша основная задача — сделать пакет удобным для российского применения на практике. То есть редко используемые фичи можно положить в самый низ бэклога.

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

Если многие другие офисные пакеты исторически шли по пути добавления в какой-то момент совместимости с онлайном ещё через один редактор, то у нас он один. И онлайн-редактирование, и редактирование в десктопном приложении будут выполняться абсолютно одинаково. Но в десктопном приложении уже будет содержаться свой собственный встроенный сервер. Это, кстати, автоматически означает, что мы не можем открывать запароленные документы на веб-версии: это означало бы передачу пароля в открытом виде на сервер, что небезопасно. Сейчас решаем это за счёт возможности работать с ЭЦП внутри локальных компонентов браузеров.

Причём необходимо сразу продумать, как российские компании это будут применять в 2020–30-х годах, и получить ещё кучу преимуществ интеграции. Следствие — то же управление правами доступа: на текущий момент мы единственный пакет, который «из коробки» умеет устанавливать в документе права на редактирование, только чтение, только комментирование, запрещать печать и копирование, встроить водяной знак хоть для какой-то защиты от фотографирования и так далее.

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

Наш документ-сервер состоит из двух основных частей: клиентской и серверной.

image

Сам редактор состоит из трёх частей. Первая часть — это логический редактор, то есть то, что отвечает за формирование структуры документа. То есть это превращение того, что делает пользователь, в наш базовый стандарт OOXML (открытый формат). Например, объектом там может быть параграф, диаграмма, автофигура и так далее. Логический же редактор отвечает за разбивку документа на страницы и подобные вещи, которые выполняются для пользователя автоматически.

Отдельная часть отвечает за презентацию документа, то есть за его рендер. Его ведь нужно отправить обратно пользователю, чтобы он его увидел. Для рендера очень важно знать габариты каждого объекта, особенно шрифтовых: там море нюансов. После расчёта положения каждого элемента делается отрисовка.

Третий модуль — это сам отрисовщик. Он работает по плану, который дал предыдущий модуль. Уровень API — нарисовать линию по координатам, сделать заливку и так далее. Верхний уровень — превращение каждого объекта с его координатами в набор инструкций для уровня API.

Рендер на принтер выглядит точно так же, как рендер на экран, но с другими настройками.

То есть каждый слой редактора заизолирован и представляет собой архитектурно отдельную единицу. Условно шрифтовой движок подключается ко второму модулю. Связь между интерфейсом пользователя и API-редактора делает Message Manager, который отвечает за взаимодействие. Например, если пользователь ввёл слово «сабака», то оно через сообщение ушло на сервер. Там сервер его проверил на орфографию и принял решение подчеркнуть в интерфейсе. Ушло сообщение на GUI пользователя, что нужно его подчеркнуть. Через эти же сообщения идут запросы прав на документ. Эти же сообщения помогают синхронизировать совместное редактирование: всё, что делает второй-третий пользователи, отправляется первому в сообщениях с сервера (для этого используется ещё один изолированный модуль — Collaboration). При выходе из редактора отправляется команда «Собрать документ», и он собирается, а затем ложится в модуль хранения. Чаще всего это Document Storage на сервере, который сразу же отдаёт ссылку на этот самый документ.

Простым изменением GUI можно получить мобильный редактор, онлайновый редактор или десктопный редактор. Такие уровни абстракции дают нам возможность добавлять новые фичи очень просто и легко сразу везде. Например, мы уже сделали сводные таблицы. Мы пишем один раз их логику и пару раз их GUI: для мобильной версии нужно сделать элементы настроек удобнее для пальцев, крупнее. Дальше, если мы вдруг найдём баг, то он будет надёжно лежать в своём слое: или на уровне принятия команды, или на уровне презентации, например. И править его будет очень легко.

Документ-сервер


Документ-сервер представляет собой набор сервисов и набор устройств. Сюда приходят команды на подготовку поля работы для редактора. Например, если пользователь хочет совместить DOX, DOCX, RTF и что-то на базе OOXML, то всё это встанет в очередь на преобразования, которая будет дёргать сервис конвертации. Документ-сервер содержит временное хранилище: когда пользователь, например, в совместном редактировании начинает создавать новый параграф, в это хранилище падает новый объект с ним, и пользователь уже фокусирует сессию на этом объекте. Сессии хранятся ещё в одном отдельном хранилище сессий — там всё оптимизировано так, чтобы блокировки применялись и отпускались очень быстро.

В конце сессий документ идёт на сборку. Это даёт ещё одну интересную особенность: при редактировании DOCX мы не конвертируем весь файл при сохранении, а только добавляем-убираем-редактируем те отдельные XML-объекты, которые трогал пользователь. То есть если он поправил там пару абзацев, то в DOCX изменятся только они. Учитывая количество разных вариантов интерпретаций этого открытого формата и тот факт, что у MS — своё видение на то, как оно должно работать, это наиболее щадящий и совместимый режим. То, что не было изменено, будет ровно таким, как в исходном документе. Обычный подход других офисных пакетов — сконвертировать всё в свой формат, а потом сконвертировать из своего формата в новый DOCX и перезаписать всё.

Важно это вот почему: например, в DOCX рамка вокруг картинки задаётся как флаг true/false. Если она есть, то она есть. А в OOXML рамка состоит из четырёх элементов, и внизу картинки её может не быть, например. Теперь, если вы хотите положить в DOCX картинку, у которой нет правой границы, а нижняя красная, то сконвертировать впрямую это не выйдет: нет взаимооднозначного соответствия между всеми свойствами.

image

image

Что это всё дало


  1. Мы не заморачиваемся на поддержку, совместимость и апдейты предыдущих версий, поскольку нам достаточно просто обновить серверную часть. Да, мы дистрибутируем пакеты под самые разные OС, включая Win и CentOS, а также RedHat-системы. Но организация будущего для крупной компании — это общее корпоративное хранилище документов (виртуализованное, кластеризованное и катастрофоустойчивое) и сервисы-обработчики вокруг него, связанные по API с GUI конечных пользователей. То есть сейчас в десктопные версии мы встраиваем этот сервер и хранилища, что выглядит некоторым оверхедом, но это всё равно в разы удобнее и современнее, чем держать несколько версий редактора и всей обвязки для разных сред.
  2. У нас проще поддержка: мы можем добавлять фичу или править баг в одном изолированном месте, а не искать 9–12 вхождений в разных версиях для разных платформ.
  3. Компоненты можно запускать распределённо, что точно нужно и для геокластеров крупных компаний, и для катастрофоустойчивости хранилищ (речь идёт про документ-сервера). Одна из особенностей работы на российском рынке для госкомпаний — готовность к тому, что один из ЦОДов внезапно нагреется до 30–40 тысяч градусов и уйдёт в офлайн вместе с населённым пунктом. Мы к этому готовы. В пакет, кстати, сразу входят и балансировщик нагрузки, и RabbitMQ, и всё остальное необходимое.
  4. Данные уже хранятся в Облаке (или корпоративном хранилище on premise). Их можно защищать паролем.
  5. Есть две типовые реализации DOCX: стандартная на базе DOC от MS и остальная, то есть чаще всего — конвертация в OOXML, который лежит в основе структуры документов большинства доступных русских офисных пакетов, включая наш. Но, как я писал выше, мы не ломаем DOCX ни на рендере, ни на сохранении.
  6. Наша архитектура не предполагает различий между редакторами на десктопе и вебе. Казалось бы, если у вас есть десктопное приложение, то достаточно запустить его на сервер и сделать тонкий клиент. Ну вот MS пробовали когда-то, и результат им не понравился настолько, что они сделали вообще отдельный редактор для веб-версии. И дело не только в самом редакторе, но и в управлении сессиями, правильном обмене данными с сервером, отзывчивости документов, скорости обновления, взаимоблокировках и так далее.
  7. При этом наш документ отрисовывается не в браузере нативно, а в браузерном компоненте, то есть не зависит от рендера браузера. В Google Docs, например, документ будет по-разному выглядеть в разных браузерах, и первая же разница будет в разном рендере шрифтов.
  8. Правильная архитектура разбиения на объекты означает, что мы можем спокойно отдавать в коллаборационный модуль большие документы. Когда открывается какой-нибудь отчёт на 100 страниц, один пользователь редактирует в начале, второй — на 50-й странице, а третий — ближе к концу: это конец Штирлица. Потому что при неправильном обмене и неправильном рендере у человека на 50-й странице всё будет скакать, а у человека на сотой — не только скакать, но и тормозить из-за постоянного пересчёта документа. У нас тоже есть некоторые сложности на файлы больше 1 000 страниц — тот же MS Word для таких ситуаций оптимизирован лучше.
  9. Нам не надо писать отдельный язык (типа Visal Basic) для поддержки макросов и плагинов. JS, дёргающего API, достаточно. Макрос от плагина в этой архитектуре отличается только тем, что макрос хранится прямо внутри документа. Объектная модель документа очень сильно облегчает работу и с макросами в том числе.


Так что поздний старт имеет некоторые преимущества. По крайней мере, нам просто разрабатывать.

И, как обычно, если у вас есть какие-то предложения по Р7, то напишите, пожалуйста. Нам очень важно понимать, как можно улучшить работу нашего пакета для российских реалий.

© Habrahabr.ru