Kiến trúc Headless Commerce: Tách frontend khỏi backend thương mại điện tử
Phân tích kiến trúc headless commerce, so sánh với monolithic, và hướng dẫn xây dựng storefront Next.js kết nối Magento hoặc Shopify GraphQL API.
Headless commerce tách biệt hoàn toàn frontend (presentation layer) khỏi backend commerce engine. Thay vì dùng theme có sẵn của Magento hay Shopify, bạn xây dựng frontend riêng với Next.js và giao tiếp với backend qua API. Kết quả là trải nghiệm người dùng vượt trội và tốc độ phát triển nhanh hơn.
1. Monolithic vs Headless — Khi nào nên chọn?
Monolithic phù hợp khi:
- Team nhỏ, timeline ngắn
- Ít customization phức tạp
- Budget hạn chế
Headless phù hợp khi:
- Cần tốc độ tải trang tối đa (Core Web Vitals)
- Nhiều frontend: web, mobile app, kiosk, smart TV
- Team có kỹ sư React/Next.js
- Cần A/B test frontend độc lập với backend
2. Kiến trúc tổng quan
┌─────────────────────────────────────────────┐
│ Frontend Layer │
│ Next.js App (SSG/ISR/SSR hybrid) │
│ - Product pages (SSG + ISR) │
│ - Search (SSR) │
│ - Cart & Checkout (CSR) │
└──────────────┬──────────────────────────────┘
│ GraphQL / REST
┌──────────────▼──────────────────────────────┐
│ API Gateway (optional) │
│ - Rate limiting, auth, caching │
└──────────────┬──────────────────────────────┘
│
┌──────────────▼──────────────────────────────┐
│ Commerce Backend │
│ Magento 2 GraphQL / Shopify Storefront API │
│ - Catalog, Inventory, Pricing │
│ - Orders, Cart, Customer │
└─────────────────────────────────────────────┘
3. Kết nối Magento 2 GraphQL từ Next.js
// lib/magento-client.ts
import { GraphQLClient } from 'graphql-request';
const MAGENTO_URL = process.env.MAGENTO_GRAPHQL_URL!;
export const magentoClient = new GraphQLClient(MAGENTO_URL, {
headers: {
'Content-Type': 'application/json',
'Store': process.env.MAGENTO_STORE_CODE ?? 'default',
},
});
// Lấy thông tin sản phẩm
const GET_PRODUCT = `
query GetProduct($urlKey: String!) {
products(filter: { url_key: { eq: $urlKey } }) {
items {
id
sku
name
price_range {
minimum_price {
regular_price { value currency }
final_price { value currency }
discount { percent_off amount_off }
}
}
description { html }
media_gallery {
url
label
position
}
... on ConfigurableProduct {
configurable_options {
attribute_code
label
values { uid label swatch_data { value } }
}
variants {
attributes { uid code label value_index }
product { sku stock_status price_range { minimum_price { final_price { value } } } }
}
}
}
}
}
`;
export async function getProduct(urlKey: string) {
const data = await magentoClient.request(GET_PRODUCT, { urlKey });
return data.products.items[0] ?? null;
}
4. Static Generation với ISR
// app/products/[slug]/page.tsx
import { getProduct, getAllProductSlugs } from '@/lib/magento-client';
export async function generateStaticParams() {
const slugs = await getAllProductSlugs();
return slugs.map(slug => ({ slug }));
}
export const revalidate = 3600; // ISR: tái tạo mỗi 1 giờ
export default async function ProductPage({
params
}: {
params: { slug: string }
}) {
const product = await getProduct(params.slug);
if (!product) notFound();
return <ProductDetailView product={product} />;
}
5. Cart Management với Server Actions
// app/actions/cart.ts
'use server';
import { cookies } from 'next/headers';
import { magentoClient } from '@/lib/magento-client';
const CREATE_CART = `mutation { createGuestCart { cart { id } } }`;
const ADD_TO_CART = `
mutation AddToCart($cartId: String!, $sku: String!, $qty: Float!) {
addSimpleProductsToCart(input: {
cart_id: $cartId
cart_items: [{ data: { sku: $sku, quantity: $qty } }]
}) {
cart {
total_quantity
prices { grand_total { value currency } }
}
}
}
`;
export async function addToCart(sku: string, qty: number = 1) {
const cookieStore = await cookies();
let cartId = cookieStore.get('cart_id')?.value;
if (!cartId) {
const data = await magentoClient.request(CREATE_CART);
cartId = data.createGuestCart.cart.id;
cookieStore.set('cart_id', cartId, { httpOnly: true, secure: true });
}
return magentoClient.request(ADD_TO_CART, { cartId, sku, qty });
}
6. Performance: Core Web Vitals
Headless Next.js cần tối ưu để đạt điểm Lighthouse cao:
// Lazy load heavy components
const ProductGallery = dynamic(() => import('./ProductGallery'), {
loading: () => <GallerySkeleton />,
});
// Preload critical images
import { unstable_noStore as noStore } from 'next/cache';
// Image optimization
<Image
src={product.mainImage}
alt={product.name}
width={800}
height={800}
priority={true} // LCP image
sizes="(max-width: 768px) 100vw, 50vw"
quality={85}
/>
Kết luận
Headless commerce mang lại lợi thế cạnh tranh rõ ràng về tốc độ và developer experience. Tuy nhiên, đòi hỏi team có kinh nghiệm với cả frontend React và backend commerce API. Ventra Rocket đã triển khai thành công nhiều dự án headless commerce, giúp khách hàng đạt Lighthouse score trên 90 và tăng conversion rate đáng kể.