Как создать платформу с несколькими приложениями на едином коде
5 мин. чтенияMohammad Shaker

Как создать платформу с несколькими приложениями на едином коде

Alphazed объединяет 7+ образовательных приложений на едином backend и Flutter-коде с отдельными настройками и базами.

Engineering

Короткий ответ

Alphazed объединяет 7+ образовательных приложений на едином backend и Flutter-коде с отдельными настройками и базами.

Как мы создали платформу с несколькими приложениями на едином коде

Alphazed управляет более 7 образовательными приложениями (Amal, Thurayya, Qais, KidElite, Alphazed School, Alphazed Montessori и другие) на едином исходном коде backend и общем Flutter-фреймворке для мобильных приложений. Каждое приложение получает собственные таблицы базы данных (с префиксом), настройки, push-уведомления, шаблоны писем и контент — при этом используется общая аутентификация (AWS Cognito), инфраструктура аналитики и основные алгоритмы обучения.

Backend: выбор приложения во время выполнения

Как это работает

При деплое переменная окружения выбирает приложение:

# Деплой Amal
export APP_NAME=amal
serverless deploy

# Деплой Thurayya
export APP_NAME=thurayya
serverless deploy

# Деплой Qais
export APP_NAME=qais
serverless deploy

Каждый деплой создает независимые функции Lambda, маршруты API Gateway и мониторинг — при этом все они подключаются к одному и тому же backend-коду.

Приоритет конфигураций с тремя уровнями

# src/config.py
import os

app_name = os.getenv('APP_NAME', 'amal')

# Уровень 1: точное совпадение приложения
config = load_json(f'config/{app_name}.json')

# Уровень 2: семейство приложений (если точной конфигурации нет)
if not config:
    family = app_name.split('_')[0]  # 'amal_beta' → 'amal'
    config = load_json(f'config/{family}.json')

# Уровень 3: по умолчанию
if not config:
    config = load_json('config/default.json')

Конфигурация для отдельного приложения (config/amal.json)

{
  "app_name": "amal",
  "app_id": "com.alphazed.amal",
  "database": {
    "table_prefix": "amal_"
  },
  "email": {
    "template_dir": "templates/amal",
    "from_address": "amal@alphazed.com",
    "from_name": "Amal Team"
  },
  "push_notifications": {
    "firebase_project": "alphazed-amal",
    "apns_certificate": "certs/amal.pem"
  },
  "content": {
    "s3_bucket": "amal-content-production",
    "curriculum_id": "amal_v3_arabic"
  },
  "feature_flags": {
    "enable_tajweed_feedback": false,
    "enable_noorani_qaida": false,
    "enable_juz_amma": false,
    "enable_creature_building": true,
    "enable_physics_games": true
  },
  "pricing": {
    "monthly_usd": 6.99,
    "yearly_usd": 67.99,
    "trial_days": 14
  }
}

Общие и отдельные ресурсы

РесурсОбщийОтдельныйПричина
Код Lambda✓ Да✗ НетЕдиный код с ветвлением по APP_NAME
RDS-инстанс✓ Да✗ Нет (префиксы в таблицах)Снижение стоимости, единый бэкап
Контент в S3✗ Нет✓ ДаУ каждого приложения своя учебная программа
AWS Cognito✓ Да✗ Нет (через app_id)Централизованная аутентификация, разграничение по app_id
Аналитика✓ Да✗ Нет (разделена по приложениям)Единый pipeline, отдельные данные
Firebase Messaging✗ Нет✓ ДаОтдельные APNs и FCM для каждого приложения

Frontend: Flutter-мультиприложения

Общее ядро (packages/alphazed_common)

  • Управление состоянием (Riverpod)
  • Обработка аудио
  • Библиотека анимаций (интеграция Rive)
  • Дизайн-система (цвета, типографика, виджеты)
  • Авторизационные потоки
  • Клиент аналитики

Слои, специфичные для каждого приложения (apps/amal, apps/thurayya и др.)

apps/
  ├── amal/
  │   ├── lib/
  │   │   ├── main.dart (точка входа в приложение)
  │   │   ├── config/
  │   │   │   ├── curriculum.json (структура уроков Amal)
  │   │   │   ├── colors.dart (тема Amal)
  │   │   │   └── characters.json (настройка аватаров)
  │   │   └── screens/ (экраны для Amal)
  │   └── pubspec.yaml (зависимости Amal)
  │
  ├── thurayya/
  │   ├── lib/
  │   │   ├── main.dart (отдельная точка входа)
  │   │   ├── config/
  │   │   │   ├── curriculum.json (структура Juz Amma)
  │   │   │   ├── colors.dart (тема Thurayya)
  │   │   │   └── characters.json (настройка аватаров)
  │   │   └── screens/ (экраны для Thurayya)
  │   └── pubspec.yaml (зависимости Thurayya)
  │
  └── packages/
      └── alphazed_common/ (общий код)

Конфигурация на этапе сборки

# Сборка Amal
flutter build apk --dart-define=APP_NAME=amal --dart-define=CURRICULUM=amal_v3

# Сборка Thurayya
flutter build apk --dart-define=APP_NAME=thurayya --dart-define=CURRICULUM=thurayya_juzamma

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

Деплой: независимые стэки

Конфигурация Serverless Framework

# serverless.yml
service: alphazed-backend

custom:
  pythonRequirements:
    dockerizePip: true
  app_name: ${env:APP_NAME}

functions:
  # Эти функции разворачиваются для каждого APP_NAME
  get_user:
    handler: src/handlers/user.get_user
    events:
      - http:
          path: app/{app_name}/user/{user_id}
          method: get
  
  content_duo:
    handler: src/handlers/content.generate_content_duo
    timeout: 20
    events:
      - http:
          path: app/{app_name}/content_duo
          method: post

resources:
  Resources:
    # Для каждого приложения создается своя группа логов CloudWatch
    ${self:custom.app_name}LogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: /aws/lambda/alphazed-${self:custom.app_name}
        RetentionInDays: 30

При деплое с APP_NAME=amal CloudFormation создаёт:

  • Lambda-функции с префиксом alphazed-amal-*
  • Маршруты API Gateway с путями /amal/*
  • Логи CloudWatch в группе /aws/lambda/alphazed-amal
  • Независимое масштабирование и мониторинг

Почему это важно

Для продуктовых команд

  • Запуск нового приложения за недели, а не месяцы благодаря общей инфраструктуре
  • Параллельность функций: исправления ошибок и улучшения алгоритмов сразу для всех приложений
  • A/B тестирование фич на одном приложении перед массовым релизом

Для инженерных команд

  • Единая кодовая база, один набор тестов, единый CI/CD
  • Общие алгоритмы обучения (интервальное повторение и микс контента) выгодны всем приложениям
  • Удобство управления инфраструктурой одной командой

Для бюджета

  • Общий RDS — в разы дешевле, чем 7 отдельных баз
  • Общий Cognito — одна система авторизации для всех
  • Общая аналитика — одна сборка для 7 приложений
  • Экономия около 40-60 тыс. долларов в год по сравнению с раздельными backend

Задачи и решения

Проблема 1: конфликты схем базы данных

  • Amal использует таблицу amal_user_memory
  • Thurayya использует thurayya_user_memory с другими полями для запоминания Корана
  • Решение: миграции зависят от APP_NAME. Например, migrations/001_amal_user_memory.sql выполняется только для APP_NAME=amal

Проблема 2: разные наборы функций

  • В Thurayya есть обратная связь по таджвиду, в Amal — нет
  • В Amal есть игры по физике, в Thurayya — нет
  • Решение: Feature flags в конфигурации (enable_tajweed_feedback, enable_physics_games) управляют доступностью функций

Проблема 3: независимое масштабирование

  • Если у Amal резкий рост трафика, а у Thurayya затишье
  • Общий пул Lambda-функций конкурирует за ресурсы
  • Решение: политики масштабирования CloudWatch для каждого приложения. При достижении лимита у amal-* происходит авторасширение, не затрагивая Thurayya

FAQ

Вопрос: Не вызывает ли общий кодовую базу сильную связанность приложений?
Ответ: Нет. Код каждого приложения находится в отдельных папках (apps/amal, apps/thurayya). Общий код — в packages/alphazed_common, с явным контролем версий. Сильная связанность — признак плохого дизайна, мы проверяем это на ревью кода.

Вопрос: Что если одному приложению нужна несовместимая с другими версия API?
Ответ: У нас версии API для каждого приложения: /amal/v1/*, /thurayya/v1/*. Они могут обновляться независимо. Старые версии поддерживаются 12 месяцев для плавного перехода.

Вопрос: Можно ли использовать общих пользователей для разных приложений?
Ответ: По умолчанию нет. У каждого приложения своя таблица пользователей с префиксом. Если родитель хочет подписаться и на Amal, и на Thurayya, нужно создать отдельные аккаунты (планируется функция «семейной» связки пользователей).

ПоделитьсяTwitterLinkedInWhatsApp

Похожие статьи