Lesson 17 — Mini-project: CLI Todo App

Lesson tổng kết Tier 1. 3 module dưới đây cho phép bạn (1) chạy thử app todo ngay trong trang (terminal mô phỏng — gõ add "Mua sữa", list, ... thấy output thật và state lưu vào localStorage); (2) khám phá kiến trúc 4-file (main / storage / task / commands); (3) xem file JSON storage cập nhật real-time mỗi khi thực hiện thao tác ở module 1.

1. Interactive Todo CLI — gõ lệnh thật

Đây là simulator của binary todo chạy thẳng trong browser (JS port của code Go ở solutions/). Gõ lệnh giống hệt như trong terminal, ấn Enter hoặc click nút "Run". State lưu trong localStorage nên reload trang vẫn còn task.

$ todo
Demo nhanh:
Toàn bộ logic ở đây map 1-1 với code Go thật trong solutions/: cùng cách parse args, cùng cách filter, cùng format bảng. Bạn có thể đọc solutions/commands.go rồi quay lại đây thử lệnh để xác nhận hành vi.

2. Architecture diagram — click vào file để xem code

4 file Go trong project, mũi tên = phụ thuộc ("file A dùng file B"). Click vào ô file để xem code snippet.

main.go entry + dispatch commands.go cmdAdd/List/... storage.go Load/Save JSON task.go type Task struct
Quy tắc phụ thuộc: luôn đi một chiều xuống dưới. Task không biết về Storage, Storage không biết về Commands. Nếu vẽ ra mũi tên thấy có vòng (A → B → A), đó là smell cần refactor.

3. JSON storage viewer — file .todo.json đổi real-time

Mỗi khi bạn chạy lệnh ở Module 1, file .todo.json được serialize lại. Bên dưới là raw JSON + view "đẹp" của task list. Highlight xanh = vừa thêm, đỏ = vừa xóa/đổi.

Raw .todo.json:
Pretty view:
Lưu JSON pretty-printed (json.MarshalIndent) thay vì compact để file dễ đọc/sửa tay khi debug. Trade-off: file lớn hơn ~30%. Với todo nhỏ không vấn đề; nếu storage thật có 100k entry mới cần compact.
Tại sao lưu cả nextId riêng?

Nếu dùng len(tasks)+1 để sinh ID mới, sẽ bug khi user xóa task. Ví dụ: có task #1, #2, #3. Xóa #3 → len = 2. Thêm task mới → ID = 3 (trùng với task vừa xóa, nếu user quay lại undo sẽ conflict). Lưu nextId riêng giúp ID strictly monotonic — chỉ tăng, không bao giờ tái sử dụng.

4. Tổng kết — chuyển sang Tier 2

Sau lesson này bạn đã có:

Tier 2 — Go Intermediate sẽ học interface (tách app khỏi storage cụ thể), goroutine + channel (process task song song), error wrapping, generic. Đó là phần Go "Go nhất" — khác Java/Python rõ rệt.

→ Xem Tier 2 — Intermediate.