Я работаю архитектором ПО, причём достаточно ленивым архитектором. Одна из моих задач — это проектирование модели данных для приложений. Ладно бы всё этим и ограничивалось, но нужна ещё и документация по модели для аналитиков и заказчиков, нужны SQL скрипты и Java‑код, реализующие эту модель данных, нужно постоянно актуализировать модель, если разработка ушла дальше, актуализировать документы, которые вечно устаревают.
Мы не ищем лёгких путей, поэтому запилили небольшой «инструмент моделирования», который упрощает всю эту работу. В этой статье я опишу как мы это сделали. Сразу предупреждаю, что инструмент наколеночный и я даже поленился упаковать его в нормальный продукт, который можно скачать и запустить. Плюс у нас активно используется Eclipse, но статья не про него, а про сам подход, можно использовать и другие инструменты.

Требования
Сначала немного конкретизирую задачу:
Нужен инструмент, в котором можно создавать модель данных и при этом задавать в ней все необходимые параметры. Не только технические названия сущностей и атрибутов на английском языке, но и их названия и описания на русском языке для документации, какие‑нибудь сложные ограничения уникальности и всё что угодно
Хотелось бы редактировать модель в разных нотациях: диаграмма, таблица, дерево, текстовое представление. Не ограничиваться только диаграммами или только DSL (типа PlantUML), а, например, использовать табличное представление для перевода модели на разные языки
Хотелось бы формировать документацию по нужному мне шаблону, например, с картинками в Mermaid, а не PlantUML. Чтобы можно было менять этот шаблон произвольным образом
Хотелось бы генерировать SQL скрипты. Причём достаточно специфические, чтобы все названия (таблиц, столбцов, ключей, ограничений) соответствовали нашему соглашению об именовании, чтобы для сложной иерархии наследования сущностей генерились правила или триггеры, которые будут удалять записи в подчиненных таблицах
Хотелось бы генерировать Java код, соответствующий нашим требованиям к документированию и т. д. Забегая вперед, в этой статье я поленился это делать, но для этого может использоваться такой же подход как и для генерации документов
В перспективе хотелось бы валидировать, версионировать, преобразовывать и кто знает что ещё делать с моделями
Варианты решения задачи
Есть много готовых или полуготовых инструментов, в которых можно создать модель данных и затем что‑нибудь сгенерировать из неё. Условно их можно разбить на следующие категории:
Большие, сложные и дорогие инструменты моделирования общего назначения (не только для моделирования данных) типа Rational Software Architect Designer, Visual Paradigm, Enterprise Architect, ARIS. Их можно использовать, если они уже применяются в компании. Но для задачи, описанной в начале статьи, большая часть их функциональности не нужна, а то что есть работает не так как нужно и запаришься это допиливать. Например, SQL скрипты или Java код генерятся не те или в модели данных не хватает поля «название сущности на русском языке»
Аналогичные инструменты, но бесплатные и попроще типа Papyrus, Archi. Они лучше в том плане, что не нужно убеждать руководство потратить миллионы денег на покупку лицензии, но в целом проблемы те же
Узкоспециализированные инструменты типа pgModeler, https://dbschema.com/. Всё те же проблемы с допилом. Плюс в перспективе мне не хотелось бы ограничиваться только моделями данных, а моделировать ещё много чего: в целом архитектуру ПО, API, требования, инфраструктуру и т. д.
Аналогичные, но веб‑инструменты типа https://dbdiagram.io/, https://www.drawdb.app/. Они немного проще, потому что не нужно ничего скачивать и устанавливать, плюс интерфейс более приятный, чем у десктопных инструментом моделирования. Но в целом проблемы те же, плюс хотелось бы инструмент без дурацких ограничений на количество моделей или пользователей в бесплатной версии. Хотелось бы инструмент, который можно развернуть локально в компании
Ещё более узкоспециализированные инструменты типа PlantUML, Structurizr, которые решают отдельные небольшие задачки. Из них можно как из конструктора собрать нужный инструмент моделирования, хранить модели в Git, написать немного кода для генерации документов, SQL, Java, а если кому‑то понадобится рисовать картинки, то прикрутить ещё сбоку draw.io. И вот он инструмент моделирования мечты! Но, нет, в области моделирования есть великие умы, например, в консорциуме Object Management Group, которые придумали тонны разных стандартов для описания метамоделей, для валидации моделей, для обмена моделями, для генерации документов из моделей, для преобразования моделей. На этих стандартах основана куча других стандартов — BPMN, UML, SysML. Не менее великие умы реализовали кучу всего для модельно‑ориентированной разработки. И вот бы теперь в 2025 году начать изобретать этот велосипед с нуля — вещь в себе, которая будет несовместима с другими инструментами моделирования. Короче мой перфекционизм мешает мне игнорировать международные стандарты в области моделирования, а лень мешает писать с нуля вещи, которые уже давно существуют
Кастомная разработка с нуля. Так исторически сложилось, что значительная часть инструментов моделирования или IDE написана на Java, более того сделана на базе платформы Eclipse (Rational Software Architect Designer, AnyLogic, SCADE, Archi, Papyrus, 1C EDT, DBeaver и много других). Java для инструментов моделирования это как Python для машинного обучения. Конечно же это не абсолютная истина и я встречал инструменты моделирования, написанные, под .NET или на C++ и это как‑раз полностью кастомная разработка с нуля. Безусловно такой подход даёт свои плюсы, но на мой взгляд здесь те же проблемы, что и в предыдущем варианте + нужен золотой прииск чтобы спонсировать тысячи человеко‑лет разработки
Фреймвоки для разработки инструментов моделирования типа десктопных Eclipse Modeling Framework, Sirius или облачных EMF Cloud, Sirius Web. И это наш путь, таким образом можно с небольшими затратами собрать ровно то, что хочется, без изобретения велосипедов и чтобы всё соответствовало OMGшным стандартам
Составные части инструмента моделирования
Собственно, вот он инструмент, в котором я реализовал большую часть своих хотелок. Вы можете установить Eclipse Modeling Tools, Eclipse Sirius, Xtext, Acceleo и QVTo. Импортировать проекты из этого репозитория и посмотреть как всё работает сами. Но вообще это не самая тривиальная задача. Цель данной статьи — рассказать про подход, совершенно не обязательно использовать именно Eclipse. Вы можете просто пролистать статью, посмотреть скриншоты. А в следующей статье я расскажу уже про гораздо более простой, но основанный на том же подходе, инструмент моделирования.
По количеству коммитов (1 штука) очевидно, что этот «инструмент моделирования» не используется в продакшене, я делал его просто в качестве демо для ArchDays 2024. И в принципе не зря. Моё «лекционное» выступление там было совершенно отстойным, я получил за него порцию хейта. Ну, простите меня, не всем дано быть великими ораторами, я старался, но получилось как получилось. Зато практическая часть, в которой я показывал этот моделер, понравилась людям на много больше.
Если мы сами не используем эту штуку, то что же мы тогда используем для моделирования данных, генерации документов, SQL, Java? В принципе примерно то же самое что и в репозитории. Просто для продакшена мне было лень делать кастомную нотацию для моделирования данных, мы используем Ecore (язык который очень похож на диаграммы классов UML). Также в продакшене не особо нужен диаграммный редактор моделей данных, но, спешу вас разочаровать, и текстовую нотацию для моделей мы там тоже не используем. Для нас удобнее всего древовидный редактор, особенно если модели достаточно большие, плюс табличный редактор для локализации модели данных на разные языки.
Ок, почему бы тогда и не показать штуку, которую мы реально используем, зачем делать искусственный пример? На ArchDays мне хотелось показать полный сценарий, демонстрирующий больше возможностей модельно‑ориентированного подхода. Если вы видите общую картину, то всегда сможете упростить некоторые вещи, например, не изобретать свою нотацию для моделирования данных, не добавлять ненужные вам формы представления моделей. В этой статье я постараюсь дать максимально полную картину.
1. Метамодель для логических моделей данных
Итак, мы хотим создать инструмент, который позволяет описывать модель данных и дальше генерить из этой модели разные вещи. Чтобы мы в принципе могли создавать какие‑либо модели сначала необходимо описать структуру этих моделей:
Из каких типов объектов модель будет состоять (в нашем случае это сущности и атрибуты)
Какими типами связей эти объекты могут связываться между собой (в нашем случае это отношение наследования, отношение композиции, просто ассоциация между сущностями)
Какие атрибуты могут быть у объектов и связей (как минимум название, описание и др.)
Всё это называется метамодель. Можно описать её неформально, как я это сделал в списке выше, а можно описать более формально:

Метамодель визуально похожа на диаграмму классов, но вообще это язык MOF (или его реализация Ecore) — специальный язык для описания языков моделирования. Например, на нём описаны BPMN, UML, SysML. Если вы хотите, чтобы с вашими моделями можно было работать не только в вашем проприетарном инструменте моделирования, но и во множестве других инструментов, то MOF вам в этом поможет.
В соответствии с нашей метамоделью каждая модель данных (DataModel) состоит из сущностей (Entity), отношений между ними (Relationship) и типов данных (DataType). Сущности состоят из атрибутов (Attribute). Отношения состоят из концов отношений (RelationshipEnd), причём отношения могут быть N‑арными, связывать более двух сущностей. Типы данных могут быть логическими (BooleanType), строковыми (StringType), числовыми (NumericType), датой (DateType), датой и временем (DateTimeType) или уникальным идентификатором (UuidType). Система типов здесь далека от идеала, у нас есть более совершенный вариант, расскажу о нём в следующей статье.
Также у всех этих объектов есть атрибуты. Например, у сущностей есть вид сущности (kind): основные данные (master data), справочные данные (reference data — отличается от основных данных только цветом на диаграммах), абстрактная сущность (abstract entity — для неё генерится абстрактный Java‑класс и разная магия в SQL скриптах) и набор атрибутов (attribute set — в Java коде для него генерится только интерфейс, а определения таблиц в SQL скриптах не генерятся вообще). У отношений есть вид отношения: ассоциация (association), композиция (composition), агрегация (aggregation).
И ещё есть такой кусочек этой метамодели:

DataModel, Entity, Attribute, Relationship и RelationshipEnd наследуют от NamedElement атрибуты название (name), описание (description) и список переводов названий и описаний на разные языки (localizations).
Такая метамодель позволяет указывать в наших моделях всю необходимую информацию для последующей генерации документов и кода.
Почему метамодель именно такая? На сколько она правильная? Для наших задач понадобилась такая метамодель, но вообще она могла бы быть какая угодно. Мы взяли за основу ER‑модели и дополнили их наследованием сущностей, разными видами сущностей и отношений, системой типов, локализацией. А могли бы взять за основу модели классов UML, Ecore, Anchor, объектно‑ролевые модели, онтологии, XML‑схемы и всё что угодно. Более того, вполне могли бы использовать их как есть без наших дополнений. Какую метамодель выбрать полностью зависит от ваших требований.
Если человек привык пользоваться каким‑то инструментом моделирования, то он просто создаёт в нём ER‑модель или модель классов UML и не задумывается о том, что за этими моделями стоят какие‑то метамодели и что они не являются данностью. Кто‑то когда‑то их придумал, но если они не соответствуют вашим требованиям, то вы вполне можете создать свою метамодель, свой язык моделирования.
Дополнительные статьи:
2. Древовидный редактор моделей
После того как мы описали метамодель Eclipse Modeling Framework автоматически предоставляет нам такой древовидный редактор моделей и формы свойств:

Видно, что структура дерева с примером конкретной модели данных соответствует метамодели: корневой элемент — это модель данных, на следующем уровне она состоит из сущностей и отношений, сущности содержат атрибуты и ещё у всего есть переводы на разные языки. Также снизу для каждого объекта в дереве доступна форма свойств, свойства определены в метамодели.
3. Диаграммный редактор моделей
В принципе, мне норм такой древовидный редактор. Особенно если в модели данных много сущностей, то запаришься двигать квадратики на диаграмме. Для документации можно из этой модели просто генерить PlantUML или Mermaid диаграммы (что мы будем делать чуть позже). Ещё можно наоборот из текста (например, PlantUML) генерить не только диаграммы, но и дерево (что мы тоже будем делать чуть позже). Но всё‑таки иногда хочется и диаграммный редактор.
Для этого в рамках проекта Eclipse есть много разных инструментов, в том числе Eclipse Sirius. Чтобы создать диаграммный редактор моделей вы можете описать в нём такую спецификацию диаграммных представлений:

В спецификации вы указываете какие слои будет содержать диаграмма. Для каждого слоя описываете с помощью каких фигур и связей на диаграмме будут отображаться объекты, отношения и атрибуты из модели. Например, здесь указано, что сущности должны отображаться на диаграмме в виде контейнеров со списком атрибутов внутри. Если это основная сущность, то она отображается с зеленым фоном, если справочная — то с синим. Бинарные отношения отображаются на диаграмме как связи. N‑арные отношения отображаются как ромбики, связанные с несколькими сущностями.
Также в спецификации указывается какие изменения должны вноситься в модель, когда вы перетаскиваете объекты с палитры инструментов на диаграмму, когда перетаскиваете атрибут из одной сущности в другую, переприсоединяете связи к другим объектам, удаляете или переименовываете объекты на диаграмме и т. д.
Иными словами Sirius позволяет описать декларативную low‑code спецификацию редактора диаграмм и в итоге с минимальными усилиями вы получаете такой редактор:

Основные сущности отображаются зеленым цветом, справочные — синим, абстрактные — белым, а вспомогательные наборы атрибутов отображаются с пунктирной границей. Отношения наследования отображаются стрелочкой, отношения композиции — связью с ромбиком, ассоциации — просто связью. Справа палитра инструментов.
Нужен вам редактор диаграмм или нет решайте сами, но для некоторых видов моделей (например, для моделей процессов) он вполне полезен.
Дополнительные статьи:
4. Формочный редактор моделей
Формы свойств, которые предоставляет Eclipse Modeling Framework из коробки, не слишком удобные (см. раздел 2 выше). Но в Sirius можно создать спецификацию и для форм:

Так выглядит итоговая форма:

Это уже на много удобнее, чем дефолтные формы, но, например, редактировать локализованные атрибуты так не очень удобно, потому что приходится прокликивать каждый объект на диаграмме или в дереве.
5. Табличный редактор моделей
Локализовывать модели гораздо удобнее в табличном редакторе. Для этого создаём в Sirius такую спецификацию табличных представлений:

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

В одной таблице видны значения атрибутов на всех языках, не нужно прокликивать каждый объект, чтобы посмотреть где значения не заполнены или где нужно что‑то изменить.
6. Текстовый редактор моделей
Все эти картинки, формочки, таблички для архитекторов и аналитиков. Но настоящим хардкорным программистам, а не всем этим смузихлёбам удобнее писать код. Ок, добавим текстовую нотацию для моделей. Для этого возьмем Xtext и опишем грамматику нашего DSL для моделирования данных. Приведу небольшой фрагмент:
grammar org.example.dm.xtext.TextualDataModel
with org.eclipse.xtext.common.Terminals
import "http://www.example.org/dm"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore
DataModel returns DataModel:
'DataModel'
name=ElementName
('{'
('@description' '(' description=Text ')')?
('@locales' '(' locales+=ID* ')' )?
localizations+=Localization*
entities+=Entity*
relationships+=Relationship*
dataTypes+=DataType*
'}')?;
Entity returns Entity:
'Entity'
name=ElementName
('[' kind=EntityKind ']')?
('::' generals+=[Entity|ElementName]+)?
('{'
('@description' '(' description=Text ')')?
localizations+=Localization*
attributes+=Attribute*
'}')?;
ElementName returns ecore::EString:
STRING;
Строка 1 — объявление грамматики для нашего DSL.
Строка 2 — импорт терминальных правил для парсинга чисел, строк в кавычках и т. д. В разных DSL эти правила обычно одинаковые, их можно переиспользовать.
Строка 4 — импорт метамодели, которую мы определили выше в разделе 1. В итоге код будет преобразовываться в абстрактное синтаксическое дерево. Структура этого дерева описывается метамоделью. Xtext может работать в двух режимах: сначала описываем метамодель и затем описываем для неё грамматику, либо сразу описываем грамматику и затем автоматически генерируем для неё метамодель. В данном примере мы пошли первым путём.
Строка 5 — импорт стандартной метамодели Ecore, в которой определены стандартные типы данных (строка, число,...).
Строки 7–17 — грамматическое правило для самой модели данных.
Строка 7 — название правила (используется для ссылок из других правил) и указание на то к объектам какого типа это правило относится.
Строка 8 — в коде описание модели данных будет начинаться с ключевого слова DataModel
.
Строка 9 — после ключевого слова DataModel
должно быть указано название модели данных. Название парсится с помощью правила ElementName
и сохраняется в атрибут name
модели данных.
Строки 10–17 — опциональный блок в фигурных скобках с описанием содержимого модели данных.
Строка 11 — для модели данных в коде можно указать ключевое слово @description
и затем в скобках указать описание, которое будет сохранено в атрибут description
модели данных.
Строка 12 — для модели данных в коде можно указать ключевое слово @locales
и затем в скобках указать перечень языков, на которые будет переводиться модель.
Строка 13 — список локализаций модели данных на разные языки.
Строка 14 — список сущностей в модели данных.
Строка 15 — список отношений между сущностями в модели данных.
Строка 16 — список типов данных.
Строки 19–28 — грамматическое правило для описания сущностей в модели данных. Оно очень похоже на предыдущее правило для моделей, не буду детально его описывать. Только в строке 23 интересное правило, описывающее ссылки на базовые сущности (или сущности от которых данная сущность наследуется).
Строки 30–31 — простое правило для парсинга названий моделей данных, сущностей, атрибутов,...
Возможно на первый взгляд всё это выглядит достаточно сложно, но вся грамматика занимает 150 строк. И в итоге мы получаем текстовый редактор для нашего DSL с такими фичами:
подсветка синтаксиса,
автодополнение,
автоформатирование (для этого придётся дописать ещё сотню строк),
навигация по коду (можно кликнуть по названию типа данных или названию базовой сущности и перейти к их определению),
древовидный outline для кода,
сворачивание/разворачивание блоков кода,
минимальные возможностями рефакторинга (можно переименовать сущность и переименуются все ссылки на неё),
поддержка Language Server Protocol,
возможностью преобразования абстрактного синтаксического дерева в код (полезно для генераторов кода).

На рисунке слева абстрактное синтаксическое дерево (по совместительству outline для навигации по коду), оно синхронизировано с текстовым редактором справа.
Этот пример показывает, что нет вообще никакой разницы между подходами «architecture as code» и «architecture as model». Просто есть модель, которая представляет собой древовидную структуру с атрибутами и горизонтальными связями. И есть много разных и совершенно равнозначных представлений этой модели в виде диаграммы, таблицы, дерева, текста. Не нужно выбирать между подходами «хватит писать код, давайте срочно рисовать картинки, сгенерим код из них» и «хватит рисовать картинки, давайте срочно писать код, сгенерим картинки из него». Этой дилеммы просто не существует.
Дополнительные статьи:
Введение в разработку предметно‑ориентированных языков (DSL) с помощью EMFText
Разработка парсера, кодогенератора и редактора SQL с помощью EMFText
7. Шаблон для генерации документов из моделей
Мы наконец создали все мыслимые редакторы для наших моделей. Теперь можно делать с этими моделями что‑то полезное, например, сформировать по модели документацию. Для описания шаблонов преобразования моделей в текст есть специальный DSL (стандарт — OMG MOF Model to Text Transformation Language, реализация — Eclipse Acceleo).
Так выглядит шаблон для генерации Markdown‑документов с Mermaid‑диаграммами (полная версия):
[comment encoding = UTF-8 /]
[module dmToMarkdown('http://www.example.org/dm')]
[import org::example::dm::acceleo::helpers /]
[template public dmToMarkdown(dataModel : DataModel)]
[comment @main/]
[file (dataModel.name.toUpperCamelCase() + '.md', false, 'UTF-8')]
# Описание модели данных «[dataModel.getRuName().toUpperFirst()/]»
```mermaid
classDiagram
[for (entity : Entity | dataModel.entities)]
class `[entity.getClassName()/]` ::: [entity.getClassKind()/]
[/for]
[for (entity : Entity | dataModel.entities)]
[for (general : Entity | entity.generals)]
`[general.getClassName()/]` <|-- `[entity.getClassName()/]`
[/for]
[/for]
[for (rel : Relationship | dataModel.relationships)]
`[rel.ends.entity->at(1).getClassName()/]` [rel.getEdge()/] `[rel.ends.entity->at(2).getClassName()/]`
[/for]
```
## Типы данных
| Тип данных | Название (англ.) |
| ---------- | ---------------- |
[for (dataType : DataType | dataModel.dataTypes)]
| [dataType.getRuName().toUpperFirst()/] | [dataType.name/] |
[/for]
[for (entity : Entity | dataModel.entities)]
## [entity.getClassName()/] ([entity.name/])
| Атрибут или ссылка | Название (англ.) | Тип данных | Мн. |
| ------------------ | ---------------- | ---------- | :-: |
[for (attr : Attribute | entity.getAllAttributes())]
| [attr.getPropertyName()/] | [attr.name/] | ['['/][attr.dataType.getRuName()/][']'/](#типы-данных) | [attr.getMultiplicity()/] |
[/for]
[for (end : RelationshipEnd | entity.getAllOppositeRelationshipEnds()->select(end | notEmpty(end.name))->sortedBy(getPropertyName()))]
| [end.getPropertyName()/] | [end.name/] | [end.entity.getLinkToEntity()/] | [end.getMultiplicity()/] |
[/for]
[/for]
[/file]
[/template]
[query public getEdge(rel : Relationship) : OclAny =
if rel.kind = RelationshipKind::COMPOSITION then '*--'
else if rel.kind = RelationshipKind::AGGREGATION then 'o--'
else '--' endif endif /]
Не буду подробно останавливаться на каждой строке, рассмотрим только основные вещи:
Строка 2 — объявление шаблона и импорт метамоделей (метамодель была определена выше в разделе 1). Шаблон строго типизирован. Если вы измените метамодель, например, переименуете атрибут name в label, то шаблон будет невалиден и нужно будет его актуализировать. На мой взгляд это достаточно удобно.
Строки 6–47 — основная часть шаблона, в которой сначала генерятся Mermaid‑диаграммы, затем Markdown‑таблички. Для запросов к моделям используется язык Acceleo Query Language, который является вариацией языка OMG Object Constraint Language. AQL/OCL — это языки запросов к моделям как SQL — язык запросов к реляционным базам данных.
Строки 49–52 — пример вспомогательного запроса к модели.
На выходе получаем такой документ:

8. Метамодель для физических моделей данных
Теперь попробуем сгенерировать из модели данных SQL‑скрипты. Можно было бы пойти тем же путём, что и для генерации документов — создать Acceleo‑шаблон. Но проблема в том, что этот шаблон получится слишком сложным, потому что в нашей модели данных есть наследование сущностей, связи «многие ко многим». И всё это нужно сначала преобразовать в таблицы, а потом для таблиц уже сформировать SQL‑скрипт.
Поэтому вместо одного сложного шаблона, который делает кучу всего сразу, проще реализовать отдельно:
Преобразование сущностей, отношений и атрибутов в таблицы, столбцы и ключи
Шаблон для генерации SQL‑скриптов
Для этого нам понадобятся промежуточные физические модели данных. Опишем для них метамодель:

Эта метамодель, как и метамодель в разделе 1, описывает модели данных, но в совершенно других терминах. Вместо сущностей, отношений и атрибутов здесь база данных (Database), схема данных (Schema), таблица (Table), столбец (Column), разные виды ключей (Key), ограничения (Constraint), индексы (Index), правила (Rule), перечислимые типы данных (Enum).
Большинство сущностей, как и в метамодели в разделе 1, наследуют атрибут name от класса NamedElement. Для упрощения он скрыт на диаграмме.
Мы можем расширять эту метамодель как угодно, например, добавить триггеры.
Также мы можем описать разные формы представления этих моделей (диаграмма, таблица, дерево, текст).
9. Преобразование логических моделей данных в физические
Теперь можно реализовать преобразование ER‑моделей в реляционные модели. Для этого можно использовать любой язык программирования или специализированный DSL для преобразования моделей, например, QVTo.
Приведу здесь небольшой фрагмент преобразования (в полном преобразовании порядка 300 строк):
import StringHelpers;
import DmHelpers;
modeltype DM uses 'http://www.example.org/dm';
modeltype RDM uses 'http://www.example.org/rdm';
transformation DmToRdm(in dm : DM, out rdm : RDM);
main()
{
dm.rootObjects()[DM::DataModel]->toSchema();
}
mapping DM::DataModel::toSchema() : RDM::Schema
{
name := self.getSchemaName();
tables := self.entities->toTable();
tables += self.relationships->toTable();
}
Строки 1–2 — импорт библиотек со вспомогательными запросами к моделям.
Строки 4–5 — импорт метамоделей. QVTo преобразования, как и Acceleo шаблоны, строго типизированные.
Строка 7 — объявление преобразования моделей,описание типов входных и выходных моделей.
Строки 9–12 — точка входа в преобразование, которая ищет в входные ER‑модели и преобразует их в реляционные схемы данных.
Строки 14–19 — правила отображения ER‑моделей в реляционные схемы данных.
Строка 16 — в соответствии с соглашением об именовании преобразуем название ER‑модели в название схемы данных и записываем его в атрибут name результирующей модели.
Строки 17–18 — ищем в ER‑модели сущности и отношения и преобразуем их в таблицы.
Остальное преобразование состоит из аналогичных правил отображения.
После запуска преобразования получаем такую реляционную модель данных.

Если сравнить эту модель с ER‑моделью из раздела 3, то видно, что для сущностей PersistentEntity и NamedEntity таблицы не сгенерированы, вместо этого атрибуты из них добавлены в дочерние сущности. Для связи «многие ко многим» между бизнес‑требованиями и системными требованиями добавлена промежуточная таблица.
Дополнительные статьи:
10. Шаблон для генерации SQL скриптов
Преобразовать такую реляционную модель в SQL‑скрипт — уже достаточно тривиальная задача. Приведу здесь часть шаблона (вот, ссылка на полный шаблон):
[comment encoding = UTF-8 /]
[module rdmToSql('http://www.example.org/rdm')]
[template public rdmToSql(db : Database)]
[comment @main /]
[for (schema : Schema | db.schemas)]
[schema.rdmToSql()/]
[/for]
[/template]
[template public rdmToSql(schema : Schema)]
[comment @main /]
[file (schema.name.toUpperCamelCase() + '.sql', false, 'UTF-8')]
CREATE SCHEMA IF NOT EXISTS [schema.name/];
[for (enum : Enum | schema.types)]
CREATE TYPE [schema.name/].[enum.name/] AS ENUM (
[for (lit : EnumLiteral | enum.literals) separator (',\n ')]'[lit.name/]'[/for]
);
[/for]
[for (table : Table | schema.tables)]
CREATE TABLE [schema.name/].[table.name/] (
[for (element : NamedElement | table.getTableElements()) separator (',\n ')][element.getTableElement()/][/for]
);
[for (index : Index | table.indices->select(unique)) before ('\n')]
CREATE UNIQUE INDEX [index.name/] ON [index.table.schema.name/].[index.table.name/] USING [index.type/] ([index.expressions->sep(', ')/]);
[/for]
[for (index : Index | table.indices->select(not unique)) before ('\n')]
CREATE INDEX [index.name/] ON [index.table.schema.name/].[index.table.name/] USING [index.type/] ([index.expressions->sep(', ')/]);
[/for]
[for (rule : Rule | table.rules) before ('\n')]
CREATE RULE [rule.name/] AS ON DELETE TO [schema.name/].[table.name/] DO [rule.statement/];
[/for]
[/for]
[/file]
[/template]
На выходе получаем такой SQL‑скрипт:
CREATE SCHEMA IF NOT EXISTS software_architecture;
CREATE TABLE software_architecture.software (
id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
description VARCHAR(1000),
CONSTRAINT software_pk PRIMARY KEY (id)
);
CREATE TABLE software_architecture.subsystem (
id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
description VARCHAR(1000),
parent_id UUID NOT NULL,
software_id UUID NOT NULL,
CONSTRAINT subsystem_pk PRIMARY KEY (id),
CONSTRAINT parent_fk FOREIGN KEY (parent_id) REFERENCES software_architecture.subsystem(id),
CONSTRAINT software_fk FOREIGN KEY (software_id) REFERENCES software_architecture.software(id)
);
Общая схема инструмента моделирования
Теперь можно собрать этот пазл, вот, общая схема (нумерация блоков соответствует нумерации разделов выше):

Вместо Eclipse, Sirius, Xtext, Acceleo и QVTo вы можете использовать что угодно, но сама схема от этого не изменится. Суть модельно‑ориентированного подхода заключается в том, что вы раскладываете вашу задачу на модели, метамодели, представления моделей, преобразования моделей (модель‑модель, модель‑текст) и затем относительно легко и быстро её решаете.
Итоги
Я привёл пример задачи (моделирование данных, генерация документов и кода), которую можно решить с помощью модельно‑ориентированного подхода и показал пример решения этой задачи с помощью Eclipse Modeling Framework, Sirius, Xtext, Acceleo, QVTo
Но вообще не обязательно использовать именно эти инструменты. Суть модельно‑ориентированного подхода заключается в том, что мы смотрим на любую задачу с точки зрения моделей, метамоделей, представлений моделей, преобразований моделей (модель‑модель или модель‑текст)
Если сложно реализовать преобразование в один шаг (например, из ER‑моделей с наследованием и связями «многие ко многим» генерировать SQL скрипты), то можно реализовать его в несколько более простых шагов через промежуточные модели. Причём эта идея достаточно общая, весь процесс разработки основан на этом принципе: сначала заказчик в целом описывает что ему нужно, затем бизнес‑аналитики погружаются в эту предметную область, описывают функциональные блоки будущей системы и бизнес‑требования, системные аналитики строят более детальные и технические модели, разработчики реализуют всё в коде — таким образом через несколько промежуточных моделей мы решаем нашу задачу. И вообще модельно‑ориентированный подход, на мой взгляд, выходит далеко за рамки генерации кода из моделей и вообще за рамки ИТ, но это тема для отдельной статьи
Модельно‑ориентированный подход не сводится к рисованию диаграмм, у моделей могут быть какие угодно формы представления, включая текстовые. Например, для языка SysML 2 есть две официальные нотации: диаграммная и текстовая. Или для UML есть официальная диаграммная нотация и неофициальный PlantUML. Нет особой разницы между подходами «something as code» и «something as model»
Что дальше
Я абсолютно уверен, что даже если вы дочитали до этой строчки, то наверняка вам капец как лень качать Eclipse, что‑то пробовать в нём делать. Мне самому было бы лень :) Но что мне точно НЕ лень — это запилить идеальный инструмент моделирования, в котором с одной стороны будет поддерживаться вся эта функциональность, описанная в статье, а с другой стороны он будет проще чем draw.io или PlantUML. И об этом я расскажу в следующей статье.