Міграція інтернет-магазину на Headless Commerce архітектуру
Headless Commerce — розділення backend (каталог, замовлення, платежі) та frontend (витрина). Backend працює як API-слой, frontend — як окреме додаток на React, Vue, Next.js або мобільному клієнті. Це не тренд, а інженерне рішення з конкретними trade-offs.
Коли міграція виправдана
Переходити варто, якщо:
- Кілька витрин з загальним каталогом (web + мобільний додаток + B2B-портал)
- Поточний моноліт не витримує навантаження на рендеринг сторінок
- Frontend-команда хочет незалежний деплой без синхронізації з backend
- Потрібна максимальна швидкість завантаження (SSG для каталогу через Next.js)
Переходити не варто, якщо:
- Один простий магазин без спеціальних вимог до продуктивності
- Немає окремої frontend-команди
- Терміни виходу на ринок критичні (міграція займає 3-6 місяців)
Варіанти backend для headless
| Платформа | API | Особливості |
|---|---|---|
| Bagisto | REST + GraphQL | Laravel, open-source, повний контроль |
| Medusa.js | REST | Node.js, швидка кастомізація |
| Vendure | GraphQL | TypeScript, плагін-архітектура |
| Shopify | GraphQL Storefront API | SaaS, обмежена кастомізація |
| WooCommerce | REST (WP REST API) | Перевірений, але повільний |
| Magento 2 | REST + GraphQL | 企業, висока складність |
Архітектура після міграції
┌─────────────────────────────────────────────────────┐
│ CDN (Cloudflare) │
└────────────┬──────────────────────┬─────────────────┘
│ │
┌────────▼───────┐ ┌─────────▼────────┐
│ Next.js (SSG) │ │ React SPA (CSR) │
│ Storefront │ │ B2B Portal │
└────────┬───────┘ └─────────┬────────┘
│ │
└──────────┬───────────┘
│ REST/GraphQL
┌─────────▼──────────┐
│ API Gateway │
│ (Kong / Nginx) │
└─────────┬──────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌──────▼──────┐ ┌─────▼─────┐ ┌────▼─────┐
│ Commerce │ │ Search │ │ CMS │
│ Backend │ │ (ES/ │ │ (Strapi/ │
│ (Bagisto) │ │ Typesens)│ │ Sanity) │
└─────────────┘ └───────────┘ └──────────┘
Етапи міграції
Фаза 1: Аудит та API-дизайн (2-3 тижні)
Інвентаризація поточного моноліту:
- Список всіх сутностей (товари, категорії, замовлення, клієнти, купони)
- Залежності між сутностями
- Кастомні поля та логіка, не покриті стандартним API
Дизайн API-контрактів: OpenAPI 3.0 специфікація до початку розробки. Це дозволяє frontend-команді почати роботу паралельно.
Фаза 2: Побудова API-слоя (4-8 тижнів)
Для Bagisto: GraphQL доступен через пакет bagisto/graphql-api:
composer require bagisto/graphql-api
php artisan vendor:publish --provider="Webkul\GraphQLAPI\Providers\GraphQLAPIServiceProvider"
Приклад запиту каталогу:
query GetProducts($categoryId: ID, $page: Int) {
products(
categoryId: $categoryId
page: $page
limit: 24
) {
data {
id
sku
name
price
images {
url
altText
}
variants {
id
price
attributes {
code
value
}
}
}
paginatorInfo {
currentPage
lastPage
total
}
}
}
Фаза 3: Strangler Fig — поступова заміна
Пряма заміна моноліту рискована. Використовується паттерн Strangler Fig:
- Новий headless backend розгортається паралельно
- Nginx/API Gateway роутить запити: частина на старий моноліт, частина на новий API
- Сторінки мігрують ітеративно: спочатку каталог, потім checkout, потім особистий кабінет
- Після повної міграції трафіку моноліт вимикається
# Переключення трафіку в nginx
location /api/v1/ {
proxy_pass http://new-api-backend:8000;
}
location / {
proxy_pass http://old-monolith:80;
}
Фаза 4: Міграція даних
Якщо змінюється платформа (наприклад, WooCommerce → Bagisto):
// Artisan command для імпорту товарів з WooCommerce
class ImportWooCommerceProducts extends Command
{
public function handle(): void
{
$client = new WooCommerceClient(config('woocommerce'));
$page = 1;
do {
$products = $client->get('products', ['per_page' => 100, 'page' => $page]);
foreach ($products as $product) {
ImportProductJob::dispatch($product)->onQueue('import');
}
$page++;
} while (count($products) === 100);
}
}
Фаза 5: Frontend-додаток (6-10 тижнів паралельно)
Next.js-витрина з ISR (Incremental Static Regeneration) для сторінок каталогу:
// pages/products/[slug].tsx
export async function getStaticProps({ params }) {
const product = await commerceClient.getProduct(params.slug);
return {
props: { product },
revalidate: 3600, // Оновлення кеша щогодини
};
}
export async function getStaticPaths() {
const slugs = await commerceClient.getAllProductSlugs();
return {
paths: slugs.map(slug => ({ params: { slug } })),
fallback: 'blocking',
};
}
Фаза 6: Стан кошика та авторизація
У headless-архітектурі кошик живе в API, не в сесії. Клієнт зберігає cart_token у localStorage:
const useCart = () => {
const [cartToken, setCartToken] = useLocalStorage<string>('cart_token', '');
const addToCart = async (productId: string, qty: number) => {
const response = await api.post('/api/v1/checkout/cart/add', {
product_id: productId,
quantity: qty,
}, {
headers: cartToken ? { 'cart-token': cartToken } : {},
});
if (response.data.token) {
setCartToken(response.data.token);
}
};
};
Продуктивність після міграції
| Метрика | Моноліт (PHP SSR) | Headless (Next.js ISR) |
|---|---|---|
| TTFB (каталог) | 400-800ms | 20-50ms (CDN) |
| LCP | 2.5-4s | 0.8-1.5s |
| Деплой frontend | З backend | Незалежний |
| A/B тестування | Складно | Edge Middleware |
Загальний термін міграції
Середній магазин (10-50k SKU, стандартний checkout): 3-5 місяців при двох паралельних командах. Критичний шлях — API-дизайн та покриття тестами контрактів між frontend та backend.







