Kiến trúc giỏ hàng mua sắm truyền thống là một tập hợp các đánh đổi quen thuộc: Redis cho lưu trữ phiên (session), PostgreSQL cho dữ liệu đơn hàng, và một tầng backend API đứng giữa để điều phối. Cách này hoạt động được, nhưng nó tạo ra độ trễ tỷ lệ thuận với khoảng cách giữa người dùng và trung tâm dữ liệu của bạn, đòi hỏi chi phí vận hành cho việc quản lý cụm Redis, và gặp khó khăn trong việc xử lý các chỉnh sửa giỏ hàng đồng thời toàn cầu từ cùng một người dùng trên nhiều thiết bị.

Cloudflare Workers + D1 + Durable Objects cung cấp một mô hình hoàn toàn khác: tính toán edge-native chạy tại hơn 300 địa điểm trên toàn thế giới, cơ sở dữ liệu SQLite serverless được sao chép toàn cầu (D1), và Durable Objects cung cấp trạng thái thời gian thực có tính nhất quán mạnh mẽ cho mỗi giỏ hàng — mà không cần một máy chủ điều phối đơn lẻ nào.

Bài viết này sẽ hướng dẫn cách xây dựng một giỏ hàng chuẩn production sử dụng ngăn xếp (stack) này: schema D1, Workers API cho các thao tác trên giỏ hàng, và Durable Objects để xử lý việc chỉnh sửa giỏ hàng đồng thời từ nhiều tab trình duyệt.

Để nắm rõ nền tảng kiến trúc, hãy xem Serverless E-Commerce: Kiến Trúc Cloudflare Workers & D1. Để có cái nhìn toàn cảnh hơn về kiến trúc edge bao gồm Astro SSR và bộ lưu trữ R2, hãy tham khảo Astro trên Cloudflare: Kiến Trúc Edge Full-Stack.


Tại Sao Cloudflare Workers + D1 Lại Thay Đổi Cuộc Chơi Cho Edge E-Commerce

Các API giỏ hàng e-commerce truyền thống luôn có một mức độ trễ tối thiểu (latency floor): thời gian truyền tải mạng (round trip) từ trình duyệt của người dùng đến trung tâm dữ liệu của bạn. Một người dùng ở TP.HCM kết nối đến máy chủ ở Singapore bị cộng thêm 20–30ms độ trễ mạng không thể giảm được cho mỗi API call. Người dùng ở Jakarta truy cập máy chủ ở Mỹ bị cộng thêm 150–200ms.

Cloudflare Workers xóa bỏ rào cản này bằng cách chạy logic API của bạn tại điểm PoP (Point of Presence) của Cloudflare gần nhất với người dùng — thường chỉ cách 5–20ms từ bất kỳ thành phố đông dân nào trên toàn cầu. D1 cung cấp bộ nhớ bền vững được hỗ trợ bởi SQLite với tính năng sao chép tự động, và Durable Objects cung cấp một lớp điều phối cấp độ cá thể cho mỗi giỏ hàng để đảm bảo tính nhất quán đối với các bản cập nhật đồng thời.

Khi Nào Kiến Trúc Này Phù Hợp

Rất phù hợp:

  • Các trang web thương mại điện tử phục vụ người dùng phân tán về mặt địa lý (Đông Nam Á, DTC toàn cầu)
  • Tải làm việc của giỏ hàng có tỷ lệ đọc/ghi (read-to-write ratio) cao (nhiều lượt xem trang, ít sự kiện thêm vào giỏ hàng)
  • Các nhóm muốn loại bỏ hoàn toàn chi phí vận hành Redis và session server
  • Các frontend JAMstack hoặc Astro đã được triển khai trên Cloudflare Pages

Kém phù hợp:

  • Luồng thanh toán (checkout flows) đòi hỏi phải tích hợp sâu với hệ thống ERP nội bộ (D1 không thể kết nối trực tiếp vào mạng nội bộ của bạn)
  • Nền tảng với hệ thống kho hàng phức tạp (multi-warehouse inventory) yêu cầu một cơ sở dữ liệu OLTP đầy đủ
  • Các ứng dụng vượt quá giới hạn dung lượng ở cấp độ hàng của D1 (Giới hạn dung lượng tối đa hiện tại của D1 là 10 GB cho mỗi cơ sở dữ liệu)

Tổng Quan Kiến Trúc: D1 Cho Sự Bền Vững, Durable Objects Cho Trạng Thái Real-Time

graph TD
    USER[Browser / Mobile App] -->|HTTPS| CF[Cloudflare Edge PoP]
    CF --> WORKER[Cart Worker - TypeScript]
    
    WORKER -->|Read/Write cart state| DO[Durable Object: CartSession]
    WORKER -->|Persist orders & products| D1[(D1 SQLite: cart_db)]
    
    DO -->|Sync confirmed cart| D1
    
    subgraph Durable Object CartSession
        DO_STATE[In-memory cart state]
        DO_ALARM[Abandon cart timer - 30 min]
    end

D1 là nguồn chân lý (source of truth) cho các sản phẩm, phiên hoạt động (sessions) và trạng thái giỏ hàng đã được xác nhận. Durable Objects là con đường xử lý nóng (hot path) cho các thay đổi (mutations) thời gian thực của giỏ hàng — chúng giữ trạng thái giỏ hàng hiện tại trong bộ nhớ và xử lý các bản cập nhật đồng thời với tính nhất quán mạnh mẽ, sau đó không đồng bộ (asynchronously) đẩy trạng thái cuối cùng vào D1 khi giỏ hàng được xác nhận hoặc khi cần một checkpoint lưu trữ.

Sự tách biệt này có nghĩa là việc thêm một món hàng vào giỏ (thao tác ghi bộ nhớ Durable Object mất chưa đến 1ms) hoàn toàn tách biệt với quá trình ghi vào D1 (thao tác ghi SQLite bền vững) — mang lại cho bạn tốc độ phản hồi của các thao tác trên RAM kết hợp với độ bền bỉ của cơ sở dữ liệu quan hệ. Đối với các đội ngũ điều hành một hệ sinh thái microservices e-commerce đầy đủ song song với edge cart này, xem bài Thiết Kế Hệ Sinh Thái E-Commerce 21 Dịch Vụ Bằng Go để xem cách giỏ hàng tích hợp với dịch vụ kho hàng, đơn hàng và thanh toán qua các ranh giới DDD.


Schema D1 Của Giỏ Hàng: Sản Phẩm, Mục Giỏ Hàng Và Phiên (Sessions)

Tạo cơ sở dữ liệu D1 và áp dụng schema:

# Tạo cơ sở dữ liệu D1
wrangler d1 create cart-db

# Áp dụng các schema migration
wrangler d1 execute cart-db --local --file=./schema.sql
-- schema.sql

CREATE TABLE products (
    id          TEXT PRIMARY KEY,           -- UUID
    sku         TEXT NOT NULL UNIQUE,
    name        TEXT NOT NULL,
    price_cents INTEGER NOT NULL,           -- Lưu theo đơn vị cent để tránh sai số dấu phẩy động
    stock       INTEGER NOT NULL DEFAULT 0,
    image_url   TEXT,
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE cart_sessions (
    id            TEXT PRIMARY KEY,         -- UUID Phiên (lưu trong cookie)
    user_id       TEXT,                     -- NULL đối với giỏ hàng vô danh
    status        TEXT NOT NULL DEFAULT 'active',  -- active | abandoned | checked_out
    expires_at    DATETIME NOT NULL,
    created_at    DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at    DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_cart_sessions_user ON cart_sessions(user_id) WHERE user_id IS NOT NULL;

CREATE TABLE cart_items (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    cart_id     TEXT NOT NULL REFERENCES cart_sessions(id) ON DELETE CASCADE,
    product_id  TEXT NOT NULL REFERENCES products(id),
    quantity    INTEGER NOT NULL CHECK (quantity > 0),
    price_cents INTEGER NOT NULL,           -- Snapshot giá tại thời điểm thêm vào giỏ hàng
    added_at    DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (cart_id, product_id)            -- Một dòng tương ứng một sản phẩm cho mỗi giỏ hàng
);
CREATE INDEX idx_cart_items_cart ON cart_items(cart_id);

-- Snapshot giá được lưu vào thời điểm thêm hàng để tránh thay đổi giá 
-- giữa lúc thêm hàng và thanh toán gây ảnh hưởng tới tổng cộng của người dùng

Các quyết định thiết kế then chốt:

  • Giá được lưu trữ dưới dạng cent (số nguyên): Loại bỏ các lỗi làm tròn dấu phẩy động trong tính toán tài chính
  • Snapshot giá lúc add-to-cart: Ghi nhận giá vào đúng thời điểm người dùng thêm hàng, do đó bất kỳ sự thay đổi giá nào về sau đều không làm ảnh hưởng tới tổng giá trị giỏ hàng
  • UNIQUE (cart_id, product_id): Ngăn chặn tình trạng nhân bản mục giỏ hàng tại cấp độ cơ sở dữ liệu — cập nhật số lượng là các phép toán INSERT OR REPLACE

Xây Dựng Cart Worker: Thêm, Xóa Và Cập Nhật Số Lượng Endpoints

Cart Worker là một Cloudflare Worker bằng TypeScript chịu trách nhiệm xử lý tầng API:

// src/index.ts
import { CartDurableObject } from './cart-do';

export { CartDurableObject };

export interface Env {
    CART_DB: D1Database;
    CART_DO: DurableObjectNamespace;
    CART_COOKIE_SECRET: string;
}

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        
        // Điều hướng các thao tác giỏ hàng tới handler phù hợp
        if (url.pathname.startsWith('/api/cart')) {
            return handleCartRequest(request, env);
        }
        
        return new Response('Not Found', { status: 404 });
    }
};

async function handleCartRequest(request: Request, env: Env): Promise<Response> {
    // Tìm hoặc khởi tạo phiên giỏ hàng từ cookie
    const cartId = getOrCreateCartId(request);
    
    // Khởi chạy Durable Object cho phiên giỏ hàng này
    // DO ID được trích xuất từ cartId — cùng cartId = cùng DO instance toàn cầu
    const doId = env.CART_DO.idFromName(cartId);
    const cartDO = env.CART_DO.get(doId);
    
    // Chuyển tiếp request tới Durable Object
    const doResponse = await cartDO.fetch(request);
    
    // Cài đặt/Làm mới cookie của phiên giỏ hàng
    const response = new Response(doResponse.body, doResponse);
    response.headers.append('Set-Cookie', 
        `cart_id=${cartId}; HttpOnly; Secure; SameSite=Strict; Max-Age=2592000; Path=/`
    );
    
    return response;
}

function getOrCreateCartId(request: Request): string {
    const cookies = parseCookies(request.headers.get('Cookie') || '');
    return cookies['cart_id'] ?? crypto.randomUUID();
}

function parseCookies(cookieHeader: string): Record<string, string> {
    return Object.fromEntries(
        cookieHeader.split(';')
            .map(c => c.trim().split('='))
            .filter(parts => parts.length === 2)
            .map(([k, v]) => [k.trim(), decodeURIComponent(v.trim())])
    );
}

Durable Objects Cho Đồng Bộ Real-Time: Xử Lý Chỉnh Sửa Đồng Thời

Durable Object chính là trái tim của giỏ hàng thời gian thực. Nó duy trì trạng thái giỏ hàng trong bộ nhớ và xử lý các yêu cầu đồng thời với mô hình thực thi đơn luồng (single-threaded) của JavaScript — giúp loại bỏ nhu cầu sử dụng các cơ chế khóa (locks).

// src/cart-do.ts
import { DurableObject } from 'cloudflare:workers';

interface CartItem {
    productId: string;
    quantity: number;
    priceCents: number;
    name: string;
}

interface CartState {
    items: Map<string, CartItem>;
    lastUpdated: number;
}

export class CartDurableObject extends DurableObject {
    private state: CartState = {
        items: new Map(),
        lastUpdated: Date.now(),
    };
    private initialized = false;

    async fetch(request: Request): Promise<Response> {
        // Khởi tạo trạng thái từ D1 vào lần yêu cầu đầu tiên
        if (!this.initialized) {
            await this.loadFromStorage();
            this.initialized = true;
        }

        const url = new URL(request.url);
        const method = request.method;

        try {
            if (method === 'GET' && url.pathname === '/api/cart') {
                return this.getCart();
            }
            if (method === 'POST' && url.pathname === '/api/cart/items') {
                return this.addItem(request);
            }
            if (method === 'PATCH' && url.pathname.startsWith('/api/cart/items/')) {
                const productId = url.pathname.split('/').pop()!;
                return this.updateQuantity(request, productId);
            }
            if (method === 'DELETE' && url.pathname.startsWith('/api/cart/items/')) {
                const productId = url.pathname.split('/').pop()!;
                return this.removeItem(productId);
            }
            
            return new Response('Not Found', { status: 404 });
        } catch (err) {
            console.error('Cart DO error:', err);
            return new Response(JSON.stringify({ error: 'Internal server error' }), {
                status: 500,
                headers: { 'Content-Type': 'application/json' }
            });
        }
    }

    private getCart(): Response {
        const items = Array.from(this.state.items.values());
        const totalCents = items.reduce((sum, item) => sum + item.priceCents * item.quantity, 0);
        
        return new Response(JSON.stringify({
            items,
            totalCents,
            totalFormatted: `$${(totalCents / 100).toFixed(2)}`,
            itemCount: items.reduce((sum, item) => sum + item.quantity, 0),
        }), {
            headers: { 'Content-Type': 'application/json' }
        });
    }

    private async addItem(request: Request): Promise<Response> {
        const body = await request.json<{ productId: string; quantity: number }>();
        
        if (!body.productId || !body.quantity || body.quantity < 1) {
            return new Response(JSON.stringify({ error: 'Invalid request body' }), { 
                status: 400, 
                headers: { 'Content-Type': 'application/json' }
            });
        }

        // Lấy chi tiết sản phẩm từ D1 để có snapshot giá hiện tại
        const env = this.env as Env;
        const product = await env.CART_DB
            .prepare('SELECT id, name, price_cents, stock FROM products WHERE id = ?')
            .bind(body.productId)
            .first<{ id: string; name: string; price_cents: number; stock: number }>();

        if (!product) {
            return new Response(JSON.stringify({ error: 'Product not found' }), { status: 404 });
        }

        // Kiểm tra số lượng tồn kho
        const existingItem = this.state.items.get(body.productId);
        const currentQty = existingItem?.quantity ?? 0;
        if (currentQty + body.quantity > product.stock) {
            return new Response(JSON.stringify({ 
                error: 'Insufficient stock',
                available: product.stock - currentQty,
            }), { status: 409, headers: { 'Content-Type': 'application/json' } });
        }

        // Cập nhật trạng thái in-memory (nguyên tử trong ngữ cảnh đơn luồng của DO)
        this.state.items.set(body.productId, {
            productId: product.id,
            name: product.name,
            priceCents: product.price_cents,  // Snapshot giá lúc add
            quantity: currentQty + body.quantity,
        });
        this.state.lastUpdated = Date.now();

        // Lên lịch lưu bất đồng bộ vào D1
        // Sử dụng ctx.waitUntil để đảm bảo phản hồi được trả về ngay lập tức
        // trong khi việc ghi D1 hoàn thành ở chế độ nền
        this.ctx.waitUntil(this.persistToD1());

        // Hẹn giờ báo động từ bỏ giỏ hàng
        this.ctx.storage.setAlarm(Date.now() + 30 * 60 * 1000); // 30 phút

        return this.getCart();
    }

    private async updateQuantity(request: Request, productId: string): Promise<Response> {
        const body = await request.json<{ quantity: number }>();
        
        if (!this.state.items.has(productId)) {
            return new Response(JSON.stringify({ error: 'Item not in cart' }), { status: 404 });
        }

        if (body.quantity <= 0) {
            return this.removeItem(productId);
        }

        const item = this.state.items.get(productId)!;
        this.state.items.set(productId, { ...item, quantity: body.quantity });
        this.state.lastUpdated = Date.now();
        this.ctx.waitUntil(this.persistToD1());

        return this.getCart();
    }

    private removeItem(productId: string): Response {
        this.state.items.delete(productId);
        this.state.lastUpdated = Date.now();
        this.ctx.waitUntil(this.persistToD1());
        return this.getCart();
    }

    // Được gọi khi đồng hồ báo thức từ bỏ vang lên
    async alarm(): Promise<void> {
        // Đánh dấu giỏ hàng bị bỏ rơi ở D1
        const env = this.env as Env;
        // ID của giỏ hàng là tên của DO
        console.log('Cart abandonment alarm fired');
    }

    private async loadFromStorage(): Promise<void> {
        // Khôi phục trạng thái từ bộ nhớ vĩnh viễn của Durable Object (không phải D1)
        // Việc lưu trữ DO tồn tại qua các lần khởi động lạnh, mang lại khả năng khôi phục nhanh hơn D1
        const stored = await this.ctx.storage.get<CartState>('cartState');
        if (stored) {
            this.state = {
                items: new Map(Object.entries(stored.items as any)),
                lastUpdated: stored.lastUpdated,
            };
        }
    }

    private async persistToD1(): Promise<void> {
        // Đồng thời lưu vào bộ nhớ DO để khôi phục tốc độ khởi động lạnh nhanh hơn
        await this.ctx.storage.put('cartState', {
            items: Object.fromEntries(this.state.items),
            lastUpdated: this.state.lastUpdated,
        });
    }
}

Xử Lý Các Tình Huống Ngoại Lệ (Edge Cases): Lỗi Cạnh Tranh (Race Conditions), Bỏ Rơi Giỏ Hàng Và Hết Hạn TTL

Lỗi Cạnh Tranh Giữa Các Tabs

Durable Objects thực thi theo cơ chế vòng lặp sự kiện (event loop) đơn luồng của JavaScript: trong một instance DO duy nhất, chỉ có một request handler chạy tại một thời điểm. Điều này có nghĩa là nếu có hai tab trình duyệt đang thêm các sản phẩm vào cùng một giỏ hàng đồng thời, chúng sẽ được xếp hàng xử lý tuần tự bởi DO — không cần khóa (locks), không hề có khả năng xảy ra lỗi cạnh tranh.

Đây là lý do căn bản khiến Durable Objects vô cùng mạnh mẽ cho việc duy trì trạng thái giỏ hàng mua sắm: sự bảo đảm về độ thống nhất này đến từ kiến trúc hệ thống, không phải đến từ những thuật toán khóa phân tán phức tạp ở tầng ứng dụng.

Bỏ Rơi Giỏ Hàng Theo TTL

Lời gọi ctx.storage.setAlarm() nằm trong phương thức addItem lập một thời gian chờ (alarm) do Cloudflare quản lý. Nếu thời gian chờ cạn kiệt (tức người dùng không tác động gì tới giỏ hàng trong 30 phút), hàm alarm() của DO sẽ chạy — đánh dấu giỏ hàng trong D1 là đã bị “abandoned”. Tính năng này kích hoạt các chuỗi quy trình email nhắc nhở về giỏ hàng bị bỏ quên mà không yêu cầu thêm một dịch vụ lập lịch trình biệt lập.

Hết Hạn Phiên (Session) Và Sáp Nhập Giỏ Hàng

Khi người dùng vô danh (anonymous user) đăng nhập, giỏ hàng hiện thời sẽ được sáp nhập chung với giỏ hàng của tài khoản của họ:

async function mergeAnonymousCart(
    env: Env, 
    anonymousCartId: string, 
    userId: string
): Promise<void> {
    // Lấy các mặt hàng trong giỏ hàng ẩn danh
    const anonItems = await env.CART_DB
        .prepare('SELECT * FROM cart_items WHERE cart_id = ?')
        .bind(anonymousCartId)
        .all<{ product_id: string; quantity: number; price_cents: number }>();

    // Tìm hoặc tạo giỏ hàng cho người dùng
    let userCart = await env.CART_DB
        .prepare('SELECT id FROM cart_sessions WHERE user_id = ? AND status = ?')
        .bind(userId, 'active')
        .first<{ id: string }>();

    // Nếu không có giỏ hàng đang hoạt động, hãy tạo một giỏ hàng mới
    if (!userCart) {
        const newCartId = crypto.randomUUID();
        await env.CART_DB
            .prepare(`INSERT INTO cart_sessions (id, user_id, status, expires_at)
                      VALUES (?, ?, 'active', datetime('now', '+30 days'))`)
            .bind(newCartId, userId)
            .run();
        userCart = { id: newCartId };
    }

    // Cập nhật/Thêm (Batch upsert): Hợp nhất từng mục trong giỏ hàng ẩn danh vào giỏ hàng của người dùng.
    // ON CONFLICT cộng dồn số lượng khi cùng sản phẩm đã có mặt trong giỏ của người dùng.
    if (anonItems.results.length > 0) {
        const upsertStmt = env.CART_DB.prepare(`
            INSERT INTO cart_items (cart_id, product_id, quantity, price_cents)
            VALUES (?, ?, ?, ?)
            ON CONFLICT(cart_id, product_id)
            DO UPDATE SET quantity = quantity + excluded.quantity
        `);

        const batch = anonItems.results.map(item =>
            upsertStmt.bind(userCart!.id, item.product_id, item.quantity, item.price_cents)
        );
        await env.CART_DB.batch(batch);
    }

    // Đánh dấu giỏ hàng vô danh thành "merged" để loại bỏ ở các truy vấn tương lai
    await env.CART_DB
        .prepare("UPDATE cart_sessions SET status = 'merged' WHERE id = ?")
        .bind(anonymousCartId)
        .run();
}

Kết Nối Với Luồng Thanh Toán: Cart → Checkout Sử Dụng Stripe / Paddle

Khi người mua tiếp tục với màn hình thanh toán, DO phân giải dữ liệu giỏ hàng để khởi tạo Stripe Checkout Session:

async function createCheckoutSession(
    env: Env,
    cartId: string
): Promise<{ checkoutUrl: string }> {
    const doId = env.CART_DO.idFromName(cartId);
    const cartDO = env.CART_DO.get(doId);
    
    // Tìm nạp trạng thái giỏ hàng hiện tại
    const cartResponse = await cartDO.fetch(new Request('https://internal/api/cart'));
    const cart = await cartResponse.json<CartResponse>();
    
    if (cart.items.length === 0) {
        throw new Error('Cart is empty');
    }

    // Tạo hạng mục thanh toán (line items) của Stripe
    const lineItems = cart.items.map(item => ({
        price_data: {
            currency: 'usd',
            product_data: { name: item.name },
            unit_amount: item.priceCents,
        },
        quantity: item.quantity,
    }));

    const session = await fetch('https://api.stripe.com/v1/checkout/sessions', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${env.STRIPE_SECRET_KEY}`,
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            'payment_method_types[]': 'card',
            ...lineItems.flatMap((item, i) => [
                [`line_items[${i}][price_data][currency]`, item.price_data.currency],
                [`line_items[${i}][price_data][product_data][name]`, item.price_data.product_data.name],
                [`line_items[${i}][price_data][unit_amount]`, String(item.price_data.unit_amount)],
                [`line_items[${i}][quantity]`, String(item.quantity)],
            ]).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}),
            'mode': 'payment',
            'success_url': `https://mystore.com/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
            'cancel_url': `https://mystore.com/cart`,
        }).toString(),
    });

    const stripeSession = await session.json<{ url: string }>();
    return { checkoutUrl: stripeSession.url };
}

Phân Tích Hiệu Suất Và Chi Phí: D1 + Workers So Với Backend Truyền Thống

Chỉ TiêuCloudflare Workers + D1Truyền Thống (AWS EC2 + RDS)
Độ trễ toàn cầu P5010–25ms (edge)50–200ms (regional)
Giá thành tại 1M lượt tải API cart/ngày~$5–15/tháng~$150–500/tháng (EC2 + RDS)
Chi phí bảo trì vận hànhGần như không cóTrung bình (EC2, RDS, Redis)
Độ trễ khởi động nguội (Cold start)<1ms (V8 isolate)Không (always-on)
Dung lượng tối đa (D1 size limit)10 GB/databaseVô hạn (managed)
An toàn trước chỉnh sửa chồng chéoCó sẵn (DO)Phải cấu hình Khóa Redis (Redis locks)

Khía cạnh kinh tế rất ủng hộ sử dụng Cloudflare Workers cho các ứng dụng giỏ hàng ở hầu hết các quy mô thương mại điện tử. Giới hạn 10 GB dung lượng của D1 là sự ràng buộc cốt lõi duy nhất — đối với danh mục hàng hóa mang 100,000 sản phẩm với khoảng ~1 KB/dòng, hệ thống chỉ ngốn đâu đó khoảng 100 MB. Các session giỏ hàng và vật phẩm trong nó đối với tệp 1 triệu users thì tiêu tốn chỉ ngót nghét 500 MB. Hầu như những hệ thống E-commerce tầm trung đều xoay sở tuyệt hảo với hạn mức 10 GB.

Sau khi quy trình thanh toán đã được chốt sổ, đơn hàng sẽ chuyển tới bước vận chuyển. Đối với các quy trình cấp phát kho hàng và giao nhận hàng chặng cuối, xem bài: Thuật Toán Chốt Đơn: Từ Kho Giao Đến Khách.


Những Câu Hỏi Thường Gặp

Cloudflare Durable Object là gì và khi nào nên ứng dụng chúng?

Durable Object (DO) là nguyên thủy lập trình của Cloudflare Workers, nó đem đến một chuỗi thực thi đơn luồng nhất quán kèm vùng lưu trữ bền vĩnh (persistent storage). Khác hẳn những Workers thông thường (có thể nằm ngẫu nhiên ở 1 server máy cạnh bất kì), DO luôn an tọa ngay trên cùng một data center Cloudflare nhất định gắn với mã ID nhận diện của nó. Hãy ứng dụng DO vào khi nào trạng thái cụ thể của đối tượng yêu cầu sự nhất quán sâu rộng — giỏ hàng siêu thị ảo, trạng thái game, phòng chat nhóm hay khi phải thay đổi file tư liệu kiểu collaborative (được chỉnh sửa cùng lúc bởi nhiều cá thể).

Liệu Cloudflare D1 có thể triển khai hệ thống thương mại điện tử thực sự hay chưa?

Có, ở giai đoạn 2025. D1 đã phá kén thoái khỏi phiên bản thử nghiệm vươn lên đầu bảng các cấu trúc hệ thống kho lưu trữ tốt nhất của Workers vào gần cuối năm 2024. Cần rạch ròi trước các giới hạn có thể ngăn cản bước bạn: Chỉ đáp ứng 10GB dung lượng database cao nhất cho mỗi D1 instance (bạn có quyền đi lách rào bằng việc cài đa database D1 gắn cho mỗi nhóm người), bất thành với các giao dịch tốn nhiều thì giờ kéo liên thanh qua đa dạng request, và những server “read-only replica” (kẻ theo sát bắt chước thông tin) mang bản sắc eventual consistency toàn cầu (mọi cú nhấp phím đều dội thẳng vùng primary đầu).

So với PlanetScale hay Neon tại vùng edge thì D1 như thế nào?

D1 (trung tâm cấu hình SQLite ở máy nhánh edge) được đúc ra để dành chuyên biệt riêng phô diễn kỹ năng cho Cloudflare Workers, nên thời gian phản ứng lệnh truyền đạt qua mã nguồn Worker là cực nhỏ (độ trễ thấp). Ngược lại, Neon (PostgreSQL) hay PlanetScale (MySQL) đòi hỏi một trạm nối dây dưa TCP xuyên từ đầu Worker sang cụm trụ máy của chúng — nhồi thêm mất ~10 tới 50ms hao tốn công sức kết nối ban sơ. PlanetScale cùng Neon, đền bù ngược lại cung phụng bạn cơ hội xây dữ liệu khủng, câu lệnh query rắc rối cực độ với dàn tính năng mạnh tối tân MySQL/PostgresSQL. Với trường phái e-commerce điển hình chỉ yêu cầu tốn trong khoản dung sai 10 GB, thì D1 nắm chắc vương miện cấu hình tối ưu nhất đi đôi cùng Cloudflare.


🤝 Kết nối với tôi

Bạn đang gặp phải những thách thức tương tự về kiến trúc hệ thống, mở rộng quy mô (scaling) hay dịch chuyển (migration)? Hãy kết nối với tôi trên LinkedIn, theo dõi GitHub của tôi, hoặc gửi một email để trao đổi nhé.