Quay lại blog
ecommerce9 phút đọc

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.

V
Bởi Ventra Rocket
·Đăng ngày 25 tháng 2, 2026
#Headless Commerce#Next.js#GraphQL#Magento#Architecture

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ể.

Kiến trúc Headless Commerce: Tách frontend khỏi backend thương mại điện tử | Ventra Rocket