Bài toán
Phần lớn ứng dụng quản lý chi tiêu chỉ dừng ở mức "thu trừ chi". Thực tế tài chính cá nhân ở Việt Nam phức tạp hơn nhiều: lương nhận qua ngân hàng, chi tiêu hằng ngày bằng tiền mặt và ví điện tử, thẻ tín dụng có kỳ sao kê riêng, điện thoại mua trả góp 0%, khoản vay trả hằng tháng. Chili quản lý tất cả trong một nơi, với số dư chính xác đến từng đồng.
Chili đang trong giai đoạn beta tại chili.tlait.online với giao diện tiếng Việt, hiện có 10 người dùng đang sử dụng để quản lý tài chính của chính họ. Tôi phát triển sản phẩm một mình: từ repo trống đến production trong khoảng ba tháng, sau đó phát hành 31 phiên bản trong ba tuần đầu.
Phạm vi
- Đủ loại ví, kể cả thẻ tín dụng có kỳ sao kê và trả góp.
- Giao dịch, ngân sách theo tháng, mục tiêu tiết kiệm, các khoản nợ và khoản vay kèm lịch trả.
- Dự báo dòng tiền trong tháng, báo cáo theo tháng và theo năm.
- Trợ lý AI chuyển câu mô tả hoặc ảnh chụp màn hình thành giao dịch.
Kiến trúc
Monorepo dùng pnpm + Turborepo. web và api đều import shared, nhưng không import lẫn nhau.
packages/shared Zod schemas · types · enums · error codes (không phụ thuộc gói nào)
▲ ▲
apps/web apps/api
React 19 + Vite Express 5
React Router 7 Controller → Service → Repository
TanStack Query Prisma 7 → PostgreSQL
Mỗi nhóm chức năng của API (wallets, transactions, budgets, …) gồm controller xử lý HTTP, service chứa quy tắc nghiệp vụ, và repository là lớp duy nhất làm việc với Prisma. Production gồm ba container Docker trên một VPS: Caddy, API và PostgreSQL.
Các quyết định kỹ thuật chính
Mọi thứ đều đi qua service layer
Web, trợ lý AI và các tác vụ định kỳ đều gọi chung một bộ method của service, và mọi method đều kiểm tra quyền sở hữu dữ liệu của người dùng. Vì vậy AI không có đường truy cập riêng vào database: nó chỉ là một bên gọi service như các bên khác.
AI chỉ làm phiên dịch, không có cửa sau
Tool của AI được sinh từ chính các Zod schema dùng chung, nên dữ liệu do AI gửi vào được kiểm tra như dữ liệu nhập từ form. userId luôn lấy từ session, không bao giờ do model truyền vào, và mỗi thao tác chỉ thực thi một lần kể cả khi request bị gửi lại. Ảnh chụp màn hình chỉ được chuyển thành bản nháp; dữ liệu chỉ được lưu sau khi người dùng xác nhận.
Hệ thống dùng một model mã nguồn mở do tôi tự host bằng Ollama, nên dữ liệu tài chính của người dùng không đi qua bất kỳ dịch vụ AI bên thứ ba nào. Model được lựa chọn dựa trên điểm số với bộ 76 câu nhập tiếng Việt, chẳng hạn "ăn trưa 50k tiền mặt", thay vì cảm nhận qua vài lần thử.
Một schema, kiểm tra ở cả hai đầu
Cùng một bộ Zod schema kiểm tra dữ liệu ở client và kiểm tra lại ở server, nên form, API và tool của AI không thể lệch nhau. Lỗi trả về cũng theo một định dạng thống nhất, nhờ đó giao diện chỉ ra chính xác quy tắc nào bị vi phạm.
Tiền và thời gian: không chấp nhận sai số
Số tiền được lưu bằng Decimal(19, 4) và truyền đi dưới dạng chuỗi, không bao giờ dùng float của JavaScript. Ngày tháng tính theo múi giờ Asia/Ho_Chi_Minh, nên kỳ sao kê hay tháng ngân sách không bị lệch chỉ vì server chạy giờ UTC.
Mỗi quy tắc được test một lần, ở tầng thấp nhất đủ để chứng minh
Unit test bao phủ quy tắc nghiệp vụ và component. Integration test chạy service trên PostgreSQL thật để kiểm tra rollback, race condition và lock. Playwright chỉ dùng cho những gì cần cả trình duyệt, API và database cùng chạy, như số dư, chuyển tiền giữa các ví và trọn một tháng sử dụng.
Làm một mình nhưng vận hành như một team
Khi làm một mình, không có ai khác phát hiện những gì tôi bỏ sót, vì vậy quy trình được văn bản hóa:
- Lát cắt nhỏ, làm trọn vẹn. Mỗi tính năng chỉ phát hành khi đã hoàn chỉnh, kèm ghi chú trong changelog.
- Quyết định có văn bản. Những lựa chọn quan trọng đều được ghi lại kèm lý do.
- Release được kiểm tra. CI chạy type check, lint, hơn 3.000 unit test, build và test end-to-end trên database thật. Database được backup mỗi đêm.
- AI làm việc có khuôn khổ. Tôi sử dụng Claude Code trong khuôn khổ các quy tắc kiến trúc và kiểm thử do tôi xây dựng, và trực tiếp review mọi thay đổi.
Từ Pocketly đến Chili
Chili là bản viết lại. Bản đầu tiên, Pocketly (Next.js, Server Actions, Prisma, đầu năm 2026), đã kiểm chứng được logic tài chính, nhưng quy tắc nghiệp vụ nằm lẫn trong phần xử lý request, nên ngoài giao diện không có cách nào khác để thay đổi số tiền một cách an toàn. Chili tách quy tắc nghiệp vụ thành một API riêng xoay quanh service layer, và tính đến trợ lý AI ngay từ khâu thiết kế.