features · Sep 3, 2014

Quire: как создать большое приложение на Dart

✦
Переведено ИИ
· Смотреть на английском

Приложение Quire, созданное на языке программирования Dart

Последнее обновление: 28 мая 2026 г.

TL;DR

Quire (53 992 строки, 1620 КБ кода на Dart) создан в 2014 году на Dart и на клиенте, и на сервере. Вместо AngularDART и Polymer.dart выбраны DQuery и Bootjack ради более тонкого контроля над DOM. Ключевые решения (строгая типизация во всём стеке, общие модели данных клиента и сервера, цикл событий на сервере, tree shaking для JS) остаются актуальными и в 2026 году.

Quire — не первое веб-приложение на Dart и не последнее, но, возможно, первое, которое так сильно опирается на Dart и на клиенте, и на сервере.

Логотип языка программирования Dart и фреймворк для веб-приложения Quire

Это увлекательный менеджер задач с древовидной структурой в самой основе. Сам проект насчитывает 53 992 строки, 1620 КБ кода на Dart и использует несколько открытых библиотек сообщества.

Кто создал Quire?

До начала работы над этим проектом мы были энтузиастами Dart и известны как команда Rikulo. Мы выпустили несколько библиотек на Dart: UI-фреймворк, UI-библиотеку, веб-сервер, сервер сообщений, клиент базы данных и другие.

Когда Dart впервые вышел в 2011 году, нас воодушевили его перспективы, и мы сразу стали использовать его в небольших частях проектов. Со временем мы решились создать полноценное приложение, написанное в основном на Dart.

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

Почему Quire выбрал Dart, а не JavaScript?

Причин выбрать Dart множество. Вот самые важные для нас:

  • Строгая система типов Dart защищает от бесчисленных мелких ошибок. Dart Editor, официальная IDE, быстро сообщает об ошибках типов, подсказывает автодополнение и упрощает отслеживание кода.
  • Модель наследования на классах, на наш взгляд, гораздо интуитивнее прототипной.
  • Dart позволяет использовать один язык на клиенте и сервере, с общей моделью данных и общей кодовой базой.
  • Dart исправляет большинство «подводных камней» JavaScript. Он не идеален, но избавляет от 99% старых неприятных сюрпризов.
  • В Dart уже сегодня доступны некоторые популярные возможности JavaScript Harmony: Future (промисы), стрелочные функции и другие.
  • За Dart стоит сильная команда: много качественных официальных библиотек и продуманные API. Язык ещё развивается, но спецификация уже вполне стабильна.
  • На сервере мы предпочитаем модель цикла событий многопоточности.
  • При компиляции в JavaScript Dart выполняет tree shaking. (См. ниже.)

У выбора Dart есть и минусы:

  • Сообщество Dart намного меньше сообщества JavaScript.
  • Взаимодействие Dart и JavaScript нетривиально.
  • В языке со строгой типизацией сложнее писать полифилы для API.
  • Производительность Dart Editor на больших проектах пока не оптимальна.

Благодаря строгой типизации писать на Dart гораздо надёжнее, чем на JavaScript. Кроме того, Dart менее многословен, чем Java, а иногда (например, в функциональных выражениях) даже лаконичнее чистого JavaScript. В целом работать с Dart комфортно, за исключением нескольких моментов:

  • Final-поле нужно инициализировать в списке инициализации, раньше тела конструктора.
  • Спецификация миксинов довольно сырая, и до версии 2.0 её не исправят.
  • Нет параметров обобщённых типов для объявления функций, только для классов. (Конечно, это увеличило бы нагрузку на компилятор.)

Как Dart работает на стороне клиента?

Пока виртуальная машина Dart не появилась в Chrome, продукт приходится доставлять, компилируя его в JavaScript. Языков, компилируемых в JavaScript, много, но у Dart есть несколько дополнительных преимуществ.

  • В разработке мы запускаем Dart нативно в Dartium, браузере на основе Chromium со встроенной виртуальной машиной Dart. Компилировать на этом этапе не нужно, и это избавляет от бесконечных перерывов.
  • В тестах и на продакшене мы компилируем код в JavaScript и запускаем во всех основных браузерах. Компилятор Dart в JavaScript выполняет tree shaking и удаляет из результата неиспользуемый код. Это сильно уменьшает размер нашего JavaScript.

Зачем использовать Dart на сервере?

На сервере Dart никогда не был популярной темой в сообществе, но мы считаем его сильным кандидатом на роль серверного языка, потому что:

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

Связанная статья: Quire получил сертификат ISO для защиты ваших данных — тот же упор на надёжность и безопасность серверной части спустя годы проявился в официальной сертификации защиты данных Quire.

Какие библиотеки Quire использует в стеке Dart?

В Quire около 30 импортируемых библиотек: 10 из них от сообщества, остальные выпущены командой Dart. Если вы эксперт по Dart, то, возможно, ждёте увидеть AngularDART и Polymer.dart, но на самом деле их здесь нет.

Мы не используем AngularDART, потому что:

  • Нам нужен тонкий контроль над DOM.
  • Мы проектируем структуру клиента по собственным архитектурным принципам, а их парадигма отличается от логики Angular.
  • Когда мы изучали AngularDART, он сильно увеличивал размер скомпилированного JavaScript, но с тех пор это значительно улучшили.

Мы не используем и Polymer.dart, потому что:

  • Из-за инкапсуляции и переназначения событий ShadowDOM не уживается с фреймворками на селекторах, например с Bootstrap.
  • Нельзя внедрить стиль извне ShadowDOM. Если сторонний набор компонентов сделан на Polymer, пользователям почти невозможно изменить его внешний вид. Обновление: с декабря 2013 года менять внутренние стили снаружи можно. См. Shadow DOM 201.

Что такое DQuery и Bootjack?

Основа нашего клиентского стека — DQuery и Bootjack, два открытых проекта, выпущенных командой Rikulo:

  • DQuery — частичный порт jQuery на Dart с упором на систему делегирования событий jQuery.
  • Bootjack — полный порт Bootstrap 3 с почти идентичными CSS и API.

Так мы построили стек приложения, чтобы использовать знания и навыки из мира JavaScript.

Что такое веб-сервер Stream?

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

Это не только продуктивнее, но и приятнее традиционной многопоточной модели. Масштабированием сервиса и HTTPS у нас занимается nginx, а запросы он передаёт в Stream. Благодаря такой архитектуре мы запускаем и останавливаем виртуальные машины Dart по отдельности и обновляем сервер, не прерывая работу пользователей.

Заключение

Работать с Dart нам нравится, и мы с нетерпением ждём новых интересных событий в его сообществе. И наконец, если вам любопытно, чего можно добиться на Dart, попробуйте наше приложение Quire. Оно бесплатное!

Читайте также: как Quire позже переосмыслили и пересоздали в версии Mark II.

Схема архитектуры Quire с реализацией клиента и сервера на Dart

Какие уроки сборки 2014 года актуальны в 2026-м?

Язык Dart и кодовая база Quire с момента написания статьи заметно изменились. Dart 2.0 (надёжная система типов) вышел в 2018 году, Dart 3.0 (null safety, records, patterns) — в 2023-м, а Flutter изменил ландшафт кроссплатформенного UI (мобильная команда Quire тоже перешла на него).

Но архитектурные решения, описанные выше, выдержали проверку временем лучше, чем можно ожидать.

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

Класс ошибок, о котором говорил Саймон (бесчисленные мелкие ошибки), по-прежнему попадает на продакшен из нетипизированного стека.

Общие модели данных клиента и сервера по-прежнему экономят работу. Проблема дублирующихся моделей (одна на JS, другая на Python, молча расходящиеся) в 2026 году такая же, как в 2014-м. Решение (один язык от начала до конца) тоже прежнее. Команды на TypeScript и Node строят архитектуры в стиле Quire именно поэтому.

Цикл событий по-прежнему лучше многопоточности для веб-серверов. Node, Deno, Bun и собственный подход Stream в Dart сходятся в этом. Спор возобновляется каждые несколько лет и приходит к тому же выводу.

Tree shaking важен ещё больше. В 2014 году компилятор Dart с tree shaking выделял язык среди других. В 2026-м это обязательный минимум для любой сборки под JS: его умеют Webpack, Rollup, esbuild и Vite. Урок остаётся в силе, просто инструменты догнали.

Проблемы ShadowDOM из 2014 года в основном решены. Polymer.dart признали устаревшим, а Web Components повзрослели. Причины, по которым Саймон отказался от Polymer (внедрение стилей, совместная работа с фреймворками), в итоге решила сама платформа.

Общий принцип (не брать передовые фреймворки в ядро продакшен-приложения) в 2026 году применим к любому нынешнему «модному» фреймворку.

Связанная статья: Как это сделано: мобильная временная шкала на Flutter — рассказ о том, как мобильная команда Quire применила Flutter, создавая просмотр временной шкалы.

Какие ошибки чаще всего допускают при выборе языка для большого веб-приложения?

Выбор языка для приложения, которое проживёт много лет, — одно из немногих инженерных решений, которые дорого отменять. Когда команды ошибаются, повторяются четыре сценария.

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

Размер сообщества важен, но не единственный фактор.

2. Выбор стека с разными языками на клиенте и сервере. Каждая дублирующаяся модель данных — будущая ошибка. Команды, у которых Ruby на сервере и JS на клиенте, платят эту цену всегда. Команды с одним языком на обеих сторонах (Dart, TypeScript, Kotlin) избегают целого класса ошибок.

3. Ставка на передовые технологии до их стабилизации. Команда Quire в 2014 году отказалась от AngularDART и Polymer.dart, потому что у обоих оставались открытые архитектурные вопросы. AngularDART резко сменил курс, а Polymer признали устаревшим.

Правило (пусть фреймворк докажет себя в продакшене в большом масштабе, прежде чем строить на нём ядро приложения) в 2026 году тоже работает для любого «модного» фреймворка.

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

Ставка команды Quire на Dart в 2014 году выглядит разумной не потому, что Dart стал доминирующим (этого не случилось), а потому, что лежащие в её основе критерии (типизация, общий код клиента и сервера, цикл событий, tree shaking) за следующее десятилетие стали консенсусом в индустрии.

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

Присоединяйтесь к 100 000+ команд, использующих программное обеспечение для управления проектами Quire — зарегистрируйтесь бесплатно

Часто задаваемые вопросы

На каком языке написан Quire?

На Dart, и на клиенте, и на сервере. Приложение содержит около 54 000 строк кода на Dart и около 30 импортируемых библиотек.

Почему Quire выбрал Dart, а не JavaScript или TypeScript?

Строгая типизация, модель наследования на классах и один язык на клиенте и сервере. Кроме того, Dart предлагал несколько современных возможностей раньше, чем их стали широко поддерживать в JavaScript.

Почему Quire не использовал AngularDART или Polymer.dart?

Команде требовался более тонкий контроль над DOM, чем позволяла привязка AngularDART, а архитектурные подходы Quire не совпадали с логикой Angular. От Polymer.dart отказались, потому что ShadowDOM в то время конфликтовал с селекторами в стиле Bootstrap. Позже Polymer признали устаревшим, что подтвердило решение.

Что такое DQuery и Bootjack?

Открытые библиотеки команды Rikulo (инженерной команды Quire). DQuery — частичный порт jQuery на Dart с упором на делегирование событий. Bootjack — полный порт Bootstrap 3 с почти идентичными CSS и API.

Что такое Stream в стеке Quire?

Веб-сервер Quire, написанный на чистом Dart, по модели цикла событий. Маршрутизация, фильтры и серверный MVC встроены. Quire запускает Stream за nginx, который отвечает за масштабирование и HTTPS.

Simon Pai
Co-Founder at Bookshow