
Множество команд сталкиваются с решением вопроса по управлению контентом на различных платформах — от веб-сайтов до мобильных приложений и интернет-магазинов. При этом, как правило, отсутствует единый инструмент, позволяющий эффективно создавать, изменять, хранить и передавать контент для отображения на всех платформах.
Кто-то приходит к самостоятельной разработке инструмента по управлению контентом с нуля. Другие смотрят в сторону готовых CMS решений (content management system/систем управления контентом), которые зачастую позволяют сэкономить время и затраты.
Меня зовут Лена. Мы в команде Enablement Platform провели пилот по сравнению нескольких популярных решений для CMS, накопленный материал показался интересным и полезным для веб-разработчиков, в результате появилась эта статья.
Поделюсь с какими проблемами мы столкнулись, какой подход выбрали для управления контентом и доставке данных до витрин. Если бы такой материал мы встретили раньше, он бы сэкономил нам много времени.
Оглавление
1. Вступление
2. Требования и критерии выбора
3. Классы CMS
4. Ситуация на рынке Headless CMS
5. Сравнение Headless CMS
6. UI систем и примеры моделирования
7. Заключение
1. Вступление
Решения класса CMS существенно различаются зрелостью и функциональностью. То, что отлично подходит для простого персонального блога или небольшого сайта, может не справиться с потребностями сложных корпоративных витрин. Для снятия этих ограничений возможностей конфигурирования недостаточно и может потребоваться существенная доработка CMS. Например, ограничения в моделировании данных, подходы к управлению и отображению различных типов контента, механизмы к ограничения доступности контента и т. д. Большинство CMS ориентированы на относительно простую модель данных с ограниченным типом связей между сущностями.
В МТС есть множество различных пользовательских витрин, которые позволяют клиентам узнать о продуктах компании:
- сайте;
- приложении;
- интернет-магазине;
- лендингах и т. п. (например, premium.mts.ru, kion.ru, music.mts.ru).
И на текущий момент в экосистеме МТС применяются различные инструменты для управления контентом. Возникла идея — проанализировать текущий рынок CMS и провести «пилотирование» выбранных инструментов. По результатам пилота оценить возможность оптимизации и унификации подходов к управлению контентом и передаче данных витринам.
Даже официальные обзоры на сайтах CMS, где решения сравнивают себя с конкурентами, содержат ошибки и неточности.
Поэтому сделать выбор только на основании описаний или обзоров, статей (в том числе на Хабре) и тематических рейтингов не удалось.
Задача вносила корректировку к требованиям. В первую очередь нам были интересны решения, которые позволяют работать не с простым контентом (как, например, статьи на сайте), а со сложной иерархичной структурой данных, связанными сущностями, вложенными компонентами и т. д.
2. Требования и критерии выбора
При выборе CMS учитывались общие требования к функциональности, возможности кастомизации и специфические потребности команды, обусловленные используемым ИТ-ландшафтом.
Основные критерии выбора:
- Open Source решение и лицензии без дополнительных ограничений, в том числе на использование в коммерческих целях — возможность кастомизации под потребности компании, использование в составе других продуктов;
- Headless CMS — ключевое внимание на управление данными, уровень представления реализуется внешними витринами и зависит от их специфики и потребностей;
- максимально близкое команде продукта по стеку — backend предпочтительно C#, СУБД MongoDB и PostgreSQL;
- поддержка GraphQL API — у нас активно внедряется данный подход межсистемного взаимодействия, поддержка GraphQL API упростит интеграцию решения в общий ИТ-ландшафт;
- возможность создавать свою модель данных — постоянно запускаются новые продукты, у каждой группы продуктов свои особенности и атрибутивный состав, поэтому важно иметь гибкие возможности по моделированию;ПримечаниеПоддержка иерархичной модели данных, вложенных элементов, в одной родительской сущности можно создать несколько дочерних.
- поддержка SSO (Single sign-on) — данный механизм является стандартом в компании, позволяет пользователю не тратить время на авторизацию в каждой системе;
- работа с мультимедийным контентом: управление изображениями в различных разрешениях, видео контентом, бинарными документами и т. п.;
- наличие локализации — для перевода контента на различные языки;
- наличие версионирования контента — для отслеживания истории изменений и возможности отката к прошлым версиям;
- возможность управления workflow — управлением возможными статусами у сущностей, включая поддержку отложенной публикации контента;
- удобство использования;
- импорт контента из внешних источников — для упрощения процесса миграции на новую систему;
- производительность ~2000 RPS;
- информирование внешних систем об изменении данных;
- возможность генерации API по созданной модели — для сокращения трудозатрат на разработку;
- ролевая модель пользователей и API — для разграничения прав доступа к управлению контентом;
- мультитенантность — возможность управления контентом в одной CMS для нескольких команд, где у каждой из них свое пространство.
3. Классы CMS
Традиционные CMS
Позволяют как управлять данными, так и отвечают за их представление, поддерживают темы и т. д. На базе таких решений вы можете создать полностью готовый сайт с дизайном, фронтом и данными. Легко и просто наполнять его.
Типичные представители: WordPress, Joomla, Drupal.
Headless CMS
Предоставляет UI для управления моделью данных, UI для управлениям самими данными и основное — API реализующее набор CRUD операций на данными.
Именно через API происходит потребление этих данных внешними витринами и каналами.
Выделяют следующие типы решений:
- API Driven — используют СУБД для хранения данных (может использовать объектное хранилище для бинарных файлов);
- Git-based — используют Git репозиторий для хранения контента, как правило подходят для для простых задач, с небольшой частотой изменений.
Некоторые решения не предоставляет UI для управления моделями, модель описывается программным кодом.
4. Ситуация на рынке Headless CMS
Нас интересовали именно Headless-решения, потому что они позволяют моделировать контент с целью дальнейшего отображения на различных платформах. В нашем случае это вышеупомянутые витрины данных (сайт, мобильное приложение, интернет-магазин, лендинги и т.п.)
Бэкенд при данном подходе существует отдельно от фронта, что дает возможность использовать один бэкенд для хранения и передачи контента для отображения на всех вышеуказанных витринах данных. Это позволяет централизовать ведение данных по продуктам, а задачу их отображения и верстки оставить на стороне витрины. Потенциально мы даем необходимую гибкость продуктовым командам.
На рынке сейчас представлено большое количество Headless CMS. Когда мы делали выбор, какие из систем хотим «пощупать» (установить и попробовать смоделировать данные на примере нескольких продуктов), в первую очередь обращали внимание на популярность системы.
Поэтому первой начали рассматривать Strapi, которая является лидером на рынке и занимает первые места в многочисленных обзорах. В процессе столкнулись с серьезными багами и ограничениями, необходимостью глобальной доработки (включая бэкенд и модель СУБД). Несмотря на то, что в нашей команде есть JS-разработчики, трудно было переключить их от бизнес-задач к полноценной доработке бэка системы для продолжения пилота.
Мы решили посмотреть альтернативные Headless CMS на стеке .NET C#. При этом уже понимали, какие возможности нужны в первую очередь и на чем нужно сфокусироваться:
- поддержка использования компонент данных (набор элементов разных типов);
- массивы компонент;
- различные типы связи;
- набор UI контроллов для удобного наполнения данными.
Под наши запросы подошли Squidex и Orchard Core.
В процессе рассматривались и тестировались другие системы, но детально не анализировались, так как не удовлетворяли ключевым критериям. Как правило, были ограничения в моделировании данных.
Отпал ряд решений, которые не предоставляют UI для управления моделью, и модель задается прямо в коде.
В обзоре ниже представлены ключевые характеристики и особенности выбранных Headless CMS.
5. Сравнение Headless CMS
Squidex
Сайт | squidex.io |
Сообщество/поддержка | 2k stars Watchers 100 Forks 402 Contributors 60
Примечание
|
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | MIT license. |
Версия на момент проведения анализа | 7.7.0 (01 Jul 2023) |
Репозиторий | github.com/Squidex/squidex |
Стек (frontend) | TypeScript / Angular / webpack / Node.js (development only — используется для сборки фронта)
Примечание
NodeJS нужна для разработки, если вы планируете дорабатывать UI админки на Angular. Все скрипты UI пакуются через webpack, а он использует NodeJS.
|
Стек (backend) | C# / .NET 7 / ASP.NET Core |
СУБД | MongoDB |
Наличие GraphQL API |
![]() ![]() ![]() ![]() |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Поддержка SSO-авторизации |
![]() |
Особенности работы с мультимедийным контентом:
|
1. Изображение загружается с исходными параметрами, есть возможность тегировать изображения. В API есть возможность передать точные размеры и тип файла. 2. Да. |
Наличие локализации | Да.![]() ![]() |
Поддержка версионирования. Рассматривали:
|
![]() ![]() ![]() |
Возможность управления flow (статусы у сущностей).
|
![]() ![]() ![]() |
Удобство использования |
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Особенности | В структуре JSON есть излишняя вложенность (с тем же столкнулись и в Orchard):
В GraphQL также:
Причина — реализация партиционирования (partitioning, используется для локализации).
Примечание
The Content API usually returns one object per schema field where the keys are the languages (or iv for non-localized) fields and the values are the actual field value.
Key is iv, which stands for «invariant». docs.squidex.io/02-documentation/concepts/localization#how-to-use-the-api Для REST можно использовать заголовок X-Flatten Header, которые сделает плоскими данным с единственным значением. Для GraphQL можно использовать объект flatData:
|
Импорт контента из внешних источников | При создании новой схемы в UI есть возможность импорта JSON-схемы,
пример
sq content export myschema --format=JSON sq content import myschema myschema_2020-05-20-09-14-34.json --format=JSON Импорт/экспорт данных с помощью CLI (подробнее тут: docs.squidex.io/02-documentation/developer-guides/automation-tools): |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | AssetStore:
|
Нефункциональные требования.
|
Можно написать свой плагин для кэша. Есть механизмы кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Предусмотрена расширяемость с помощью плагинов. |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | Генерация OpenAPI спецификации по модели и базовым методам. Встроенный GraphQL браузер и документация по API. |
Ролевая модель пользователей и API | Есть различные роли и можно добавлять свои:![]() |
Мультитенантность | Можно создавать пространства для различных команд: ![]() |
Strapi CMS
Сайт | strapi.io |
Сообщество/поддержка | 54.9k stars Watchers: 661 Forks: 6.7k Contributors 895
Примечание
Данные с github.com/strapi/strapi
|
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | MIT Expat для Публичной версии. |
Версия на момент проведения анализа | 4.11.7 (19 Jul 2023) + плагины — установлена 21.07.2023 |
Репозиторий | github.com/strapi/strapi |
Стек (frontend) | JS/TypeScript Angular |
Стек (backend) | JS/TypeScript, Nodejs |
СУБД | PostgreSQL MySQL/MariaDB SQLite |
Наличие GraphQL API |
![]() ![]() |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов |
![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Поддержка SSO-авторизации | Есть плагин. |
Особенности работы с мультимедийным контентом:
|
1. Изображения сохраняются сразу в трех размерах.
Примечание
Есть несколько плагинов, которые можно применить, если дефолтные настройки нас не устраивают:
2. Да. |
Наличие локализации | Да. |
Поддержка версионирования. Рассматривали:
|
![]() Можно было бы создавать новые версии, используя функцию дублирования, но с учетом ее некорректной работы (не все поля дублировались, только простые, без компонентов) это проблематично. Для реализации версионирования нужны доработки. ![]() |
Возможность управления flow (статусы у сущностей).
|
![]() Также по ссылке есть пример, как использовать внешний автоматизатор n8n. ![]() |
Удобство использования |
![]()
|
Особенности | 1. Есть два режима запуска:
2. Слабая защита от «дурака» в UI можно изменить модель так, что система перестанет стартовать, и потребуется исправлять ошибки модели в исходных файлах проекта. Разработчикам (если они увидят нашу статью) было бы здорово обратить внимание на валидацию данных на UI (ограничение на количество символов в названиях объектов, запрет создания поля с именем «ID» и т. п.). |
Импорт контента из внешних источников | Есть плагин. |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | 1. В виде файлов в public/uploads на веб-сервере. 2. Коннекторы к объектным облачным хранилищам. |
Нефункциональные требования.
|
Есть плагин для кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Магазин плагинов. Кастомные типы данных, для сложных случаев можно использовать объект с типом JSON (потребуется реализовать UI для управления данными). |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | GraphQL playground с документаций по API (после установки плагина). |
Ролевая модель пользователей и API | Есть различные роли и можно добавлять свои:![]() |
Мультитенантность | Через разработку, из коробки решения нет, но есть возможности настройки, которые не тестировались: Strapi-v4 | refine Есть плагин (также не тестировали): strapi-plugin-multi-tenant — npm Package Overview — Socket |
Orchard Core
Сайт | orchardcore.net |
Сообщество/поддержка | 6.7k stars Watchers 339 Forks 2.2k Contributors 242 This project is supported by the .NET Foundation. |
Open source решение | Да. |
Лицензия и ее классификация по SAM OSS лицензии | SAM 2.0 (mts.ru) | BSD-3-Clause license. |
Версия на момент проведения анализа | 1.5.0 (04 Nov 2022) — была актуальна по состоянию на март 2023 |
Репозиторий | github.com/OrchardCMS/OrchardCore |
Стек (frontend) | JS/ASP.NET Core |
Стек (backend) | ASP.NET Core |
СУБД | SQL Server MySQL PostgreSQL SQLite using a document abstraction (YesSql) |
Наличие GraphQL API |
![]() ![]() |
Удобство создания и управления моделью данных, включая поддержку вложенности элементов |
![]() ![]() ![]() ![]() |
Поддержка SSO-авторизации | Есть плагин OpenId. |
Особенности работы с мультимедийным контентом:
|
1. Можно менять через appsettings.json. 2. Да. |
Наличие локализации | Да. |
Поддержка версионирования. Рассматривали:
|
Судя по всему функция версионирования реализована через данный модуль Audit Trail module (Lombiq Technologies: OCORE-23) by domonkosgabor · Pull Request #6679 · OrchardCMS/OrchardCore · GitHub, но возможности не тестировались. |
Возможность управления flow (статусы у сущностей).
|
![]() ![]() |
Удобство использования |
![]() ![]() ![]() ![]() ![]() |
Особенности | 1. Может работать в трех режимах, в том числе как Headless CMS:
2. Субъективно кажется, что структура в JSON усложняется неоправданно (добавляется формат поля), например:
|
Импорт контента из внешних источников | Попробовали сделать импорт, структура по JSON не создается, импорт прошел успешно, но при этом результат никуда не сохранился. Похоже, только импорт данных в уже созданную структуру. |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой |
|
Нефункциональные требования.
|
Можно написать свой плагин для кэша. Есть механизмы кэширования данных. |
Расширяемость (дорабатываемость) системы.
|
Предусмотрена расширяемость с помощью плагинов. |
Публикация событий об изменении контента | Да. |
Возможность генерации API по созданной модели | Встроенный GraphQL браузер. |
Ролевая модель пользователей и API |
![]() ![]() |
Мультитенантность |
![]() Tenants — Orchard Core Documentation |
6. UI систем и примеры моделирования
Squidex
Панель управления Squidex включает два ключевых раздела:

Работа с созданием структуры данных проводится в разделе «Sсhemas», как выглядит управление данными в этом разделе можно посмотреть на скрине ниже:

Для моделирования доступны следующие типы данных:

Особенность, которую не обнаружили в других CMS, тип данных Components — позволяет к одному блоку привязать сразу несколько несколько схем данных.
Например, можно настроить заполнение в одном месте всех групп, в состав которых входит продукт. При этом может заполняться либо одна группа, либо другая, либо обе, в зависимости от кейса.

На этапе внесения сотрудник выбирает — о какой из сущностей хочет внести информацию в данный момент:

При этом сущности Category и Group могут иметь различный атрибутивный состав:

Доступно много возможностей для настройки валидации полей, причем перечень настроек зависит от типа данных. Ниже пример для текстового поля:

Для типа данных можно выбрать более подходящий «редактор», который предлагает подходящий интерфейс и дополнительные ограничения и валидации. Например, для текстового поля можно выбрать следующие варианты:

Удобно вносить текстовые данные в «MarkDown». В редакторе можно встраивать в текст гиперссылки, списки и т. п. И сразу просматривать результат:

Что касается моделирования сложной структуры данных, инструмент позволяет моделировать данные даже для прайс-листов. Например:

Внутри прайс-листа элементы группируются (группы — «В пределах пакета»/«После исчерпания пакета»/«Не включено в пакет в примере выше»).
При этом у элементов есть значения (стоимость) и различные единицы измерения (за сообщение/за минуту и т. п.).
Сами группы и единицы измерения — это не просто текст, это значения, которые должны выбираться из справочников.
Моделирование подобных структур данных возможно при добавлении в схему данных компонентов (тип Components).

Наш компонент в разделе «Sсhemas» имеет следующую структуру:

{
"id": "6a2abfe1-cf0c-4453-b449-3886171668c5",
"created": {
"value": "2023-06-21T06:05:12.000Z"
},
"createdBy": "subject:6437d117ed1a95c0fc81c021",
"lastModified": {
"value": "2023-06-22T05:03:11.000Z"
},
"lastModifiedBy": "subject:6437d117ed1a95c0fc81c021",
"version": {
"value": "10"
},
"status": "Published",
"statusColor": "#4bb958",
"scheduleJob": null,
"isDeleted": false,
"data": {
"title": {
"iv": "МТС Music"
},
"slug": {
"iv": "mts-music"
},
"channel": {
"iv": [
"323377a9-c720-43a4-aab0-7df9b7ad4f65"
]
},
"region": {
"iv": null
},
"validFrom": {
"iv": "2023-05-20T00:00:00Z"
},
"validTo": {
"iv": "9999-12-31T00:00:00Z"
},
"icon": {
"iv": [
"299224cf-20e5-4936-9ad5-073721ce7938"
]
},
"mainImage": {
"iv": [
"e0fa12c6-ddcb-48c3-ae95-15a9afa2c92a"
]
},
"otherMedia": {
"iv": null
},
"description": {
"iv": "Персональные подборки под любое настроение в приложении МТС Music"
},
"shortDescription": {
"iv": "Месяц музыки бесплатно без рекламы"
},
"addInformation": {
"iv": [
{
"schemaId": "0b417452-e2df-486f-8f92-09aae9ad2d03",
"title": "Почему стоит подключить:",
"text": "*Два месяца бесплатно\n* Без рекламы\n* Можно скачивать и слушать офлайн\n* Персональные рекомендации\n* Бесплатный трафик для абонентов МТС\n* Более 65 млн треков в высоком качестве"
}
]
},
"productSpecId": {
"iv": "93c20eac-e70b-4364-9626-fdf36148745e"
},
"advantages": {
"iv": [
{
"schemaId": "a518c845-f85f-4342-9e71-675d4f6a7ad0",
"title": "Выбирайте готовый плейлист",
"description": "В дороге, дома, на работе или тренировке",
"icon": null,
"image": [
"4036fcb7-e4aa-496f-a60c-eb502d036982"
]
},
{
"schemaId": "a518c845-f85f-4342-9e71-675d4f6a7ad0",
"title": "Детский режим",
"description": "С аудиосказками, песнями и подборками из мультфильмов",
"icon": null,
"image": [
"70debf41-5106-47d7-871c-057ac378e4eb"
]
}
]
},
"faq": {
"iv": [
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Если у меня уже была подписка МТС Music, будет ли доступено два месяца бесплатно?\n ",
"answer": "Нет. Предложение доступно только новым пользователям, которые никогда не подключали платную подписку МТС Music."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Зачем подключать подписку МТС Music?\n ",
"answer": "По подписке вы получаете полный доступ ко всему функционалу приложения и сайта, в том числе к поиску и офлайн-режиму прослушивания музыки."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Что будет, если отключить подписку в пробный период?\n ",
"answer": "При отключении услуги до истечения 30 дней и повторном её подключении услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\nПосле отключения платной подписки прослушивание своих сохранённых треков и плейлистов в режиме офлайн становится недоступным."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Сколько будет стоить подписка после двух месяцев бесплатного доступа?\n ",
"answer": "169 рублей в месяц."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Как оплачивать МТС Music после пробного периода?\n ",
"answer": "Для абонентов ПАО «МТС» списание средств за подписку будет производиться с лицевого счёта абонента.\n\nАбоненты других операторов мобильной связи смогут оплатить подписку, привязав банковскую карту с помощью сервиса МТС Деньги, который будет открываться непосредственно в приложении МТС Music – дополнительная авторизация не потребуется. Списания будут производиться с БК в соответствии с графиком списания подключённой услуги, безакцептно. История списаний за пользование сервисом будет отображаться в истории платежей по банковской карте абонента. Управлять подпиской пользователь сможет в приложении МТС Music, в разделе «Моя музыка» —> «Профиль»."
},
{
"schemaId": "f51d088d-6758-41c5-9d5a-674a5786a899",
"question": "Условия использования",
"answer": "*Если в течение 30 дней пробного бесплатного периода вы не пользовались сервисом, услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\n* Под «первые 30 дней для новых пользователей в подарок» понимается скидка в размере 100% стоимости первых по времени 30 дней пользования сервисом.\n\n* Подписка продлевается автоматически на месяц, если автопродление не отключено до окончания текущего периода, до 24:00. Неиспользованная часть бесплатного пробного периода аннулируется после приобретения подписки. Вы можете управлять своими подписками после покупки и отключить автопродление в настройках своей учётной записи.\n\n* Обращаем ваше внимание, что для онлайн-прослушивания музыки необходимо наличие доступа к сети интернет. В случае если включённая в основной и дополнительный пакет квота интернет-трафика будет превышена, вы сможете прослушивать только сохранённые в приложении композиции, онлайн-прослушивание музыки будет недоступно.\n\n* Бесплатный интернет-трафик при прослушивании музыки в приложении МТС Music не тарифицируется только для абонентов МТС.\n\n* Бесплатный интернет-трафик предоставляется только при прослушивании музыки в приложении МТС Music, загрузка прочего содержимого оплачивается согласно условиям тарифного плана.\n\n* Сервис доступен абонентам сети мобильной связи любого оператора на всей территории Российской Федерации и предназначен для смартфонов и планшетов на базе операционных систем Android (4.1 и выше), iOS (7.0 и выше), а также ПК.\n\n* Услугу предоставляет ПАО «МТС» при поддержке ООО «Стрим» и ООО «Яндекс.Медиасервисы».\n\n* Все цены указаны с учётом НДС."
}
]
},
"price": {
"iv": [
{
"schemaId": "7766d9e4-5d1a-4853-8ff3-0896544b2851",
"currentPrice": "0",
"oldPrice": "169",
"unit": [
"c229c4a8-2d3e-4634-9ccc-45778b357193"
],
"title": "Триальный период",
"shortDescription": "30 дней бесплатно, далее 169 ₽/мес",
"description": null
}
]
}
},
"schemaName": "contentspecifiction",
"schemaDisplayName": "contentspecifiction",
"referenceFields": [
{
"fieldId": 1,
"name": "title",
"properties": {
"isRequired": false,
"isRequiredOnPublish": false,
"isHalfWidth": false,
"fieldType": "String",
"createEnum": false,
"editor": "Input",
"inlineEditable": false,
"isEmbeddable": false,
"isUnique": false,
"contentType": "Unspecified",
"label": "Наименование"
},
"isLocked": false,
"isHidden": false,
"isDisabled": false,
"_links": {},
"canAddField": false,
"canDelete": false,
"canDisable": false,
"canEnable": false,
"canOrderFields": false,
"canHide": false,
"canLock": false,
"canShow": false,
"canUpdate": false,
"partitioning": "invariant",
"nested": []
}
],
"_links": {
"self": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "GET"
},
"previous": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/9",
"method": "GET"
},
"draft/create": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/draft",
"method": "POST"
},
"status/Archived": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/status",
"method": "PUT",
"metadata": "#eb3142"
},
"status/Draft": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5/status",
"method": "PUT",
"metadata": "#8091a5"
},
"delete": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "DELETE"
},
"patch": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "PATCH"
},
"update": {
"href": "/api/content/mts/contentspecifiction/6a2abfe1-cf0c-4453-b449-3886171668c5",
"method": "PUT"
}
},
"canDelete": true,
"canDraftCreate": true,
"canDraftDelete": false,
"canCancelStatus": false,
"canUpdate": true,
"statusUpdates": [
{
"status": "Archived",
"color": "#eb3142"
},
{
"status": "Draft",
"color": "#8091a5"
}
]
}
А вот пример запроса и ответа в GraphQL API.
{
queryContentspecifictionContents(
filter: "data/productSpecId/iv eq '93c20eac-e70b-4364-9626-fdf36148745e' and status eq 'Published' and data/channel/iv eq '323377a9-c720-43a4-aab0-7df9b7ad4f65' and data/validFrom/iv gt 2023-05-19T00:00:00Z"
) {
id
status
version
flatData {
title
validFrom
validTo
productSpecId
channel {
id
... on Channel {
flatData {
externalid
title
}
}
}
description
shortDescription
addInformation {
title
text {
text
}
}
advantages {
title
description
icon {
type
metadata
url
}
}
icon {
type
metadata
url
tags
}
mainImage {
type
metadata
url
tags
}
otherMedia {
type
metadata
url
tags
}
faq {
question {
text
}
answer {
text
}
}
price {
title
description
shortDescription
oldPrice
currentPrice
unit {
id
flatData {
title
alias
titleInGenitiveCase
fullTitle
}
}
}
}
}
}
{{
"data": {
"queryContentspecifictionContents": [
{
"id": "6a2abfe1-cf0c-4453-b449-3886171668c5",
"status": "PUBLISHED",
"version": 10,
"flatData": {
"title": "МТС Music",
"validFrom": "2023-05-20T00:00:00Z",
"validTo": "9999-12-31T00:00:00Z",
"productSpecId": "93c20eac-e70b-4364-9626-fdf36148745e",
"channel": [
{
"id": "323377a9-c720-43a4-aab0-7df9b7ad4f65",
"flatData": {
"externalid": "741ba712-3069-48e3-928c-8631e5ed186e",
"title": "Personal"
}
}
],
"description": "Персональные подборки под любое настроение в приложении МТС Music",
"shortDescription": "Месяц музыки бесплатно без рекламы",
"addInformation": [
{
"title": "Почему стоит подключить:",
"text": {
"text": "*Два месяца бесплатно\n* Без рекламы\n* Можно скачивать и слушать офлайн\n* Персональные рекомендации\n* Бесплатный трафик для абонентов МТС\n* Более 65 млн треков в высоком качестве"
}
}
],
"advantages": [
{
"title": "Выбирайте готовый плейлист",
"description": "В дороге, дома, на работе или тренировке",
"icon": null
},
{
"title": "Детский режим",
"description": "С аудиосказками, песнями и подборками из мультфильмов",
"icon": null
}
],
"icon": [
{
"type": "UNKNOWN",
"metadata": {},
"url": "http://127.0.0.1/api/assets/mts/299224cf-20e5-4936-9ad5-073721ce7938/",
"tags": [
"type/svg",
"image"
]
}
],
"mainImage": [
{
"type": "IMAGE",
"metadata": {
"pixelWidth": 934,
"pixelHeight": 937,
"imageQuality": 100,
"description": "JFIF File"
},
"url": "http://127.0.0.1/api/assets/mts/e0fa12c6-ddcb-48c3-ae95-15a9afa2c92a/",
"tags": [
"type/jpg",
"image",
"image/medium"
]
}
],
"otherMedia": null,
"faq": [
{
"question": {
"text": "Если у меня уже была подписка МТС Music, будет ли доступено два месяца бесплатно?\n "
},
"answer": {
"text": "Нет. Предложение доступно только новым пользователям, которые никогда не подключали платную подписку МТС Music."
}
},
{
"question": {
"text": "Зачем подключать подписку МТС Music?\n "
},
"answer": {
"text": "По подписке вы получаете полный доступ ко всему функционалу приложения и сайта, в том числе к поиску и офлайн-режиму прослушивания музыки."
}
},
{
"question": {
"text": "Что будет, если отключить подписку в пробный период?\n "
},
"answer": {
"text": "При отключении услуги до истечения 30 дней и повторном её подключении услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\nПосле отключения платной подписки прослушивание своих сохранённых треков и плейлистов в режиме офлайн становится недоступным."
}
},
{
"question": {
"text": "Сколько будет стоить подписка после двух месяцев бесплатного доступа?\n "
},
"answer": {
"text": "169 рублей в месяц."
}
},
{
"question": {
"text": "Как оплачивать МТС Music после пробного периода?\n "
},
"answer": {
"text": "Для абонентов ПАО «МТС» списание средств за подписку будет производиться с лицевого счёта абонента.\n\nАбоненты других операторов мобильной связи смогут оплатить подписку, привязав банковскую карту с помощью сервиса МТС Деньги, который будет открываться непосредственно в приложении МТС Music – дополнительная авторизация не потребуется. Списания будут производиться с БК в соответствии с графиком списания подключённой услуги, безакцептно. История списаний за пользование сервисом будет отображаться в истории платежей по банковской карте абонента. Управлять подпиской пользователь сможет в приложении МТС Music, в разделе «Моя музыка» —> «Профиль»."
}
},
{
"question": {
"text": "Условия использования"
},
"answer": {
"text": "*Если в течение 30 дней пробного бесплатного периода вы не пользовались сервисом, услуга будет предоставляться на платной основе, бесплатные дни предоставляться не будут.\n\n* Под «первые 30 дней для новых пользователей в подарок» понимается скидка в размере 100% стоимости первых по времени 30 дней пользования сервисом.\n\n* Подписка продлевается автоматически на месяц, если автопродление не отключено до окончания текущего периода, до 24:00. Неиспользованная часть бесплатного пробного периода аннулируется после приобретения подписки. Вы можете управлять своими подписками после покупки и отключить автопродление в настройках своей учётной записи.\n\n* Обращаем ваше внимание, что для онлайн-прослушивания музыки необходимо наличие доступа к сети интернет. В случае если включённая в основной и дополнительный пакет квота интернет-трафика будет превышена, вы сможете прослушивать только сохранённые в приложении композиции, онлайн-прослушивание музыки будет недоступно.\n\n* Бесплатный интернет-трафик при прослушивании музыки в приложении МТС Music не тарифицируется только для абонентов МТС.\n\n* Бесплатный интернет-трафик предоставляется только при прослушивании музыки в приложении МТС Music, загрузка прочего содержимого оплачивается согласно условиям тарифного плана.\n\n* Сервис доступен абонентам сети мобильной связи любого оператора на всей территории Российской Федерации и предназначен для смартфонов и планшетов на базе операционных систем Android (4.1 и выше), iOS (7.0 и выше), а также ПК.\n\n* Услугу предоставляет ПАО «МТС» при поддержке ООО «Стрим» и ООО «Яндекс.Медиасервисы».\n\n* Все цены указаны с учётом НДС."
}
}
],
"price": [
{
"title": "Триальный период",
"description": null,
"shortDescription": "30 дней бесплатно, далее 169 ₽/мес",
"oldPrice": "169",
"currentPrice": "0",
"unit": [
{
"id": "c229c4a8-2d3e-4634-9ccc-45778b357193",
"flatData": {
"title": "₽/мес",
"alias": null,
"titleInGenitiveCase": "рублей в месяц",
"fullTitle": null
}
}
]
}
]
}
}
]
},
"extensions": {
"tracing": {
"version": 1,
"startTime": "2023-06-26T05:20:49.9570693Z",
"endTime": "2023-06-26T05:20:50.0001375Z",
"duration": 43068200,
"parsing": {
"startOffset": 1100,
"duration": 43300
},
"validation": {
"startOffset": 45100,
"duration": 1028900
},
"execution": {
"resolvers": []
}
}
}
} }
Strapi
А теперь посмотрим, как выглядит Strapi.
Панель управления:

Ключевыми разделами Strapi CMS являются:
- Content-Type Builder — инструмент создания объектов, который позволяет создавать таблицы для хранения данных и связывать их между собой, а также настраивать их отображение (порядок полей при заполнении и прочее) для пользователя;
- Сontet Manager — раздел для заполнения созданных объектов данными.
С чем ведется работа в Content-Type Builder:
- коллекции «COLLECTION TYPES» — набор данных разных типов, который может включать множество записей;
- компоненты — объект данных, упрощающий внесение данных в коллекции. Например, когда нужно создать подряд несколько однотипных записей внутри коллекции;
- диночные типы «SINGLE TYPES» — набор данных разных типов. В отличие от коллекции может включать только одну запись. Полезно для создания простых страниц. Например, для создания раздела «Контакты» на сайте.

Подробнее про создание объектов в Strapi CMS можно почитать тут.
Наполнять контент можно данными следующих типов:


В отличии от Squidex нельзя настроить отдельное наименование поля и подсказку для отображения пользователю при внесении:

Гораздо меньше настроек по валидации и формату ввода данных (также на примере текстового поля):

Моделирование сложных структур данных и данных с несколькими элементами в составе производится с помощью компонентов (как и в Squidex):

Что касается моделирования сложной структуры данных, рассмотрим их заведение (как и для Squidex) на этом примере:

Напомним, что внутри прайс-листа элементы группируются. При этом группы и единицы измерения — это не просто текст, а значения, которые должны выбираться из справочников.
Моделирование подобных структур данных возможно при добавлении в схему данных компонентов (тип Components).

Наш компонент имеет следующую структуру:

В отличие от Squidex на этом экране нет функции перетаскивания и нельзя изменить очередность заполнения полей и компонентов на форме.
Есть кнопка конфигурирования представления

и только после клика по ней происходит переход к виду, где можно менять очередность блоков:

Так будет выглядеть внесение информации в разделе «Сontet Manage» UI Strapi для первого элемента данных:
Поэтому не повторяйте наших ошибок, следите, чтобы поле «title» всегда шло первым, если используете данную CMS.

В отличие от Squidex при внесении данных нельзя добавить новую запись в справочник, например, в перечень групп, только выбрать существующую из списка значений.
Остальные заполняются аналогично — путем добавления нового экземпляра компонента:

Ниже можно посмотреть, как будет выглядеть данная информация в GraphQL API.
query {
tariffs(
filters: {
tp: { title: { eq: "Тарифище" } }
and: [
{ region: { title: { eq: "Краснодар" } } }
{ vaidFrom: { gte: "2010-08-25T10:15:30Z" } }
{ validTo: { lte: "2050-08-25T10:15:30Z" } }
]
}
) {
data {
id
attributes {
detailInformation {
group {
data {
attributes {
title
iconURL
}
}
}
sub_gr {
data {
attributes {
title
slug
}
}
}
title {
data {
attributes {
slug
title
}
}
}
value
unit {
data {
attributes {
title
slug
}
}
}
}
}
}
}
}
{
"data": {
"tariffs": {
"data": [
{
"id": "6",
"attributes": {
"detailInformation": [
{
"group": {
"data": {
"attributes": {
"title": "SMS и MMS, за сообщение",
"iconURL": "//contents.mts.ru/b-icon_envelope.svg"
}
}
},
"sub_gr": {
"data": {
"attributes": {
"title": "В пределах включенного пакета SMS",
"slug": "SMS-v-predelah-vklyuchennogo-paketa"
}
}
},
"title": {
"data": {
"attributes": {
"slug": "SMSRussiaOperators",
"title": "SMS на номера любых операторов России"
}
}
},
"value": "3.70",
"unit": {
"data": {
"attributes": {
"title": "₽ за сообщение",
"slug": "rub-msg"
}
}
}
}
]
}
}
]
}
}
}
Orchard Core
В Orchard Core столкнулись с ограничениями в моделировании данных и прочими сложностями, которые представлены в сравнительном анализе (таблица выше). Поэтому покажем только базовые элементы интерфейса. Примеры моделирования данных будут приведены только для Strapi и Squidex.

В основном работа ведется с Типами (Parts) и Частям контента.

Части контента — это что-то вроде компонентов в других системах:

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

Перечень доступных настроек очень ограничен.

7. Заключение
Выбор и применение современных CMS является важным вопросом для команд, которые хотят эффективно управлять контентом на различных платформах. Решение в каждом случае необходимо принимать исходя из собственных потребностей и ИТ-ландшафта.
Сводная таблица со сравнением CMS по ключевым для нашей команды критериям.
Критерий | Strapi CMS | Squidex | Orchard Core |
Сообщество/поддержка | Большое, частые обновления | Среднее, тесное взаимодействие с пользователями | Среднее, поддержка .NET Foundation |
Open source решение | ![]() |
![]() |
![]() |
Стек:
|
Angular + Node.js (JS/TypeScript) | Angular (TypeScript) + C# | JS + ASP.NET Core |
СУБД | SQL (PostgreSQL, MySQL/MariaDB, SQLite) | NoSQL (MongoDB) | SQL (SQL Server, MySQL, PostgreSQL, SQLite) |
Удобство использования | Среднее | Высокое | Низкое |
Контроль над ошибками в UI и возможность «сломать» систему | Низкий | Высокий | Средний |
Гибкость системы при моделировании данных | Средняя | Высокая | Низкая |
Поддержка GraphQL API |
![]() |
![]() |
![]() |
Наличие локализации | ![]() |
![]() |
![]() |
Поддержка версионирования |
![]() |
![]() |
![]() |
Возможность управления flow |
![]() |
![]() |
![]() |
Импорт контента из внешних источников |
![]() |
![]() |
![]() |
Хранение бинарных файлов (assets)/мультимедиа (S3 compatible) или интеграция с такой системой | ![]() |
![]() |
![]() |
Расширяемость системы |
![]() |
![]() |
![]() |
Публикация событий об изменении контента | ![]() |
![]() |
![]() |
Ролевая модель пользователей и API | ![]() |
![]() |
![]() |
Мультитенантность из коробки | ![]() |
![]() |
![]() |



По результатам пилотных задач на наш взгляд самой удобной оказалась CMS Squidex, как при моделировании схемы данных, так и при её наполнении.
В Strapi постоянно сталкивались с различными багами. Есть замечания к пользовательскому интерфейсу, не все операции интуитивны и понятных даже для специалистов с опытом работы с другими CMS.
В UI Orchard Core были большие сложности с моделированием контента. В CMS не оказалось ряда критичных для нас функций (к которым мы уже привыкли после Strapi и Squidex), например, создание нескольких экземпляров одного компонента при внесении контента или возможность вложить компонент в компонент.
У Squidex интуитивно понятный UI, гибкие возможности по созданию модели данных. Мы не столкнулись с какими-либо ограничениями при моделировании.
Это решение отвечает всем исходным требованиям, которые мы предъявляли к Headless CMS.
Из-за особенностей предметной области у нас есть дополнительные требования по регионализации и версионированию контента, которые не поддерживаются ни одной из систем, но базовая функциональность и стек Squidex являются хорошей основной для последующей кастомизации и доработок.
Будем рады, если статья оказалась вам полезной. В комментариях поделитесь, интересны ли вам другие возможности CMS или, возможно, хотелось бы поподробнее узнать про какую-то функциональность. Если тема будет интересна, обязательно расскажем подробнее в будущих статьях.
CloudMTS поможет гибко управлять и разворачивать системы любого масштаба: от сайтов-визиток и простых CMS до высоконагруженных e-commerce-проектов. Сервис позволяет быстро выстраивать отказоустойчивую систему и масштабировать виртуальные машины под разный уровень нагрузки.
Комментарии (6)
aol-nnov
30.08.2023 07:55а где ghost? https://ghost.org https://github.com/TryGhost/Ghost
elkorotch Автор
30.08.2023 07:55+1Нам для детального рассмотрения были интересны CMS, которые соответствовали ключевым критериям отбора: не только создание постов, а поддержка моделирования сложных структур данных (вложенность и переиспользуемость компонентов и т.п.), QraphQL API и другие. Не было задачи рассмотреть детально каждую систему на рынке, по максимальному соответствию критериям для пилота были выбраны 3 CMS, одна из них полностью подошла, поэтому дальнейшее исследование было нецелесообразным.
garikmk
30.08.2023 07:55А как же вот эта CSM https://quantumart.ru/products/cms, которая у вас уже используется ? Она всем вашим требованиям удовлетворяет.
elkorotch Автор
30.08.2023 07:55+1Как было написано в статье, на текущий момент в экосистеме МТС применяются различные инструменты для управления контентом. Возникла потребность в оптимизации и унификации текущих подходов к управлению контентом и передаче данных витринам. Развитие данной CMS требует привлечения внешней экспертизы, а также было интересно сравнить функциональность и удобство использования с другими решениями, поэтому начали заниматься исследованием рынка.
Pitcentr0
обратите внимание на directus cms