Проектування архітектури веб-застосунку

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Проектування архітектури веб-застосунку
Складна
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Проектування архітектури веб-додатку

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

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

З чого почати

Перш ніж вибирати технології, відповідайте на структурні запитання:

Якої природи навантаження? Read-heavy (новинний портал, довідник) — одна стратегія кешування. Write-heavy (біржа, система моніторингу) — інша. Mixed (електронна комерція) — третя.

Яка допустима затримка? Для торгової платформи 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 'USD',
  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-діаграма, а набір перевірених рішень з обґрунтуванням компромісів.

Архітектурний огляд існуючого проекту — три–п'ять днів: аналіз кодової бази, виявлення вузьких місць, план еволюції без переписування.