Thiết kế kiến trúc phần mềm để mở rộng

Tác giả
Nam Pham
Từ MVP lên Enterprise mà không phải viết lại từ đầu (hy vọng là vậy)
Mình không phải kiểu “architect xịn xò”. Mình là dev từng làm sập production vài lần rồi phải fix trong áp lực. Nên bài này không phải lý thuyết — mà là những thứ mình ước gì mình làm sớm hơn khi build MVP.
Teaser (dùng cho card)
Những quyết định nhỏ về kiến trúc từ sớm có thể giúp bạn tránh việc phải rewrite cả hệ thống sau này. Đây là câu chuyện về việc chọn đủ tốt, không phải hoàn hảo.
Cái bẫy MVP (ai làm rồi cũng dính)
Khi làm MVP, mục tiêu là ship nhanh:
- Nhét logic vào controller
- 1 database xử lý tất cả
- Bỏ qua structure vì “để sau refactor”
Vấn đề là… “sau” đến rất nhanh.
Một ngày đẹp trời:
- user tăng gấp 10
- nhiều team cùng code
- khách enterprise yêu cầu SLA, security, audit log…
Và lúc đó, architecture bắt đầu “phản lại” bạn.
Quyết định #1: Tách Domain Logic sớm (dù chỉ một chút)
Trước mình nghĩ Clean Architecture là overkill cho MVP.
Giờ mình thấy: làm full thì có thể overkill, nhưng không tách gì cả thì còn tệ hơn.
Một rule đơn giản:
Đừng để business logic nằm trong controller/UI
Chỉ cần:
- có services/ hoặc use-cases/
Là đã giúp:
- dễ reuse
- dễ test
- dễ scale về sau
Quyết định #2: Ưu tiên scale phần đọc (read)
Phần lớn hệ thống scale read trước write.
Thay vì over-engineer:
- cứ bắt đầu với monolith
- nhưng tối ưu cho read
Những thứ mình hay làm gần đây:
- caching sớm (Redis hoặc đơn giản hơn)
- tách read model (kiểu CQRS nhẹ)
- pagination ở mọi nơi
Không cần CQRS đầy đủ — chỉ cần đừng assume query nào cũng rẻ.
Quyết định #3: Cẩn thận với “1 database cho tất cả”
Cái này đau thật.
MVP thì 1 DB là ok. Nhưng:
Khi mọi feature dùng chung schema mà không có boundary → rất khó maintain
Cách mình cải thiện:
- group table theo domain
- hạn chế join cross-domain
- dùng API nội bộ thay vì query trực tiếp DB
Kiểu như giả lập microservices… nhưng chưa phải chịu đau thật 😅
Quyết định #4: Nghĩ async sớm (khi hợp lý)
Sai lầm trước đây:
Cái gì cũng synchronous cho đơn giản
Hậu quả:
- API chậm
- timeout
- UX tệ
Giờ mình tách sớm:
- gửi email → async
- report → async
- xử lý nặng → async
Tools mình đang thử:
- Kafka, RabbitMQ
- job queue như BullMQ
Không cần event-driven full, nhưng async boundary rất đáng giá.
Quyết định #5: Observability không còn optional
Trước mình ignore cái này. Cho đến khi production lỗi 😅
Giờ dù MVP vẫn cố có:
- logging có structure
- tracing cơ bản (OpenTelemetry)
- metrics
Vì:
Không nhìn thấy thì không debug được.
Một vài thứ mới mình đang explore
Gần đây mình có thử:
- Serverless + edge (scale global read khá hay)
- BFF pattern cho frontend phức tạp
- GraphQL (dùng chọn lọc, không spam nữa 😅)
- AI hỗ trợ monitoring (còn mới nhưng thú vị)
Không phải cái nào cũng cần — nhưng nó đang thay đổi cách mình nghĩ về scale.
Kết
MVP không cần architecture hoàn hảo.
Nhưng cần:
- boundary rõ ràng
- awareness về scale
- và kỷ luật tránh “hack tạm”
Vì cái đắt nhất không phải build nhanh —
mà là phải build lại từ đầu.