Миграция интернет-магазина на Headless Commerce архитектуру
Headless Commerce — разделение backend (каталог, заказы, платежи) и frontend (витрина). Backend работает как API-слой, frontend — как отдельное приложение на React, Vue, Next.js или мобильном клиенте. Это не тренд, а инженерное решение с конкретными trade-offs.
Когда миграция оправдана
Переходить стоит, если:
- Несколько витрин с общим каталогом (web + mobile app + 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 REST API
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.







