Khám phá góc nhìn thực dụng của Martin Fowler về kiến trúc: mẫu, tái cấu trúc và kiến trúc tiến hóa giúp vượt qua các stack thịnh hành và giảm rủi ro dài hạn.

Một framework mới, một dịch vụ đám mây bóng bẩy, hoặc “stack chuẩn” ở một công ty hot có thể khiến bạn tưởng là đường tắt đến chất lượng. Nhưng cách nghĩ chọn stack trước thường nhầm lẫn công cụ với cấu trúc. Bạn có thể xây một hệ thống lộn xộn, khó thay đổi với công nghệ hiện đại nhất — hoặc một hệ thống sạch, dễ thích nghi với những lựa chọn cũ, quen thuộc.
Quyết định chọn stack trước đẩy các đội vào những lựa chọn trông ấn tượng trên slide nhưng không trả lời các câu hỏi thực sự:
Khi lựa chọn công nghệ dẫn dắt, kiến trúc trở thành hệ quả ngẫu nhiên — dẫn tới coupling chặt, logic bị nhân bản, và các phụ thuộc khiến thay đổi đơn giản trở nên tốn kém.
Chính vì vậy “chúng tôi dùng microservices” (hoặc “chúng tôi serverless rồi”) không phải là kiến trúc. Đó là hướng triển khai và công cụ. Kiến trúc là cách các phần của hệ thống hợp tác, cách các quyết định ràng buộc công việc tương lai, và mức độ dễ dàng để sản phẩm tiến hóa.
Một hệ quả thực tế: công cụ có thể tăng tốc giao hàng, nhưng không thay thế tư duy kiến trúc. Ngay cả với các phương pháp “vibe-coding” hiện đại — nơi bạn sinh và lặp nhanh qua chat — những câu hỏi này vẫn còn nguyên giá trị. Các nền tảng như Koder.ai có thể đẩy nhanh việc xây dựng web, backend và ứng dụng di động, nhưng những đội đạt hiệu quả nhất vẫn coi ranh giới, quyền sở hữu và khả năng thay đổi là mối quan tâm hàng đầu (không phải thứ framework sẽ tự động giải quyết).
Viết của Martin Fowler luôn kéo sự chú ý trở lại điều quan trọng: thiết kế rõ ràng hơn các thành phần thời thượng, đánh đổi thực tế hơn tư tưởng hệ thống, và khả năng tiến hóa hệ thống khi bạn học được. Công trình của ông xem kiến trúc là thứ bạn cải thiện liên tục — không phải mốc “thiết kế lớn” một lần.
Kỳ vọng ba chủ đề lặp lại: dùng mẫu như công cụ tùy ý (không phải luật), tái cấu trúc như thói quen thường xuyên, và kiến trúc tiến hóa — xây để thay đổi chứ không phải cho sự chắc chắn.
Nếu bạn là người lãnh đạo kỹ thuật, tech lead, hoặc nhóm sản phẩm muốn phát hành nhanh hơn mà không làm sụp chất lượng, bài này dành cho bạn. Mục tiêu không phải chọn stack “hoàn hảo” — mà là đưa ra quyết định giúp phần mềm dễ thay đổi khi lộ trình tất yếu thay đổi.
Kiến trúc phần mềm là tập hợp các quyết định định hình hệ thống theo những cách mà về sau khó (và tốn) để thay đổi.
Định nghĩa này cố tình đơn giản. Nó không đòi hỏi sơ đồ đặc biệt hay danh hiệu “architect”. Nó là về những lựa chọn quyết định phần mềm có thể phát triển thế nào, đội ngũ làm việc ra sao, và chi phí vận hành là bao nhiêu.
Framework, công cụ và phong cách code quan trọng — nhưng hầu hết chúng dễ thay thế so với các quyết định kiến trúc thực sự.
Kiến trúc sát hơn với cấu trúc và ranh giới: cách các phần giao tiếp, dữ liệu nằm ở đâu, cách xử lý lỗi, và thay đổi nào đòi hỏi phối hợp giữa các đội.
Không có kiến trúc “tốt nhất” phổ quát. Mỗi quyết định lớn tối ưu cho vài mục tiêu và gây thiệt thòi cho các mục tiêu khác:
Kiến trúc tốt làm cho các đánh đổi này rõ ràng thay vì vô tình.
Quyết định kiến trúc: “Chúng ta tách billing thành service có thể deploy riêng với database riêng, phần còn lại tích hợp qua sự kiện bất đồng bộ.”
Điều này ảnh hưởng tới triển khai, quyền sở hữu dữ liệu, chế độ lỗi, giám sát và phối hợp đội.
Chọn thư viện: “Chúng ta sẽ dùng Thư viện X để sinh PDF.”
Hữu ích, nhưng thường có thể thay bằng tác động vùng ảnh hưởng hạn chế.
Nếu hoàn tác một quyết định cần vài tuần phối hợp, rất có thể đó là kiến trúc.
Mẫu thiết kế tốt nhất được hiểu là giải pháp tái sử dụng cho vấn đề lặp lại, không phải là điều răn. Quan điểm chung của Fowler là thực dụng: mẫu hữu dụng khi làm rõ thiết kế, có hại khi thay thế tư duy.
Dùng đúng, mẫu cho đội ngôn ngữ chung. Nói “strategy” hay “repository” có thể nén một giải thích dài thành một thuật ngữ, giúp review nhanh hơn và giảm hiểu lầm.
Mẫu cũng làm hành vi hệ thống dự đoán hơn. Một mẫu quen thuộc thiết lập kỳ vọng nơi logic nằm, cách các đối tượng hợp tác, và thay đổi nào có khả năng lan tỏa. Sự dự đoán này giảm bất ngờ trong production và giảm câu hỏi “cái này hoạt động thế nào?” cho thành viên mới.
Hỏng hóc là cargo-culting: áp dụng mẫu vì nó phổ biến, vì sách đề cập, hoặc vì “đó là cách ta làm ở đây.” Điều này thường dẫn tới over-engineering — lớp thừa, sự gián tiếp, và các abstraction không đem lại lợi ích tương xứng.
Một bẫy khác là “mẫu cho mọi thứ.” Khi mọi vấn đề nhỏ đều có tên gọi, codebase có thể biến thành bảo tàng của sự tinh tế thay vì công cụ để giao hàng và bảo trì phần mềm.
Bắt đầu từ vấn đề, không phải từ mẫu.
Hỏi:
Rồi chọn mẫu đơn giản nhất phù hợp và giữ mở các tùy chọn. Nếu cần thêm cấu trúc sau này, bạn có thể bổ sung từng bước — thường được dẫn dắt bởi nỗi đau thực tế và xác nhận bằng tái cấu trúc, thay vì phỏng đoán trước.
Tái cấu trúc là thực hành cải thiện thiết kế nội bộ của phần mềm mà không thay đổi hành vi bên ngoài. Người dùng không nên nhận thấy gì khác sau refactor — ngoại trừ việc các thay đổi trong tương lai dễ dàng, an toàn và nhanh hơn.
Quan điểm của Martin Fowler không phải “giữ mã đẹp.” Mà là kiến trúc không phải sơ đồ một lần ở đầu. Kiến trúc là các quyết định quyết định mức độ dễ thay đổi của hệ thống. Tái cấu trúc là cách bạn ngăn các quyết định đó đóng băng thành ràng buộc.
Theo thời gian, ngay cả hệ thống thiết kế tốt cũng bị trôi dạt. Tính năng mới được thêm dưới áp lực thời gian, sửa nhanh trở thành vĩnh viễn, và ranh giới mờ đi. Tái cấu trúc là cách bạn phục hồi sự tách bạch rõ ràng và giảm độ phức tạp tình cờ, để hệ thống luôn có thể thay đổi.
Một kiến trúc khỏe mạnh là nơi:
Tái cấu trúc là công việc hàng ngày giữ gìn những chất lượng đó.
Bạn thường không lên lịch refactor bằng lịch. Bạn làm khi mã bắt đầu phản kháng:
Khi những điều đó xuất hiện, kiến trúc đã bị ảnh hưởng — refactor là sửa chữa.
Tái cấu trúc an toàn dựa trên vài thói quen:
Làm theo cách này, tái cấu trúc trở thành bảo trì thường xuyên — giữ hệ thống sẵn sàng cho thay đổi tiếp theo thay vì mong manh sau lần thay đổi trước.
Nợ kỹ thuật là chi phí tương lai do các đường tắt hôm nay tạo ra. Nó không phải “mã tồi” như một lỗi đạo đức; đó là đánh đổi bạn chấp nhận (đôi khi có chủ ý) khiến giá thay đổi tăng lên sau này. Cách nhìn của Martin Fowler hữu ích ở đây: nợ chỉ trở thành vấn đề khi bạn ngừng theo dõi nó và giả vờ như nó không tồn tại.
Nợ có chủ ý là khi bạn chấp nhận rõ ràng: “Chúng ta sẽ giao phiên bản đơn giản bây giờ, rồi củng cố sprint sau.” Điều đó hợp lý — nếu bạn cũng có kế hoạch trả.
Nợ vô ý xảy ra khi đội không nhận ra họ đang vay: phụ thuộc lộn xộn len lỏi, mô hình miền mơ hồ lan rộng, hoặc sửa nhanh thành mặc định. Nợ vô ý thường đắt hơn vì không ai sở hữu nó.
Nợ tích tụ qua áp lực bình thường:
Kết quả dễ đoán: tính năng chậm lại, bug tăng, và tái cấu trúc trở nên rủi ro thay vì thường xuyên.
Bạn không cần chương trình lớn để bắt đầu trả nợ:
Nếu bạn cũng làm cho các quyết định liên quan nợ minh bạch (xem /blog/architecture-decision-records), bạn biến chi phí ẩn thành công việc có thể quản lý.
Kiến trúc là tập hợp các quyết định mà việc đảo ngược sau này sẽ tốn kém: ranh giới, quyền sở hữu dữ liệu, kiểu tích hợp và cách xử lý lỗi.
Một tech stack chủ yếu là các công cụ bạn dùng để thực hiện những quyết định đó (framework, thư viện, dịch vụ đám mây). Bạn có thể thay nhiều công cụ với ít tác động, nhưng thay ranh giới hoặc luồng dữ liệu thường đòi hỏi vài tuần phối hợp nhiều đội.
Một kiểm thử tốt là tính khả năng đảo ngược: nếu hoàn tác một quyết định mất vài tuần và cần nhiều đội phối hợp, đó là quyết định kiến trúc.
Ví dụ:
Dùng mẫu thiết kế để giải quyết một vấn đề lặp lại cụ thể, không phải để làm cho thiết kế trông “chuyên nghiệp”.
Checklist nhanh:
Nếu bạn không thể nêu rõ vấn đề, chưa vội áp dụng mẫu.
Xem tái cấu trúc như bảo trì thường xuyên gắn với mâu thuẫn thực tế, không phải dự án dọn dẹp hiếm hoi.
Dấu hiệu thường gặp:
Giữ an toàn với tests, bước nhỏ và scope review hẹp.
Theo dõi nợ kỹ thuật như một chi phí, không phải một bí mật xấu hổ.
Cách thực tế quản lý:
Làm rõ các quyết định về nợ (ví dụ, với ADR nhẹ).
Nó nghĩa là thiết kế sao cho bạn có thể đổi hướng an toàn khi học, thay vì đặt cược vào dự đoán dài hạn.
Thành phần điển hình:
Mục tiêu là khả năng thích nghi, không phải bản thiết kế hoàn hảo từ đầu.
Fitness function là một hàng rào tự động bảo vệ một mục tiêu kiến trúc.
Ví dụ hữu ích:
Chọn vài cái phản ánh cam kết của bạn (tốc độ thay đổi, độ tin cậy, bảo mật) và chạy liên tục.
Ưu tiên modular monolith trừ khi bạn có áp lực đã được đo lường và kéo dài đòi hỏi khả năng deploy độc lập.
Microservices có lợi khi:
Nếu bạn chưa thể chạy một service thoải mái trong production, tách thành mười thường nhân lên nhiều khó khăn.
Bắt đầu bằng cách làm cho phụ thuộc trở nên hiển nhiên và có chủ ý.
Các bước hiệu quả:
Database chung tạo “phụ thuộc bí mật”, buộc phải phát hành phối hợp dù hệ thống có vẻ tách rời.
Dùng ADR để ghi lại điều chúng ta quyết định và tại sao, khi ngữ cảnh còn rõ ràng.
Một ADR nhẹ gồm:
Đặt gần mã (ví dụ, /docs/adr/) và liên kết hướng dẫn liên quan như /blog/architecture-decision-records.