Проектирование архитектуры веб-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Проектирование архитектуры веб-приложения
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1212
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    822
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Проектирование архитектуры веб-приложения

Архитектура веб-приложения — это набор принятых решений, которые сложно или дорого изменить позже. Выбор базы данных, способ организации сервисов, стратегия масштабирования — каждое из этих решений закладывает границы того, что можно будет построить через два года без переписывания.

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

С чего начинается проектирование

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

Какой характер нагрузки? Read-heavy (новостной портал, справочник) — одна стратегия кеширования. Write-heavy (биржа, система мониторинга) — другая. Mixed (e-commerce) — третья.

Какова допустимая задержка? Для торговой платформы 100ms — катастрофа. Для CMS — приемлемо.

Есть ли пики? Если трафик равномерный — проще. Если раз в году Black Friday даёт 100x нагрузки — нужен autoscaling или буферизация через очереди.

Где границы транзакционности? Можно ли разделить базу данных или всё завязано на ACID?

Слои типичного веб-приложения

[Клиент]
    ↓ HTTPS
[CDN / Edge Cache]
    ↓ Cache Miss
[Load Balancer]
    ↓
[Приложение — N инстансов]
    ├── [Кэш — Redis/Memcached]
    ├── [Очередь — RabbitMQ/Kafka]
    └── [База данных — Primary + Replica]
              ↓
         [Object Storage — S3]

Каждый слой решает одну задачу. CDN — статика и кэш на краю. Load Balancer — распределение и терминирование TLS. Приложение — бизнес-логика. Redis — горячие данные и сессии. Очередь — асинхронные задачи, которые нельзя выполнить в рамках HTTP-запроса.

Монолит против микросервисов

Стандартный вопрос, на который слишком часто дают стандартный неправильный ответ.

Монолит — правильный выбор для большинства новых проектов с командой до 15–20 человек. Причины:

  • Одна транзакция на несколько агрегатов без saga-паттернов
  • Простой деплой и наблюдаемость (один процесс — один лог)
  • Рефакторинг без сетевых контрактов
  • Нет проблемы согласованности при распределённых данных

Переход к микросервисам оправдан, когда команды работают над независимыми доменами, деплои начинают мешать друг другу, и конкретные сервисы требуют разного масштабирования (например, сервис обработки изображений vs CRUD API).

Монолит с чёткими границами модулей:

src/
├── modules/
│   ├── catalog/       # продукты, категории, поиск
│   │   ├── domain/
│   │   ├── application/
│   │   └── infrastructure/
│   ├── orders/        # заказы, корзина, checkout
│   ├── users/         # аутентификация, профили
│   └── notifications/ # email, push, sms
└── shared/
    ├── events/        # доменные события (для будущей декомпозиции)
    └── infrastructure/ # HTTP клиент, логгер

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

Выбор базы данных

PostgreSQL подходит для 90% задач. Релационная модель, JSONB для гибких данных, полнотекстовый поиск, партицирование, репликация — всё из коробки. Начинать с PostgreSQL и менять при конкретных проблемах — правильная стратегия.

Дополнительные хранилища по назначению:

Задача Инструмент
Сессии, кэш, rate limiting Redis
Полнотекстовый поиск с фасетами Elasticsearch / OpenSearch
Аналитика и OLAP ClickHouse
Граф-данные Neo4j / PostgreSQL с recursive CTE
Очереди сообщений Redis Streams, RabbitMQ, Kafka

Схема данных и миграции

Ранние ошибки в схеме данных — самые дорогие. Несколько принципов:

Используйте UUID вместо serial/bigint для ID, если планируется горизонтальное масштабирование или публичный API. UUID v7 сортируемый и хорошо работает как кластерный индекс.

-- UUID v7 генерируется в приложении
CREATE TABLE orders (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     UUID NOT NULL REFERENCES users(id),
  status      TEXT NOT NULL DEFAULT 'draft',
  total_cents INTEGER NOT NULL,
  currency    CHAR(3) NOT NULL DEFAULT 'RUB',
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Триггер для updated_at (лучше чем в ORM)
CREATE TRIGGER set_updated_at
BEFORE UPDATE ON orders
FOR EACH ROW EXECUTE FUNCTION trigger_set_timestamp();

Миграции — только вперёд, не backward-incompatible. Цикл: добавляем колонку (nullable) → деплоим код, который её пишет → делаем NOT NULL с DEFAULT → удаляем старую колонку.

Кэширование

Три уровня:

HTTP кэш — для публичных ресурсов. Cache-Control: public, max-age=3600, stale-while-revalidate=86400. CDN кэширует на краю, браузер — локально.

Application cache — Redis для данных, которые дорого вычислять. Паттерн Cache-Aside:

async function getProduct(id: string): Promise<Product> {
  const cached = await redis.get(`product:${id}`);
  if (cached) return JSON.parse(cached);

  const product = await db.product.findUniqueOrThrow({ where: { id } });

  await redis.set(`product:${id}`, JSON.stringify(product), 'EX', 3600);
  return product;
}

// Инвалидация при обновлении
async function updateProduct(id: string, data: Partial<Product>) {
  const updated = await db.product.update({ where: { id }, data });
  await redis.del(`product:${id}`);
  // Инвалидируем зависимые ключи
  await redis.del(`category:products:${updated.categoryId}`);
  return updated;
}

Query cache — PostgreSQL сам кэширует планы запросов. Правильные индексы важнее любого application-уровня.

Асинхронная обработка

Всё, что занимает больше 200ms или может упасть, должно идти в очередь:

  • Отправка email
  • Генерация PDF/изображений
  • Интеграции с внешними сервисами
  • Импорт данных
  • Пересчёт агрегатов
// Паттерн: API принимает, ставит в очередь, отвечает 202
app.post('/api/orders/:id/invoice', async (req, res) => {
  const { id } = req.params;

  await queue.add('generate-invoice', {
    orderId: id,
    userId: req.user.id,
  }, {
    attempts: 3,
    backoff: { type: 'exponential', delay: 2000 },
  });

  res.status(202).json({ message: 'Счёт генерируется, пришлём на email' });
});

Наблюдаемость

Три столпа: логи, метрики, трассировки.

// Структурированные логи (Pino)
import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
  formatters: {
    level: (label) => ({ level: label }),
  },
});

// Привязываем request-id ко всем логам в рамках запроса
app.use((req, res, next) => {
  req.log = logger.child({
    requestId: req.headers['x-request-id'] ?? crypto.randomUUID(),
    method: req.method,
    path: req.path,
  });
  next();
});

Метрики через Prometheus-формат: /metrics endpoint с RED-метриками (Rate, Errors, Duration) на каждый роут.

Сроки

Проектирование архитектуры — не одноразовый документ, а итеративный процесс. Первоначальное проектирование для нового продукта: одна-две недели на исследование требований, ADR (Architecture Decision Records) по ключевым решениям, схему данных, выбор технологического стека. Результат — не Visio-диаграмма, а набор проверяемых решений с обоснованием компромиссов.

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