<Team/>
#embracing_challenges
Chợ 2Hand
Mini App v1.0
• By [loctd3] | 14.10.2026
Content
#embracing_challenges
Chợ 2Hand
#1. Tổng quan
Đặt vấn đề
Thị trường đồ cũ
1,28 tỷ USD
quy mô thị trường đồ cũ Việt Nam năm 2025
Tăng 13,7% so với năm 2024
Research and Markets, 6/2025 (ước tính).
Social commerce
71%
người Việt được khảo sát từng mua hàng qua mạng xã hội
50 triệulượt truy cập Chợ Tốt mỗi tháng
1 triệu+món hàng đăng bán mỗi tháng
PwC Voice of the Consumer 2024.
Zalo
81,3 triệu
người dùng Zalo mỗi tháng
Kho Mini App của Zalo chưa có chợ đồ cũ
Sản phẩm tương tự & giải pháp
Chợ TốtFacebook MarketplaceNhóm FacebookChợ 2Hand
Không cần đăng ký
Dễ tiếp cận
Đăng tin với video
Miễn phí
An toàn
Có    Không
Feature breakdown
Phase 1Đã xong

Core feature

  • Feed, tìm kiếm, bộ lọc
  • Reels
  • Đăng tin có ảnh và video
  • Quản lý tin
  • Báo cáo, chia sẻ tin
Phase 2

Admin & Fraud

  • Trang quản trị
  • Duyệt tin
  • Xử lý báo cáo
  • Quản lý tài khoản
  • Chống spam
Phase 3

Engagement & Discovery

  • Tin yêu thích, thông báo
  • Lịch sử xem tin và tìm kiếm
  • Gợi ý tin theo sở thích
  • Mở rộng danh mục, form và bộ lọc theo danh mục
  • Tìm kiếm nâng cao
Phase 4

Seller tools

  • Thống kê cho người bán
  • Tin ưu tiên (trả phí)
  • Duyệt tin nhanh (trả phí)
  • Hồ sơ và đánh giá người bán
Phase 5

AI

  • Gợi ý tìm kiếm bằng AI
  • Tìm kiếm bằng hình ảnh
  • AI kiểm tra tin
  • AI điền tin từ ảnh, video
  • AI trả lời người mua
#embracing_challenges
Chợ 2Hand
#2. Kiến trúc hệ thống
Architecture overview
Kiến trúc: FE Mini App có Web Worker, ZMP SDK, API, Database có Job queue, Media worker, Zalo Open API, S3, CloudFront, Sentry
Frontend architecture
Kiến trúc phân tầng của FE Mini App, mỗi tầng chỉ gọi tầng ngay dưới: Presentation (Home feed, Reels, Chi tiết tin, Đăng tin) → State (Server state TanStack Query, Client state Zustand) → Data access (HTTP clients: Feature APIs, Axios, Token Refresh; Media Services: Upload Manager, Web Worker) → Backend
Entity relationship diagram
ERD: User, Product, Media, Report, Category, Location với khóa chính và khóa ngoại
Thiết bị hỗ trợ
iPhoneAndroid
Zalo yêu cầu tối thiểuiOS 15.1Android 7.0
Thiết bịiPhone 6s, iPhone SE (thế hệ 1) trở lênMọi máy Android 7.0 trở lên
Mini App chạy trongWKWebView, nhân WebKit của iOSAndroid WebView, nhân Chromium
Nguồn: Zalo trên App Store và Google Play (6/10/2026). Apple App Review Guidelines 2.5.6: app duyệt web phải dùng WebKit. Danh sách máy chạy iOS 15 (AppleInsider).
#embracing_challenges
Chợ 2Hand
#3. Tính năng & thách thức kỹ thuật
Feed
Feed
Technical Challenge
Feed: Loading time
Báo cáo hiệu suất bản v1: Performance 82, LCP 3,3 s, TBT 320 ms, CLS 0
Trước khi tối ưu
Báo cáo hiệu suất Zalo Mini App: Performance 95, LCP 2,5 s, TBT 100 ms, CLS 0
Sau khi tối ưu
Code splittingApply WebP thumbnailPrefetch feed dataPreload first row imagePrefetch các trang khác
Feed: Seamless scrolling - Giải pháp
1Fixed card sizeGiữ bố cục ổn định, không layout shift khi ảnh tải xong
2Show ThumbHashHiện ảnh mờ trong lúc chờ ảnh thật
3Virtualize listChỉ render card trong viewport và một vùng đệm nhỏ
4Play one videoChỉ phát 1 video, giảm tốn pin và băng thông cho người dùng
Feed: Seamless scrolling - Kết quả

Cuộn Feed liên tục 60 s, đo bằng Chrome DevTools Performance (CPU throttling 4×).

Chrome DevTools Performance: cuộn feed 60 giây, Frames gần như toàn xanh, JS heap và DOM nodes lên xuống trong một khoảng cố định
Reels
Reels
Technical Challenge
Reels: Autoplay with sound
Sound-OnUX pattern của các nền tảng video ngắn lớn.
Vì sao cần sound-on?
88%người dùng TikTok coi âm thanh là thiết yếu
+15%khả năng skip quảng cáo khi tắt tiếng
78%quan tâm sản phẩm hơn khi nhạc phù hợp
Reels: Autoplay with sound - Vấn đề
Autoplay policyChromeSafari
MutedMặc định cho phépMặc định cho phép
UnmutedSau khi user đã click vào trangplay() phải gọi ngay trong user click action
Video element tạo sau lần click đầu vẫn sẽ bị chặn autoplay unmuted, nên Reels tải thêm không tự phát có âm thanh được.
Reels: Autoplay with sound - Giải pháp
01

Create one global video

Tạo 1 video element ngay khi mở app.

02

Prefetch 10 reels

Tải trước 10 reels.

03

Play in tab switch

Gọi play() ngay trong hàm chuyển tab Reels.

04

Reuse the video

Chỉ đổi src khi lướt sang video khác.

Reels: Autoplay with sound - Kết quả
AutoplayCó âm thanh ngay khi mở tab Reels
Seamless ScrollLướt liên tục, video vẫn tự phát có âm thanh
Reels: Phát video sớm
Một file MP4 gồm 3 phần chính
Chưa xử lýMetadata nằm cuối file
ftypmdatmoov
Đã xử lýMetadata nằm ngay đầu file
ftypmoovmdat
ftypLoại file
moovMetadata: thời lượng, codec, vị trí khung hình
mdatDữ liệu hình và tiếng
Đăng tin
Đăng tin
Technical Challenge
Đăng tin: Tối ưu ảnh - Vấn đề
Giới hạn đầu vào
Số ảnhtối đa 10 / tin
Định dạngJPEG, PNG, WebP
Dung lượng≤ 10 MB / ảnh
Cạnh ngắn≥ 600 px

Ảnh gốc nặng

10 ảnh × vài MB = vài chục MB phải upload.

Đăng tin: Tối ưu ảnh - Giải pháp
Luồng xử lý ảnh trên sơ đồ kiến trúc: Web Worker xoay đúng chiều theo EXIF, resize 1600 px, JPEG 0.85; xin URL, upload S3, báo xong; media worker xóa metadata, nén mozjpeg nếu ảnh gốc, tạo thumbnail WebP 600 px; tải qua CloudFront
Đăng tin: Tối ưu ảnh - Ý nghĩa các con số
1 CSS px=
Device Pixel Ratio (DPR)iPhone 13: DPR = 3
Hiển thịCSS px× DPR= Pixel cầnChọn
Ảnh chi tiết390× 31.1701600 px
Thumbnail feed172× 3516600 px
JPEG 0.85Mức Google khuyên, Lighthouse dùng để chấm ảnh
Ảnh gốc ≤ 10 MBẢnh 12 MP: 1,5–6,2 MB
Đăng tin: Tối ưu ảnh - So sánh định dạng
Bộ data test: 8 ảnh gốc 1,5–4,5 MB.
Định dạngDùng ở đâuDung lượng / ảnhThời gian nén / ảnhƯu điểmHạn chế
JPEGlibjpeg-turboClient180 KiB10 msTương thích rộng
Nén nhanh nhất
Nặng nhất
Không có nền trong suốt
JPEGmozjpegServer · ảnh chi tiết152 KiB98 msTương thích rộng
Nhẹ hơn libjpeg-turbo
Ảnh lớn nhẹ ngang WebP
Nén chậm hơn libjpeg-turbo
Không có nền trong suốt
WebPServer · thumbnail feed149 KiB126 msẢnh nhỏ nhẹ hơn JPEG
Có nền trong suốt
iPhone không nén được
AVIFKhông dùng88 KiB2.340 msNhẹ nhất ở cùng chất lượngiOS 15 không xem được
Nén chậm nhất
JPEG XLKhông dùng113 KiB865 msHiện dần khi đang tải
Đổi từ JPEG không mất gì
iOS 15–16 chưa xem được
Điện thoại không nén được
Đăng tin: Tối ưu ảnh - Kết quả
6–12×

Ảnh upload nhẹ hơn ảnh gốc, chất lượng gần như ảnh gốc.

Đăng tin: Tối ưu ảnh - Kết quả
Ảnh điện thoại có nhãn chữ và mã QR, so sánh nén ở 1×, 2×, 3×: gửi gốc, client nén và server giữ nguyên, client nén rồi server nén
Đăng tin: Tối ưu video - Vấn đề
Giới hạn đầu vào
Số videotối đa 1 / tin
Định dạngMP4, MOV · H.264
Thời lượng≤ 60 giây
Dung lượng≤ 150 MB

Video nặng

Upload, xử lý và tải về đều lâu.

Đăng tin: Tối ưu video - Giải pháp
Luồng xử lý video trên sơ đồ kiến trúc: video trên 1080p được nén xuống H.264 1080p 30 fps, còn lại giữ nguyên; xin URL, upload S3, báo xong; media worker chuyển về 720p nếu trên 720p hoặc 4 Mbit/s, dời moov lên đầu; tải qua CloudFront
Đăng tin: Tối ưu video - Vì sao chọn H.264
CodecTrình duyệt hỗ trợNhẹ hơn H.264Điểm mạnhHạn chế
H.26497,5%Mốc so sánhHỗ trợ trên tất cả máy
Nén nhanh trên cả app và server
File nặng nhất trong 4 codec
H.265HEVC95%75% có điều kiện~50%File nhẹ bằng khoảng một nửa
iPhone quay mặc định bằng H.265
Android: máy phải có chip giải mã H.265 và Chrome 107 (2022) trở lên
VP997,3%~25%Phát được trên Chrome và AndroidiPhone từ iOS 17.4 mới phát được
AV196%17% có điều kiện~50%File nhẹ nhất
Phát được trên Chrome và Android
iPhone chỉ phát được từ 15 Pro trở lên
Đăng tin: Tối ưu video - Kết quả
~6×

Video người bán trên production nhẹ hơn video gốc.

Đăng tin: Tối ưu video - Kết quả
Khung hình giây 10 của clip Android 31,5 s, phóng 1×, 2×, 3× quanh chữ Zalo: gốc 1080p HEVC 40,4 MB; client chuyển H.264 1080p, upload 32,4 MB, VMAF 100; server chuyển 720p, download 9,2 MB, VMAF 99,2
#embracing_challenges
Chợ 2Hand
#4. Caching, Error handling & Testing
Caching
Tầng cacheDữ liệuTTLInvalidate
App cacheIn memoryFeed, Detail, Reels60 giâyUpdate tin; kéo để làm mới
Quản lý tin60 giâyUpdate tin
Category, LocationInfinityMở lại app
PersistentDiskCategory, Location1 ngày-
Phiên đăng nhậpTheo tokenLỗi 401
HTTP + CDNWebView, CloudFrontẢnh, video1 năm, immutable-
API khácETag / 304-
Error Handling — Application Recovery
Authentication401 → Refresh Zalo token một lần → Retry requestSingle-flight refresh cho các request đồng thời.
Network / APIRetry một lần → Error UICó nút Thử lại; tự refetch khi kết nối phục hồi.
Duplicate SubmitChống tạo tin trùng khi retryGiữ nguyên Idempotency-Key khi gửi lại; server trả về kết quả đã tạo.
RenderingError Boundary cô lập lỗi theo trangGiữ navigation hoạt động; ghi nhận lỗi bằng Sentry.
Trang chủ mất mạng, nút Thử lại
Home mất mạng
Màn hình Đã có lỗi xảy ra
Error Boundary
Error Handling — Media Pipeline
Image ProcessingLỗi xử lý → Fallback ảnh gốcWorker crash/timeout → khởi tạo lại, retry 1 lần; không hỗ trợ → bỏ qua tối ưu.
Image UploadTối đa 3 lần thử với exponential backoff + jitterHết lượt → người dùng chủ động Thử lại.
Video UploadMultipart upload → chỉ gửi lại các part chưa hoàn thànhGiữ các part đã hoàn thành, tránh upload lại toàn bộ video.
Tile ảnh Đang thử lại
Đang thử lại
Upload lỗi, nút Thử lại
Hết lượt: nút Thử lại
Đăng tin lỗi, form giữ nguyên
Đăng lỗi, giữ nội dung
Testing
Unit & componentVitest + Testing Library
Kết quả chạy Vitest: 73 file, 484 test pass
E2EPlaywright
Kết quả chạy Playwright: 31 pass, 1 skip

Kèm manual testing trên máy Android và iPhone thật.

#embracing_challenges
Chợ 2Hand
#5. Retrospective
Retrospective: What went well
Sản phẩmPhase 1Core feature
Lighthouse95/100Điểm performance
Smooth scroll5.000+ tinScroll vẫn mượt
ReelsAutoplayWith sound on
Đăng tin · ảnh6–12×Nhẹ hơn ảnh gốc
Đăng tin · video5,7×Nhẹ hơn video gốc
Retrospective: What could be improved

Task Prioritization

Cần xác định ưu tiên sớm hơn, cân bằng thời gian giữa UX và technical challenges.

Continuous Documentation

Nên ghi lại quyết định kỹ thuật và xây dựng slide theo từng milestone, thay vì dồn vào cuối dự án.

Retrospective: Key Learnings & Next Actions

Key Learnings

End-to-end ownershipThiết kế, triển khai và vận hành Mini App từ FE đến BE và AWS.
Data-driven engineeringMỗi quyết định kỹ thuật đều dựa trên số đo và nguồn nghiên cứu.

Next Actions

Improve reliabilityLoad test, mở rộng kiểm thử và tự động hóa CI/CD.
Improve media deliveryHLS cho video và tối ưu ảnh phía server.
#embracing_challenges
Chợ 2Hand
Q&A
Cảm ơn mọi người đã lắng nghe
References

Thị trường và sản phẩm

  1. Research and Markets, Vietnam Recommerce Market Report 2025 (GlobeNewswire, 25/6/2025)www.globenewswire.com/news-release/2025/06/25/…
  2. PwC, Voice of the Consumer Survey 2024 (Việt Nam)www.pwc.com/vn/en/publications/vietnam-publica…
  3. VietnamPlus, Zalo có 81,3 triệu người dùng hằng tháng (30/7/2026)www.vietnamplus.vn/zalo-co-813-trieu-nguoi-dun…
  4. Chợ Tốt trên Google Playplay.google.com/store/apps/details?id=com.chot…
  5. Trợ giúp Chợ Tốt: đăng tin kèm videotrogiup.chotot.com/nguoi-ban/huong-dan-dang-ti…

Thiết bị hỗ trợ

  1. Apple, iPhone 13 Tech Specs (2532 × 1170 pixel)support.apple.com/kb/SP851
  2. Zalo trên App Store (yêu cầu iOS 15.1)apps.apple.com/vn/app/zalo/id579523206
  3. Zalo trên Google Playplay.google.com/store/apps/details?id=com.zing…
  4. Apple, App Review Guidelines 2.5.6developer.apple.com/app-store/review/guidelines/
  5. AppleInsider, What you need to run iOS 15 and iPadOS 15 (2021)appleinsider.com/articles/21/06/07/what-you-ne…

Reels

  1. TikTok × Kantar, How brands are making noise with sound on TikTok (2021)ads.tiktok.com/business/en-US/blog/kantar-repo…
  2. TikTok Creative Center, Power Creative Elements (2022)ads.tiktok.com/business/creativecenter/quickto…
  3. Meta for Business, Reels adswww.facebook.com/business/ads/facebook-instagr…
  4. Yu, Hong, Egger (2026), The Sound of Reels, JHTRjournals.sagepub.com/doi/10.1177/1096348026142…
  5. Markarova, Kim (2024), TikTok Sound Branding, JDMCTdoi.org/10.29056/jdmct.2024.06.04
  6. Chrome, Autoplay policydeveloper.chrome.com/blog/autoplay
  7. WebKit, New video policies for iOSwebkit.org/blog/6784/new-video-policies-for-ios/

Ảnh

  1. Google, Optimize Images (PageSpeed Insights)developers.google.com/speed/docs/insights/OptimizeImages
  2. Chrome, Lighthouse: Efficiently encode imagesdeveloper.chrome.com/docs/lighthouse/performan…
  3. Google, WebP Compression Studydevelopers.google.com/speed/webp/docs/webp_study
  4. Siipola, Is WebP really better than JPEG? (2020)siipo.la/blog/is-webp-really-better-than-jpeg
  5. Samsung Community: JPEG Galaxy S25 Ultra (người dùng tự báo)eu.community.samsung.com/t5/galaxy-s25-series/…
  6. Ảnh thử nghiệm: Wikimedia Commonscommons.wikimedia.org/wiki/File:Smartphone_(54…

Video

  1. TikTok, Top Tips for Making TikTok Videos (2021)ads.tiktok.com/business/library/Top_Tips_One_…
  2. Reichstein, Dost (2025): độ dài video và tương tácarchives.marketing-trends-congress.com/2025/…
  3. Fanpage Karma (2026): độ dài video TikTokwww.fanpagekarma.com/insights/optimal-tiktok-…
  4. Ohm et al. (2012), IEEE TCSVT: HEVC so với H.264doi.org/10.1109/TCSVT.2012.2221192
  5. Facebook Engineering (2018): AV1, x264, VP9engineering.fb.com/2018/04/10/video-engineeri…
  6. Can I use: H.264, HEVC, VP9, AV1caniuse.com/mpeg4 · /hevc · /webm · /av1
  7. Apple Newsroom (2023): A17 Pro giải mã AV1www.apple.com/newsroom/2023/09/apple-unveils-…

Performance

  1. Chrome, Lighthouse 10.0developer.chrome.com/blog/lighthouse-10-0
  2. web.dev, Largest Contentful Paintweb.dev/articles/lcp
  3. Chrome, Total Blocking Timedeveloper.chrome.com/docs/lighthouse/performan…
Số Web Worker trên iPhone
Bảng đo trên iPhone trong Zalo: thời gian tối ưu và upload xong của 1×1, 1×2, 2×1, 2×2
Worker × ảnhTối ưu xongUpload xong
1 × 1 (hiện tại)1,4 s5,6 s
1 × 21,4 s4,4 s
2 × 11,1 s6,2 s
2 × 21,4 s7,2 s
  • Tối ưu ảnh chỉ chiếm 1,4 s, upload mới là phần lâu
  • Thêm worker nhanh hơn tối đa 0,3 s
  • Không lần nào bị tải lại trang → giữ 1 × 1
iPhone trong Zalo, bản Development ngày 8/10/2026, upload thật lên server production. 10 ảnh camera, 57 MB → 9,5 MB, mỗi cấu hình 1 lần.