Практически каждый человек сталкивается с ведением какого-либо учета, сбором и анализом данных: от использования таблиц в экселе до работы с данными в клиент-банковском приложении. Повсеместно для такого учета используются различные системы управления базами данных (СУБД).
В статье я хотел бы рассказать о своем пути поиска такой системы.
Сталкиваясь подобными СУБД (клиент-банки, системы ФСЗН, расчетно-кассовые приложения) с точки зрения пользователя, я всегда удивлялся, почему даже крупные компании с достаточными финансовыми возможностями часто не могут реализовать действительно удобный интерфейс в своих приложениях.
Очевидно, чтобы интерфейс приложения был удобным, он должен разрабатываться на базе хорошо продуманной реализации набора типовых операций над данными. Отсутствие подобного фундамента либо негативно сказывается на качестве программного продукта, либо существенно увеличивает время его разработки.
1С: Предприятие
В 2003 году, когда у меня возникала необходимость в приложениях для ведения любого рода учета, я писал конфигурации под 1С, которые обычно выглядели так:
На тот момент в этом подходе меня все устраивало. Однако через некоторое время я столкнулся с потребностью в сетевом доступе к системе учета. Проанализировав цены на серверные части 1С, я понял, что для меня они слишком высоки, и стал искать альтернативу, желательно бесплатную и с открытым исходным кодом.
Django Admin
После анализа и изучения отзывов я выбрал фреймворк Django с его генератором admin-интерфейсов. При этом пришлось перенести некоторые идеи, заложенные в 1С, на код Python. В итоге получались примерно такие интерфейсы:
Однако, при больших объемах данных стали проявляться недостатки такого подхода.
Во-первых, интерфейсы Django Admin обладают очень ограниченным функционалом и удобство их создания сходит на нет, если нужен функционал, не предусмотренный Django. В таком случае приходится ковыряться в коде Django и искать место, куда можно было бы внедрить свой код, наследуя классы или переделывая шаблоны. Такие модификации носят несистемный характер и через некоторое время к ним трудно возвращаться и вспоминать, как они работают (что приходилось делать, в частности, после обновления очередной версии Django).
Во-вторых, работать с создаваемыми Django Admin интерфейсами было не достаточно удобно, ввод данных был затруднителен и не оперативен. Хотелось интерактивности хотя бы на уровне 1С, чтобы интерфейс не перегружал страницу каждый раз, когда отправляются данные, а использовал такие технологии, как Ajax или WebSocket.
MySQL, Navicat и другие
В результате пришлось полностью отказаться от использования модуля Django Admin и клиентскую часть писать самостоятельно на JavaScript с использованием вышеуказанных технологий. Так был решен вопрос с интерактивностью, но время, которое стало уходить на создание интерфейсов, было неоправданно большим. Иногда, чтобы сэкономить время, для сбора и анализа данных я использовал чистый Mysql с клиентской частью в виде Navicat. Как оказалось, благодаря триггерам и видам, это не самое плохое решение, а огромное число задач решаются таким образом довольно просто (что не удивительно, ведь, согласно википедии, Mysql и создавался первоначально для решения подобных задач).
Критерии оптимального инструмента разработки СУБД
Со временем я сформулировал для себя перечень субъективных пожеланий к инструменту для разработки СУБД. Он должен:
- Позволять быстро создавать интерфейсы для работы с данными.
- Предлагать не только механизмы взаимодействия объектов между собой, но и механизмы отображения этих взаимодействий в интерфейсе.
- Предоставлять возможность выбора: разрабатывать интерфейс либо его часть быстрее (но менее гибко) либо функциональнее (но медленней).
- Работать с различными источниками данных.
- Быть кроссплатформенным и иметь открытый исходный код.
Ничего, что бы отвечало этим требованиям, я найти не смог. Поэтому решил написать свой фреймворк, решающий поставленные задачи. В качестве основных технологий были выбраны TypeScript в связке с Angular2. При этом фреймворк проектировался так, чтобы его можно было использовать для разработки приложений не только с применением Angular, но и с помощью других JavaScript-библиотек. Хотелось бы поделиться тем, что получилось: возможно, кому-то пригодится.
Структура нового фреймворка
Фреймворк заточен на быстрое создание интерфейсов для СУБД. Он состоит из нескольких частей (модулей). Некоторые могут использоваться отдельно, некоторые — только совместно с остальными.
Модуль core содержит механизмы описания моделей, взаимодействия объектов (записей) данных между собой, механизмы описания запросов к базе данных. Модуль core обращается к источникам данных через модуль backend.
Модуль backend — это прослойка между модулем core и базой (источником) данных. В качестве источника данных может выступать как непосредственно сервер баз данных, вроде SQL, так и прослойка для доступа к моделям других фреймворков, таких как Django или Sequelize.
Модуль model-ui отвечает за генерацию интерфейса: он визуализирует данные, предоставляемые модулем core, используя элементы управления, предоставляемые модулем ui.
Модуль ui содержит базовые элементы управления, которые используются модулем model-ui при генерации интерфейса. Эти элементы могут использоваться также и независимо от фреймворка.
Модуль windows-manager управляет контейнерами для отображения пользовательских интерфейсов. В зависимости от типа windows-manager приложения можно разворачивать как на компьютерах, так и на мобильных устройствах.
Модели и работа с данными
Особенностью фреймворка, в отличие от того же Django, является то, что для описания списков записей и самих записей используются разные типы моделей: ListModel и RecordModel. Такой подход позволяет в списках отображать записи не только от одной, но и от разных моделей, а также нередактируемые строки (являющиеся, например, результатом работы над этими записями).
Хоть фреймворк и содержит необходимые для этого механизмы, описывать модели при разработке приложения не требуется. Модуль backend автоматически формирует внутренние модели на основании тех, которые уже существуют и описаны в других средах (таких как Django, Sequelize, SQL и иные).
У фреймворка есть сходства работы с Django. Например, классы для работы с выборками и запросами (QuerySet и Query) являются эквивалентами одноименных классов Django, адаптированными из кода Python в код TypeScript. Например, для выборки данных из источника данных необходимо написать примерно следующий код:
backend
.getListModel('product')
.getQueryset()
.filter({name__icontains: 'Штаны'})
.orderBy('-price')
.limit(0,10)
.getRows()
.subscribe(products => {…});
Ещё одна отличительная особенность фреймворка — поддержка виртуальных полей. Когда изменяется виртуальное поле, оно может менять реальные поля объектов, а когда меняются значения реальных полей, могут изменяться и виртуальные. Что-то похожее есть и в Django, когда через объект мы имеем доступ к данным, не хранящимся в базе данных в том виде, в котором они доступны в этом объекте,— это получение ссылающихся на объект других объектов через свойство xxxxxx_set либо получение доступа к объекту через свойство, когда в базе данных хранится лишь id этого объекта.
На иллюстрации ниже поля product_id и product_name — реальные, а поле product — виртуальное.
Во фреймворке реализована «ленивая загрузка зависимых записей». В отличие от Django, здесь разработчик может решать, в каких случаях этот механизм лучше не применять, а получать данные сразу, тем самым уменьшая количество запросов между клиентом и сервером. Так, в примере выше у продукта product есть поле supplier, которое ссылается на поставщика. По-умолчанию, поставщики будут запрашиваться из базы данных только при обращении к полю product['supplier']. Однако, если вышеприведенный пример модифицировать следующим образом: ...getRows('supplier').subscribe(products => {…}); — то каждый продукт из списка products уже будет содержать данные о поставщике и при обращении к ним не будет происходить запроса к базе данных.
Интерфейс
Для ускорения разработки приложений фреймворк позволяет использовать встроенный интерфейс на базе модулей model-ui и ui. Это удобно, когда нет потребности в специфическом интерфейсе либо необходимо развернуть приложение максимально быстро. Однако, отказавшись от этих модулей, можно использовать и произвольный интерфейс (написанный, скажем, на базе Bootstrap или Angular Material).
Все элементы управления ввода данных в стандартном интерфейсе сделаны так, чтобы любой из них мог использоваться как в формах, так и в ячейках таблиц. Элементы также наделены свойствами, упрощающими работу с данными. Вот, к примеру, так выглядит текстовое поле.
При нажатии на кнопку расширения открывается увеличенное поле ввода: это удобно, когда ширина поля ввода меньше, чем помещенный в него текст. Такая ситуация может возникнуть при плотном расположении элементов на форме либо при редактировании данных в виде таблицы.
Если рассмотреть поле ввода числа, то в нем, кроме самого числа, можно вводить математические выражения.
Поле выбора другого объекта также имеет несколько способов ввода: оно позволяет выбирать объект как из выпадающего списка по вхождению слов, так и из отдельной всплывающей формы.
Что касается ошибок некорректности ввода, то они отображаются не текстом под элементом управления, а специальным маркером. Такой подход позволяет сохранять плотность элементов формы и не менять их положение при выводе ошибок. Аналогичным образом выводятся ошибки для ячеек таблицы с этим элементом управления.
Взаимодействие интерфейса и данных
Во фреймворке большое внимание уделено взаимодействию записей (объектов) между собой.
При изменении записи на форме она автоматически обновляется в списке.
Если же запись одновременно редактируется с разных компьютеров, то у пользователя появляется предупреждение.
В интерфейсе фреймворка также реализован механизм сортировки строк зависимых записей (если это предусмотрено моделью и модулем backend).
Приведенные примеры элементов интерфейса лишь малая часть из того, что заложено во фреймворке. Более детальное описание всех элементов и принципов работы интерфейса — тема для отдельных статей. Так же стоит повториться, что приводимые примеры интерфейсов не навязываются, а лишь предлагаются как основа и могут настраиваться по усмотрению разработчика.
Планы на будущее
Во фреймворке заложены основные возможности, которые я хотел в нем видеть. Он вполне работоспособен, но тем не менее ту стадию, на которой он находится в данный момент, я бы назвал только концептом.
Так, например, на данный момент существует backend, который позволяет фреймворку работать только с базой данных MySQL, из-за чего его можно запустить только на Electron. Также не реализован интерфейс для работы на мобильных устройствах и ряд других возможностей.
Ближайшие планы по развитию фреймворка:
- Реализовать механизмы объединения и группировок в запросах в классе Query.
- Добавить элементы управления для работы с объединениями и группировками.
- Разработать backend для преобразования объекта Query в json или xml, а также разработать серверную часть для работы с моделями Django.
- Реализовать механизм кеширования запросов к серверу данных.
- Воплотить в жизнь большое количество других идей.
Как попробовать?
Если у вас возникло желание опробовать фреймворк в работе, вы можете посмотреть пример приложения, написанного на нем. Его можно установить следующим образом:
git clone https://github.com/astoniocom/astonio-demo.git
Далее необходимо установить зависимости:
npm install
Затем открыть файл app.module.ts и настроить доступ к любой базе данных MySQL, после чего собрать приложение:
npm run watch
и запустить его командой:
npm run app
Фреймворк доступен на github.
Сайт проекта: astonio.com/framework/
Инструкция по созданию приложения на базе фреймворка: astonio.com/framework/quickstart/
Пример более сложного приложения, в котором реализованы базовые концепты разработки, находится здесь (инструкция по установке в файле README.md).
Автор: navar