Marais De France

Chi tiết dự án
Những thách thức kỹ thuật & công nghệ đáng tự hào trong dự án MF PARIS
MF PARIS không chỉ là một website thương mại điện tử thông thường. Đây là dự án mình xây dựng lại gần như toàn bộ từ nền tảng cũ sang một hệ thống hiện đại hơn, có khả năng mở rộng tốt hơn, tốc độ nhanh hơn và phục vụ thực tế cho hoạt động bán hàng nước hoa, mỹ phẩm, dược mỹ phẩm và thực phẩm chức năng cao cấp.
Điểm đáng tự hào nhất của dự án nằm ở việc mình không chỉ làm phần giao diện, mà còn xử lý toàn bộ các bài toán phía sau: migration dữ liệu lớn, thiết kế cấu trúc CMS, tối ưu hiệu năng, realtime chat, giỏ hàng thông minh, thanh toán Fundiin và triển khai production trên VPS.
1. Chuyển đổi dữ liệu từ website cũ sang hệ thống mới
Một trong những thử thách lớn nhất là chuyển dữ liệu từ website cũ sang hệ thống mới dùng Payload CMS.
Website cũ chứa nhiều nội dung HTML không đồng nhất, bao gồm mô tả sản phẩm, bảng thông tin, hình ảnh, heading, đoạn văn và các block nội dung đã được viết theo cấu trúc cũ. Nếu import trực tiếp, dữ liệu sẽ khó quản lý, khó chỉnh sửa và không tận dụng được sức mạnh của CMS mới.
Vì vậy, mình phải xử lý lại dữ liệu theo hướng có cấu trúc hơn:
- Chuyển nội dung HTML cũ sang định dạng phù hợp với CMS mới.
- Làm sạch các thẻ HTML dư thừa.
- Tách nội dung theo heading để tạo bố cục dễ đọc hơn.
- Chuẩn hóa mô tả sản phẩm.
- Giữ lại dữ liệu quan trọng cho SEO.
- Import hình ảnh, thương hiệu, danh mục và thông tin sản phẩm.
- Thiết kế lại schema để phù hợp với sản phẩm đơn, sản phẩm nhiều biến thể và các nhóm hàng khác nhau.
Điều khó ở phần này là dữ liệu cũ không hoàn toàn sạch. Mỗi sản phẩm có thể có cấu trúc nội dung khác nhau, nên việc migration không thể chỉ đơn giản là copy/paste. Mình phải xây dựng logic xử lý dữ liệu để chuyển từ nội dung cũ sang hệ thống mới một cách tự động nhưng vẫn giữ được tính chính xác.
2. Thiết kế hệ thống CMS linh hoạt cho nhiều ngành hàng
MF PARIS không chỉ bán một loại sản phẩm. Website cần quản lý nhiều nhóm hàng khác nhau như:
- Nước hoa
- Mỹ phẩm
- Dược mỹ phẩm
- Thực phẩm chức năng
- Sản phẩm mẹ và bé
- Sản phẩm chăm sóc cá nhân
Mỗi ngành hàng lại có cách trình bày thông tin riêng. Ví dụ nước hoa cần dung tích, nồng độ, giới tính, nhóm hương. Dược mỹ phẩm cần loại da, công dụng, thành phần nổi bật. Thực phẩm chức năng cần đối tượng sử dụng, cách dùng, quy cách và lưu ý.
Vì vậy, mình xây dựng hệ thống sản phẩm theo hướng linh hoạt:
- Hỗ trợ sản phẩm đơn.
- Hỗ trợ sản phẩm nhiều biến thể.
- Mỗi biến thể có giá, tồn kho, SKU và hình ảnh riêng.
- Cho phép mở rộng thông tin sản phẩm theo từng nhóm hàng.
- Dễ quản lý trong Payload CMS.
- Phù hợp cho đội vận hành cập nhật sản phẩm hằng ngày.
Điểm đáng tự hào là hệ thống này không bị bó cứng theo một loại sản phẩm duy nhất, mà có thể mở rộng cho nhiều ngành hàng bán lẻ khác nhau.
3. Xử lý giỏ hàng thông minh và tồn kho theo biến thể
Một bài toán thực tế trong thương mại điện tử là giỏ hàng không chỉ lưu sản phẩm, mà còn phải lưu đúng biến thể sản phẩm.
Ví dụ cùng một chai nước hoa có nhiều dung tích khác nhau như 10ml, 30ml, 50ml, 100ml. Mỗi dung tích có giá và tồn kho riêng. Nếu xử lý không kỹ, khách có thể mua nhầm giá, mua vượt tồn kho hoặc đổi biến thể nhưng hệ thống vẫn giữ dữ liệu cũ.
Mình đã xây dựng giỏ hàng bằng Zustand Persist, kết hợp với API validate tồn kho từ server.
Các vấn đề đã xử lý:
- Không cho thêm sản phẩm hết hàng vào giỏ.
- Không cho tăng số lượng vượt tồn kho.
- Khi đổi biến thể, cập nhật lại giá, SKU, tồn kho và hình ảnh.
- Khi admin thay đổi tồn kho trong CMS, giỏ hàng phía khách sẽ được kiểm tra lại.
- Nếu sản phẩm trong giỏ đã hết hàng, hệ thống giữ lại để cảnh báo thay vì xóa im lặng.
- Checkout kiểm tra lại dữ liệu để tránh đơn hàng sai.
Đây là phần rất quan trọng vì localStorage chỉ lưu dữ liệu tại thời điểm khách thêm sản phẩm. Nếu không có bước validate lại từ database, giỏ hàng rất dễ bị sai giá hoặc sai tồn kho.
4. Lựa chọn công nghệ cho hệ thống chat realtime
Ban đầu, một website bán hàng có thể dùng các công cụ chat bên thứ ba. Nhưng với MF PARIS, mình muốn hệ thống chat gắn trực tiếp vào website, có lịch sử hội thoại, có định danh khách hàng và có giao diện quản trị riêng cho admin.
Vì vậy, mình lựa chọn xây dựng hệ thống chat realtime bằng Socket.io.
Lý do chọn realtime chat:
- Khách hàng cần được phản hồi nhanh khi đang xem sản phẩm.
- Admin có thể tư vấn trực tiếp ngay trong hệ thống quản trị.
- Tin nhắn được lưu vào database, không bị mất khi reload.
- Có thể định danh khách hàng bằng profile riêng.
- Khách đăng nhập lại vẫn xem được lịch sử trò chuyện.
- Chủ động kiểm soát dữ liệu thay vì phụ thuộc hoàn toàn vào nền tảng bên ngoài.
Hệ thống chat được thiết kế theo mô hình tách riêng:
Next.js Web App
↓
Payload CMS lưu tin nhắn
↓
Socket Server riêng
↓
Admin Chat Center + Customer Live Chat
Socket server chạy riêng bằng Node.js + Express + Socket.io, có domain riêng và được bảo vệ bằng internal token. Điều này giúp hệ thống realtime ổn định hơn, dễ debug hơn và không làm nặng app chính.
Một điểm khó khi đưa lên production là local chạy bình thường nhưng VPS lại có nhiều vấn đề liên quan đến domain, CORS, biến môi trường, reverse proxy và token bảo mật. Mình đã xử lý bằng cách tách rõ:
NEXT_PUBLIC_SOCKET_URLcho browser.SOCKET_SERVER_URLcho server.SOCKET_INTERNAL_TOKENđể bảo vệ endpoint broadcast.- Socket server chạy riêng trên domain
socket.maraisdefrance.vn.
5. Xử lý dữ liệu lớn và tối ưu tốc độ website
MF PARIS có lượng dữ liệu sản phẩm và hình ảnh lớn. Website không thể chỉ đẹp, mà còn phải nhanh, nhẹ và đạt điểm tốt trên PageSpeed.
Các vấn đề cần xử lý:
- Nhiều sản phẩm.
- Nhiều danh mục.
- Nhiều thương hiệu.
- Hàng nghìn hình ảnh sản phẩm.
- Trang sản phẩm cần load nhanh trên cả mobile.
- Giao diện phải đẹp nhưng không được quá nặng JavaScript.
Mình đã tối ưu theo nhiều hướng:
- Dùng Next.js App Router để tận dụng Server Components.
- Hạn chế client component ở những nơi không cần thiết.
- Tối ưu ảnh sản phẩm bằng Next Image.
- Dùng lazy loading cho hình ảnh.
- Chuẩn hóa tỉ lệ ảnh sản phẩm 1:1 để tránh layout shift.
- Tách socket server khỏi web server để giảm tải.
- Phân trang dữ liệu ở các API lớn.
- Chỉ fetch dữ liệu cần thiết cho từng màn hình.
- Tối ưu UI để hạn chế animation nặng.
- Thiết kế layout ổn định, tránh CLS.
Mục tiêu không chỉ là website load nhanh ở lần đầu, mà còn phải mượt khi khách lướt danh sách sản phẩm, mở chi tiết sản phẩm, thêm vào giỏ và thanh toán.
6. Tích hợp thanh toán Fundiin và xử lý bảo mật
Một phần quan trọng khác là tích hợp thanh toán Fundiin API V2 cho hình thức mua trước trả sau.
Khó khăn của phần thanh toán nằm ở việc dữ liệu gửi sang cổng thanh toán phải rất chính xác. Các thông tin như tổng tiền, danh sách sản phẩm, thông tin khách hàng, địa chỉ, định dạng field và chữ ký bảo mật đều phải khớp.
Mình đã xử lý các phần chính:
- Tạo order trước khi chuyển sang thanh toán.
- Tạo payment request theo chuẩn Fundiin API V2.
- Ký request bằng HMAC-SHA256.
- Dùng biến môi trường để bảo vệ secret key.
- Tách sandbox và production endpoint.
- Tạo waiting page để giữ khách ở lại website.
- Log lỗi có kiểm soát để dễ debug.
- Không đưa secret key ra phía client.
Điểm quan trọng nhất là bảo mật. Những thông tin như FUNDIIN_SECRET_KEY, PAYLOAD_SECRET, DATABASE_URI, SOCKET_INTERNAL_TOKEN đều phải nằm ở server environment, không được public ra browser hoặc commit lên GitHub.
7. Deploy production trên VPS với Docker/Coolify
Dự án không chỉ dừng ở local development. Mình đã triển khai production trên VPS với mô hình tách service:
mfparis-web
Domain: maraisdefrance.vn
Port: 3000
mfparis-socket
Domain: socket.maraisdefrance.vn
Port: 3001
Việc tách riêng như vậy giúp hệ thống dễ quản lý hơn:
- Web app chạy Next.js và Payload CMS.
- Socket app chạy realtime chat.
- Mỗi service có biến môi trường riêng.
- Dễ restart từng phần mà không ảnh hưởng toàn bộ hệ thống.
- Phù hợp với VPS RAM giới hạn.
- Dễ kiểm tra log và debug lỗi production.
Một thử thách lớn là cấu hình Docker, domain, port, CORS và environment variables sao cho các service giao tiếp đúng với nhau trong môi trường production. Đây là phần local thường không bộc lộ hết vấn đề, chỉ khi deploy thật lên VPS mới thấy rõ.
Công nghệ đáng tự hào đã sử dụng
Next.js
Mình dùng Next.js làm nền tảng chính vì framework này phù hợp với e-commerce hiện đại: tốc độ nhanh, SEO tốt, hỗ trợ server rendering và dễ mở rộng API.
Payload CMS
Payload CMS giúp mình xây dựng hệ thống quản trị riêng thay vì phụ thuộc WordPress. Mình có thể tự thiết kế schema sản phẩm, đơn hàng, chat profile, message và các collection theo đúng nhu cầu vận hành.
Zustand
Zustand được dùng cho giỏ hàng vì nhẹ, dễ kiểm soát và phù hợp với localStorage persist. Mình có thể chủ động xử lý logic phức tạp như đổi biến thể, validate tồn kho và đồng bộ lại giỏ hàng.
Socket.io
Socket.io được dùng cho realtime chat vì hỗ trợ room, reconnect và giao tiếp realtime ổn định. Đây là lựa chọn phù hợp khi cần xây dựng hệ thống chat riêng có lưu lịch sử và đồng bộ giữa khách hàng với admin.
Docker & Coolify
Docker và Coolify giúp việc deploy rõ ràng, tách service tốt và phù hợp với VPS. Thay vì build thủ công trên server, mình có thể quản lý web app và socket app như hai service độc lập.
Fundiin API V2
Việc tích hợp Fundiin giúp website có thêm hình thức thanh toán hiện đại. Phần này cũng giúp mình hiểu sâu hơn về API payment, chữ ký HMAC, cấu trúc request production và bảo mật dữ liệu thanh toán.
Tổng kết
MF PARIS là dự án giúp mình xử lý nhiều bài toán thực tế của một hệ thống e-commerce production:
- Migration dữ liệu từ website cũ sang CMS mới.
- Thiết kế cấu trúc sản phẩm linh hoạt.
- Quản lý sản phẩm nhiều biến thể.
- Xử lý giỏ hàng, tồn kho và checkout.
- Tích hợp thanh toán.
- Xây dựng realtime chat riêng.
- Tối ưu tốc độ và PageSpeed.
- Deploy production bằng Docker/Coolify trên VPS.
Điều mình tự hào nhất ở dự án này là nó không chỉ dừng ở giao diện đẹp, mà còn giải quyết được các vấn đề thật trong vận hành bán hàng: dữ liệu lớn, tồn kho, thanh toán, chat tư vấn, tốc độ tải trang và khả năng mở rộng lâu dài.