28 нояб. 2025 г.·7 мин

Правила именования базы данных для админ‑панели, которые остаются читабельными

Используйте правила именования для админ‑панели, чтобы автоматически сгенерированные экраны были понятными: ясные правила для таблиц и полей, enums, связей и краткий чек‑лист.

Правила именования базы данных для админ‑панели, которые остаются читабельными

Почему имена решают, кажется ли админ‑панель понятной или запутанной

Большинство админ‑панелей строятся из вашей модели данных. Имена таблиц и полей оказываются в пунктах меню, заголовках страниц, заголовках колонок, метках фильтров и даже в словах, которые люди вводят в поиск.

Когда имена понятны, администратор может пробежать взглядом список и понять его за секунды. Когда имена неочевидны, он останавливается, догадывается, открывает запись, возвращается и пробует снова. Эти колебания накапливаются. В итоге это превращается в вопросы поддержки «Как найти нужного клиента?» и в руководства по обучению, которые никто не хочет читать.

Разработчики обычно называют вещи так, чтобы им было удобно строить и отлаживать. Операторы называют вещи так, чтобы выполнять работу. Разработчику может быть достаточно acct, addr1 или stat, потому что он помнит, что это значит. Оператору нужен «Account», «Address line 1» и «Status» без расшифровки.

В админ‑экране «читабельным» обычно считается:

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

Если вы используете платформу, которая генерирует экраны из модели (например, Data Designer и админ‑вью в AppMaster), то именование становится частью дизайна UI. Хорошие имена дают аккуратные экраны по умолчанию с первого дня, ещё до того как вы начнёте править метки и раскладки.

Простой базовый набор правил, которого может придерживаться вся команда

Если хотите, чтобы сгенерированные админ‑экраны выглядели аккуратно с первого дня, согласуйте базу до того, как кто‑то добавит первую таблицу. Большинство проблем с именами не технические — они про несогласованность.

Выберите один стиль идентификаторов и не смешивайте его. Для баз данных snake_case обычно проще читать и искать. Если ваш стек ожидает camelCase, используйте его везде (таблицы, колонки, внешние ключи, enums). Смена стиля в середине проекта — вот что делает метки и фильтры случайными.

Базовые правила, которые подходят большинству команд:

  • Используйте полные слова: customer_id, а не cust_id; description, а не desc.
  • Для сущностей — существительные, для действий — глаголы: invoice, payment, refund_requested.
  • Согласованные имена времён: created_at, updated_at, deleted_at.
  • Избегайте расплывчатых слов вроде data, info, value или type, если вы не добавляете контекст (например, shipping_address, payout_method).
  • Держите единство единственного/множественного числа (многие команды используют множественные имена таблиц, например customers, а для колонок — единственное, вроде customer_id).

Напишите маленький глоссарий и держите его на виду. Решите рано: customer, client, account или user — и используйте один термин. Так же поступайте с «order» vs «purchase» или «ticket» vs «case».

Быстрая проверка: если двое людей могут посмотреть на колонку account_status и согласиться, что она значит, не задавая вопросов, базовая политика работает. Если нет — переименуйте до того, как строить экраны и фильтры.

Правила именования таблиц, которые аккуратно мапятся на меню и списки

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

Выберите один стиль для таблиц сущностей и придерживайтесь его: единственное (user, invoice, ticket) или множественное (users, invoices, tickets). Единственное часто лучше читается в заголовках форм («Edit Ticket»), а множественное — в меню («Tickets»). Любой вариант приемлем. Смешивание делает навигацию непоследовательной.

Называйте таблицы по тому, чем они являются, а не по тому, что делают. Таблица должна представлять предмет, на который можно указать. payment — это предмет; processing — действие. Если позже появятся refunds, retries и settlements, имя processing станет вводящим в заблуждение.

Правила, которые сохраняют меню и списки аккуратными:

  • Используйте конкретные существительные (customer, subscription, invoice, ticket_message).
  • Избегайте «ведёрных» таблиц для постоянных данных (settings, misc, temp, data). Разбейте их на реальные сущности (notification_setting, tax_rate, feature_flag).
  • Предпочитайте короткие, читаемые составные имена с подчёркиванием (purchase_order, support_ticket) вместо аббревиатур.
  • Добавляйте префикс модуля только когда он предотвращает конфликты (например, billing_invoice vs invoice). Если префикс используете — делайте это согласованно во всём модуле.

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

Join‑таблицы и идентификаторы: как сохранить many‑to‑many читабельным

Many‑to‑many связи — место, где админ‑панели часто начинают выглядеть грязно. Если join‑таблица и её ключи названы хорошо, сгенерированные экраны остаются читаемыми без ручной правки.

Начните с одного скучного правила и не нарушайте его: у каждой таблицы первичный ключ называется id. Не смешивайте user_id как первичный ключ в одной таблице и id в другой. Единые идентификаторы делают отношения предсказуемыми и помогают сгенерированным формам и полям ссылок оставаться согласованными.

Для чистых join‑таблиц называйте их по обеим сущностям, используя один шаблон и порядок. Распространённые варианты — в алфавитном порядке (product_tag) или «главная сущность первой» (user_role). Выберите порядок и держитесь его.

Избегайте расплывчатых имён вроде links или mappings, если таблица не хранит действительно общие связи между объектами. В большинстве админ‑панелей конкретность лучше хитрости.

Когда join‑таблица становится реальной сущностью

Если отношение имеет дополнительные поля, рассматривайте его как самостоятельную модель и называйте её существительным, понятным людям: membership, assignment, subscription. Например, если у роли пользователя есть starts_at, ends_at и granted_by, user_role подойдёт, но membership может в UI читаться лучше.

Простой набор правил, который сохраняет экраны профессиональными:

  • Используйте id как первичный ключ в каждой таблице.
  • Называйте join‑таблицы по обеим сущностям в согласованном порядке (user_role).
  • Используйте понятные внешние ключи вроде user_id и role_id.
  • Добавляйте правило уникальности, которое отражает реальность (например, один role_id на user_id).
  • Если вы храните историю, делайте правило уникальности равным «активным» записям (например, уникально там, где ended_at = null).

Эти выборы выдерживают рост данных и хорошо работают с Data Designer в AppMaster, где экраны могут генерироваться прямо из модели.

Шаблоны имен полей, которые дают понятные столбцы и фильтры

Переименовывайте без рассинхронизации UI
Перегенерируйте приложение при изменении имён, чтобы UI и API оставались синхронизированы.
Сгенерировать приложение

Имена полей делают больше, чем помогают разработчикам. Они определяют, что пользователи увидят в заголовках колонок, метках фильтров и полях форм.

Предсказуемые суффиксы убирают догадки:

  • Используйте _id для внешних ключей: customer_id, assigned_agent_id.
  • Используйте _at для времён: created_at, paid_at, closed_at.
  • Используйте _count для счётчиков: login_count, attachment_count.

Булевы поля должны читаться как простые предложения. Предпочитайте префиксы is_ и has_, чтобы чекбоксы были понятны сразу: is_active, has_paid, is_verified. Избегайте двойных отрицаний вроде is_not_approved. Если нужен «не»‑состояние, моделируйте позитив и инвертируйте логику в коде.

Поля денег часто вызывают путаницу в решётках админки. Выберите один подход и придерживайтесь его: храните в минимальных единицах (центах) как integer или храните decimal с фиксированной точностью. Назовите поле так, чтобы никто не догадывался. Например: total_amount_cents + currency_code, или total_amount + currency_code. Не смешивайте price, amount и total, если они не обозначают разные понятия.

Текстовые поля должны явно отражать назначение, а не просто тип. description виден пользователю. internal_comment — приватный. notes — сборный вариант и его стоит использовать осторожно. Если несколько заметок, называйте по аудитории: customer_note, agent_note.

Контактные поля должны быть буквальными, потому что часто становятся быстрыми фильтрами: website_url, contact_email, billing_email. В сгенерированных AppMaster админ‑экранах такие имена обычно превращаются в аккуратные метки по умолчанию.

Связи и внешние ключи: имена, которые объясняют модель данных

Хорошие связи читаются как простая английская фраза. Когда админ‑панель генерируется из базы, имена внешних ключей часто становятся заголовками колонок, фильтрами и метками форм.

Держитесь одного правила: колонка внешнего ключа — имя ссылаемой таблицы плюс _id. Если есть customer.id, используйте customer_id. Если есть order.id, используйте order_id. Такая согласованность делает очевидным, на что указывает колонка.

Self‑relations требуют дополнительной ясности, потому что их легко неправильно прочитать. Избегайте общего related_id. Используйте имена, которые объясняют направление и смысл: parent_id для деревьев, manager_id для орг‑структур или merged_into_id для объединений.

Когда связь включает join‑таблицу, называйте её так, чтобы она читалась как предложение. Например, ticket_assignee.user_id понятнее, чем ticket_user.user_id, если роль — «assignee» (а не «reporter» или «watcher").

Практические проверки, которые предотвращают большинство проблем:

  • Не переиспользуйте owner_id с разными значениями в разных таблицах. Предпочитайте created_by_user_id, account_manager_user_id или billing_contact_id.
  • Если у вас несколько связей на одну таблицу, указывайте роль: requested_by_user_id и approved_by_user_id.
  • Выберите один маркер soft delete и используйте его везде. deleted_at широко понятен и хорошо работает с фильтрами.

Если вы будете строить экраны в AppMaster позже, эти имена появятся повсюду, поэтому небольшая осторожность сейчас сэкономит много правок UI позже.

Enums и поля статуса, которые остаются понятными со временем

Постройте полный стек без кода
Используйте AppMaster, чтобы построить полный стек: база данных, бэкенд и UI админки из одной модели.
Начать

Если админ‑панель генерируется из базы, самый быстрый способ сделать экраны беспорядочными — разбросать смысл по множеству мелких флагов. Предпочитайте один понятный enum‑статус для основного жизненного цикла записи и держите дополнительные флаги только для действительно отдельного поведения.

Полезное правило: если пользователи будут спрашивать «Где сейчас этот элемент в своём пути?», это статус. Если вопрос «Нужно ли его скрыть?» или «Заблокирован ли он?», это отдельный булев флаг.

Один статус лучше пяти булевых флагов

Вместо is_new, is_in_progress, is_done, is_cancelled используйте одно поле ticket_status. Оно лучше читается в колонках списков, фильтрах и массовых действиях. Также это исключает невозможные комбинации вроде «done + in_progress».

Держите значения enum стабильными. Текст в UI можно менять, а хранимые значения менять нежелательно. Храните pending, а не waiting_for_review. Храните rejected, а не rejected_by_manager. Дружелюбные метки можно показывать позже без миграции данных.

Когда нужен дополнительный контекст, добавьте отдельное поле, а не перегружайте статус. Пример: оставьте payment_status для жизненного цикла, а для причины неудачи добавьте failure_reason (текст).

Именуйте enum по домену (чтобы фильтры были понятны)

Добавляйте префикс домена, чтобы экраны оставались читабельными, когда у разных моделей есть «status»:

  • payment_status (чекаут заказа)
  • ticket_priority (срочность поддержки)
  • user_role (уровень доступа)
  • invoice_status (жизненный цикл выставления счетов)
  • delivery_status (жизненный цикл доставки)

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

Напишите однострочное объяснение для каждого значения enum в командных заметках. Вы забудете позже разницу между cancelled и voided. В AppMaster такие короткие определения помогают сохранять согласованные выпадающие списки и фильтры между вебом и мобильными экранами.

Крайние случаи: даты, поля аудита и колонки type

Руководства по именам часто охватывают таблицы и базовые поля, но админ‑панели путаются в крайних случаях. Даты, поля аудита и колонки type — это места, где путаные имена превращаются в путаные экраны.

Для дат и времён сделайте имя говорящим: запланировано, фактическое или напоминание? Простейший паттерн — глагол по смыслу плюс понятный суффикс. Например, due_at (планируемый дедлайн) и completed_at (фактическое завершение) будут читаться как понятные колонки и фильтры. Избегайте расплывчатых пар вроде start_date и end_date, если на самом деле вы имеете в виду scheduled_at и finished_at.

Опциональные связи — ещё одна ловушка. Не придумывайте новых шаблонов для каждой таблицы. Сохраняйте имя связи стабильным и выражайте «опциональность» через nullable, а не через переименование поля. manager_id остаётся manager_id, даже если он необязателен.

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

  • address_line1, address_line2, city, region, postal_code, country_code
  • Избегайте address1, address2 (хуже читаются, легче дублируются)

Поля аудита должны быть скучными намеренно:

  • created_at, updated_at
  • created_by_id, updated_by_id (только если действительно нужна история по пользователям)

Будьте осторожны с type. Это почти всегда слишком общее и со временем стареет. Вместо type называйте по смыслу: payment_method, ticket_channel, customer_tier. В схеме‑дривен админке (включая AppMaster) такой выбор часто определяет разницу между понятным фильтром и запутанным выпадающим списком.

Пример: как назвать модель тикета поддержки, чтобы она выглядела хорошо в админке

Прототипируйте одну фичу быстро
Проверьте базовый набор правил именования на небольшом модуле, прежде чем база вырастет.
Создать прототип

Небольшая реалистичная настройка поддержки: клиенты пишут, сотрудники отвечают, тикеты можно тегировать. Правила именования — то, что делает авто‑сгенерированные меню, списки и фильтры очевидными.

Начните с имён таблиц, которые читаются как существительные в сайдбаре:

  • customer
  • ticket
  • ticket_message
  • ticket_tag
  • ticket_tag_link

В большинстве админ‑панелей они превратятся в метки типа «Tickets» и «Ticket Messages», а join‑таблица не будет мешать.

Для экрана списка тикетов выбирайте имена полей, которые станут понятными заголовками колонок и фильтрами:

  • subject, status, priority
  • assigned_to_id (указывает на сотрудника)
  • last_message_at (помогает сортировать по последнему сообщению)
  • created_at (стандартно и предсказуемо)

Enum‑ы — частое место, где читабельность ломается позже, поэтому держите набор стабильным и простым:

  • ticket_status: new, open, pending_customer, resolved, closed
  • ticket_priority: low, normal, high, urgent

Одно решение, которое предотвращает постоянную путаницу: не перегружайте «customer». В поддержке запросчик не всегда является клиентом (коллега может отправить от имени клиента). Если вы храните человека, который отправил тикет, назовите поле requester_id, а отдельно храните customer_id для аккаунта, о котором идёт речь. Это разделение делает формы и фильтры правдивыми с самого начала.

Пошагово: как назвать новую модель перед созданием экранов

Протестируйте пример тикетов
Постройте модель тикетов поддержки и посмотрите, как названия join‑таблиц влияют на сгенерированные экраны.
Попробовать

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

Повторяемый процесс для каждой фичи

  1. Начните с мини‑глоссария (5–10 терминов). Запишите слова, которые нетехнический коллега использовал бы на встрече, затем выберите по одному предпочитаемому термину для каждой концепции (например, «customer» vs «client").

  2. Набросайте экраны, которые ожидаете: список, карточка, создание, редактирование. Для списка решите, какие 5–8 колонок должны быть понятны сразу. Если имя поля будет выглядеть странно как заголовок, его стоит доработать.

  3. Набросайте таблицы и связи, затем называйте поля по правилам суффиксов (*_id, *_at, is_*, *_count). Когда вы потом генерируете админ‑экраны (включая AppMaster), эти паттерны дают аккуратные метки и предсказуемые фильтры.

Перед тем как двигаться дальше, убедитесь, что вы не смешиваете стили (customer_id в одной таблице и clientId в другой). Согласованность важнее хитрости.

  1. Определите enum‑ы заранее, а не после появления первого UI. Напишите однострочное пояснение для каждого значения, как будто объясняете его сотруднику поддержки. Предпочитайте значения, которые выдержат изменения, например pending, active, archived (не new, newer, newest).

  2. Проведите «прочтение заголовков колонок». Представьте себя админом, пробегающим список взглядом.

  • Были бы понятны «Created At», «Updated At», «Status», «Assigned To», «Total Amount» без инструкций?
  • Есть ли поля, которые выглядят как внутренний код (tmp_flag, x_type, data1)?
  • Являются ли единицы очевидными (amount_cents vs amount, duration_seconds vs duration)?

Если что‑то звучит непонятно вслух — переименуйте сейчас. Переименование позже возможно, но часто протекает в отчёты, фильтры и привычки.

Частые ошибки именования, которые делают админ‑панели неудобными

Если схема грязная, экраны будут грязными, независимо от того, насколько красив UI. Правила именования — это не про «стиль», а про повседневную удобочитаемость.

Первая ловушка — смешанная лексика. Если в одной таблице client, а в другой customer, меню, фильтры и результаты поиска кажутся описывающими разные вещи. Выберите одно слово для каждой ключевой концепции и используйте его везде, в том числе в именах связей.

Ещё одна проблема — чрезмерные сокращения. Аббревиатуры вроде addr, misc или info экономят пару символов, но сильно снижают понятность в таблицах и экспортируемых данных.

Третья ошибка — запекание UI‑потока в базу. Поле вроде new_customer_wizard_step имеет смысл при запуске, но путает, когда процесс меняется. Храните бизнес‑факт (onboarding_status) и дайте UI решать, как вести пользователя.

Также остерегайтесь перегрузки булевыми флагами. Когда добавляете is_new, is_open, is_closed, рано или поздно получите конфликтующие состояния и непонятные фильтры. Предпочитайте одно поле статуса с небольшим набором значений.

Красные флаги, которые обычно ведут к уродливым экранам:

  • Два разных имени для одного и того же (client_id vs customer_id)
  • «Ящики для мусора» в колонках (notes, misc, extra), где смешиваются разные данные
  • Имена, зависящие от времени (summer_campaign_*), которые переживают кампанию
  • Множество булевых полей, описывающих одно состояние
  • Переименования, сделанные без плана миграции

Переименование — это не просто найти‑и‑заменить. Если вы меняете customer_phone на phone_number, спланируйте миграцию, обновите сгенерированные экраны и сохраните обратную совместимость там, где нужно (особенно если внешние системы читают API). В AppMaster аккуратные имена окупаются сразу, потому что списки, формы и фильтры наследуют метки из модели.

Быстрый чек‑лист перед релизом админ‑панели

Спроектируйте модель данных для админа
Создайте схему, готовую для Postgres, в Data Designer и сохраните согласованность меню и меток.
Начать строить

Прежде чем объявить схему «готовой», пройдитесь по ней глазами человека, который будет жить в админ‑панели каждый день.

  • Таблицы звучат как реальные вещи. Коллега должен уметь сказать, что представляет таблица (ticket, customer, invoice) без домыслов.
  • Ключевые поля следуют предсказуемым суффиксам. Используйте шаблоны: *_id для ссылок, *_at для времён, *_amount (или *_amount_cents) для денег и is_* для булевых флагов.
  • Enum‑ы стабильны и просты. Храните значения вроде pending, paid, failed, а не фразы из UI, которые поменяются.
  • Новый коллега может вывести смысл. Если поля оказались бы в сгенерированном списке без подсказок, была бы интенция очевидна?
  • Расплывчатые слова удалены или уточнены. Замените data, value, type, info на конкретные status, source, category, notes или external_reference.

Если вы используете Data Designer в AppMaster для генерации админ‑вью, этот чек‑лист практически моментально полезен: понятные имена дают понятные колонки и фильтры, и вам придётся меньше править метки после того, как пользователи начнут работать в системе.

Следующие шаги: превратите именование в привычку и сохраняйте согласованность экранов

Хорошие имена — это не разовая чистка, а небольшая рутина, которая сохраняет админ‑UI читабельным по мере роста схемы.

Начните с одного существующего модуля и примените правила только к следующей таблице, которую добавите. Это избегает пугающего переписывания и даёт реальную площадку для практики. Если следующая фича добавляет «returns» в систему заказов, назовите таблицу, внешние ключи и статусы по вашим шаблонам с самого начала, а затем переиспользуйте подход дальше.

Держите одностраничное руководство по именованию рядом с местом, где вы работаете со схемой. Оно должно быть коротким: как вы называете таблицы, первичные ключи, внешние ключи, временные метки и enum‑ы статусов. Цель — быстрые решения, а не долгие споры.

Если вы строите в AppMaster, полезно задать эти паттерны в Data Designer до того, как тронете UI. Когда вы переименовываете таблицы или поля, перегенерируйте приложение, чтобы экраны, API и логика оставались синхронизированы, а не расходились.

Лёгкий этап ревью перед каждым релизом обычно достаточен:

  • Читаются ли имена таблиц и полей как пункты меню, заголовки колонок и фильтры?
  • Ясны ли статусы и enum‑ы без дополнительных объяснений?
  • Объясняют ли связи и внешние ключи себя (без таинственных аббревиатур)?
  • Называются ли похожие модели одинаково (одни и те же слова, одинаковый порядок)?

Со временем реальная выгода — это согласованность. Когда каждая новая модель следует одним и тем же правилам, ваши админ‑панели начинают выглядеть продуманными даже если они сгенерированы, потому что метки и списки читаются как единый продукт.

Вопросы и ответы

Почему имена в базе данных влияют на то, как админ‑панель выглядит и ощущается?

Используйте имена, которые читаются как то, чем запись является, а не что она делает. Таблица ticket или invoice превратится в понятный пункт меню, тогда как processing быстро станет запутанной, когда процесс поменяется.

Стоит ли использовать snake_case или camelCase для таблиц и столбцов?

Выберите один стиль и придерживайтесь его повсюду. Для большинства баз snake_case легче сканировать и помогает сгенерированным меткам и фильтрам не выглядеть случайно.

Как решать, когда аббревиатуры допустимы?

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

Именовать таблицы в единственном или множественном числе?

Выберите один подход и соблюдайте его: либо единственное (ticket), либо множественное (tickets). Главное, чтобы навигация и заголовки страниц не меняли стиль между модулями.

Какое простое правило для первичных и внешних ключей?

Одна простая догма: первичный ключ каждой таблицы — id, а внешние ключи выглядят как something_id. Это делает связи предсказуемыми и помогает сгенерированным формам и полям ссылок выглядеть единообразно.

Как называть many‑to‑many таблицы, чтобы UI оставался читабельным?

Именуйте чистые join‑таблицы по двум сущностям в одном согласованном порядке, например user_role или product_tag. Если у отношения появляются свои поля и смысл, переименуйте таблицу в реальное существительное, например membership или assignment, чтобы UI читался естественно.

Какие паттерны имен полей дают чистые столбцы и фильтры?

Используйте предсказуемые суффиксы, соответствующие типу и назначению данных: _at для времён, _count для счётчиков. Для булевых полей предпочитайте is_ и has_, тогда чекбоксы в сгенерированных экранах будут читаться как простые предложения.

Лучше использовать одно enum‑поле статуса или несколько булевых флагов?

Предпочитайте одно понятное поле статуса для основного жизненного цикла, например ticket_status или invoice_status, вместо нескольких пересекающихся булевых флагов. Храните значения стабильными и простыми, чтобы можно было менять текст отображения без миграций данных.

Как именовать несколько связей на одну и ту же таблицу без путаницы?

Не используйте одно и то же общее имя вроде owner_id или type, когда в разных контекстах оно означает разное. Применяйте роль‑специфичные имена: created_by_user_id, approved_by_user_id, payment_method, чтобы экраны и фильтры были понятны сами по себе.

Когда стоит переименовать таблицу или столбец и как избежать поломок в AppMaster?

Переименовывайте лучше на раннем этапе, до того как экраны, фильтры и отчёты начнут зависеть от старых имён. В AppMaster обновите имена в Data Designer и перегенерируйте приложение, чтобы UI и API оставались согласованными, а не расходились со временем.

Легко начать
Создай что-то невероятное

Экспериментируйте с AppMaster с бесплатной подпиской.
Как только вы будете готовы, вы сможете выбрать подходящий платный план.

Попробовать AppMaster