Software Architecture: Khi “Best Practices” Trở Nên Vô Nghĩa

“Không có phát triển nào – cả công nghệ lẫn quản lý – có thể tăng năng suất đến mười lần chỉ trong một thập kỷ.”
Fred Brooks, No Silver Bullet (1986)


🌫 Khi “best practice” trở nên vô dụng

Người làm công nghệ thường chia sẻ “best practice” – những công thức đã chứng minh hiệu quả. Nhưng trong kiến trúc phần mềm, hầu hết vấn đề lại chưa từng xảy ra trước đó theo đúng cùng một cách.

Mỗi tổ chức có bối cảnh riêng: văn hóa, chính trị, hệ thống cũ, năng lực đội ngũ.
Một giải pháp từng thành công ở nơi khác có thể là rào cản chết người ở đây.

Trong kiến trúc, “đúng” hay “sai” không cố định – chỉ có “ít tệ hơn” trong từng hoàn cảnh.

Kiến trúc sư không đi tìm công thức, mà đi tìm điểm cân bằng giữa các đánh đổi.


🧩 “The Hard Parts” – vì sao lại khó đến thế

Từ hard trong Software Architecture: The Hard Parts có hai tầng nghĩa:

  • Khó – vì đây là những vấn đề không có sẵn hướng dẫn, chạm đến kỹ thuật, con người, và quyền lực.
  • Cứng – vì đây là phần cấu trúc nền, thay đổi khả năng làm đứt gãy toàn hệ thống.

Dấu hiệu nhận biết đâu là phần “hard” trong hệ thống của bạn:

  1. Thay đổi ở đó ảnh hưởng dây chuyền đến nhiều phần khác.
  2. Quyết định ở đó cần được cân nhắc kỹ và ghi chép lại.
  3. Sai lầm ở đó rất đắt để sửa.

🔄 Kiến thức kiến trúc không nên lỗi thời

Mười năm trước, SOA là chuẩn. Giờ, microservices lên ngôi. Sắp tới, có thể modular monolith quay lại.
Nhưng đừng bận tâm vào cái tên — hãy để ý vào điều kiện đã tạo ra nó:

  • Khi hạ tầng đắt và quản trị phức tạp → ta gom lại để dễ kiểm soát.
  • Khi ảo hóa, container, CI/CD phổ biến → ta chia nhỏ để linh hoạt.

Ai hiểu được vì sao xu hướng xuất hiện, sẽ biết khi nào nên dừng chạy theo nó.


🧠 Dữ liệu – trung tâm thật sự của kiến trúc

“Data is a precious thing and will last longer than the systems themselves.”
Tim Berners-Lee

Dữ liệu sống lâu hơn bất kỳ framework nào.
Nhiều hệ thống thất bại khi tách monolith không phải vì API phức tạp, mà vì dữ liệu không thể chia rời.

Một nguyên tắc quan trọng:

  • Dữ liệu vận hành (OLTP) – giao dịch, cập nhật, thứ giúp doanh nghiệp chạy được.
  • Dữ liệu phân tích (OLAP) – tổng hợp, dự đoán, thứ giúp doanh nghiệp nhìn xa.

Khi tách hệ thống, đừng để dữ liệu chết theo service.
Mỗi team nên sở hữu một phần dữ liệu rõ ràng, trong bounded context riêng – đủ độc lập, nhưng vẫn kết nối được giá trị toàn cục.


📝 Ghi lại lý do, không chỉ kết quả

Khi một kiến trúc sư rời đi, điều mất lớn nhất không phải là code – mà là vì sao các quyết định từng được chọn.

Architectural Decision Record (ADR) là cách ghi nhớ đơn giản nhưng mạnh mẽ:

# ADR: Cấu trúc lưu log tập trung

**Context**
Hệ thống cần lưu nhiều loại log (service A, B, C) cho mục đích giám sát và bảo mật.

**Decision**
Sử dụng cơ chế tập trung với Elasticsearch thay vì lưu riêng từng service.

**Consequences**
Dễ truy vấn và cảnh báo thống nhất, nhưng tăng độ phụ thuộc vào hạ tầng trung tâm.

ADR chỉ cần vài đoạn Markdown.
Ghi cùng mã nguồn để mọi thay đổi đều mang theo lý do sống cùng code.


⚙️ Khi kiến trúc tự kiểm tra chính mình

Một bản thiết kế tốt vẫn có thể bị “xói mòn” nếu không được bảo vệ.
Thay vì review thủ công, ta có thể để hệ thống tự kiểm tra cấu trúc của chính nó bằng Architecture Fitness Functions.

🔍 Ví dụ kiểm tra vòng phụ thuộc:

// JDepend: kiểm tra cycle giữa các package
assertFalse(jdepend.containsCycles());

🧱 Ví dụ kiểm tra tầng:

// ArchUnit: controller không được gọi trực tiếp persistence
layeredArchitecture()
  .layer("Controller").definedBy("..controller..")
  .layer("Service").definedBy("..service..")
  .layer("Persistence").definedBy("..persistence..")
  .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
  .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller");

💡 Trong .NET:

// NetArchTest: Presentation không phụ thuộc Data
Types.InCurrentDomain()
  .That().ResideInNamespace("Presentation")
  .ShouldNot().HaveDependencyOn("Data")
  .GetResult().IsSuccessful;

Những bài kiểm tra này chạy tự động trong pipeline, trở thành hệ miễn dịch của kiến trúc.
Nếu ai đó vi phạm nguyên tắc, build dừng lại — không cần tranh luận.


🚨 Equifax – bài học của governance thủ công

Vụ rò rỉ dữ liệu Equifax năm 2017 đến từ lỗi Apache Struts đã có bản vá.
Vấn đề không nằm ở lỗ hổng, mà ở việc họ không biết hệ thống nào đang dùng bản lỗi.

Giả sử mọi pipeline của họ có một bài kiểm tra đơn giản:

“Nếu framework X phiên bản Y, fail build.”

Thảm họa đó đã không xảy ra.
Automation không thay thế trách nhiệm, nó chỉ khiến cái quan trọng không thể bị lãng quên.


🧭 Giữ cho yếu tố “vì sao” luôn lớn hơn “làm sao”

“Why is more important than How.”
Fundamentals of Software Architecture

Kiến trúc bắt đầu bằng lý do.
Nếu không rõ “vì sao”, mọi yếu tố “làm sao” đều dễ sai.

Framework có thể lỗi thời, công cụ có thể thay đổi —
nhưng nguyên tắc, giới hạn, và những đánh đổi hợp lý sẽ còn hiệu lực rất lâu sau khi dòng code cuối cùng bị xóa.


🧩 Câu chuyện của Sysops Squad

Để làm rõ mọi khái niệm, các tác giả tạo ra ví dụ hư cấu Sysops Squad – hệ thống quản lý dịch vụ sửa chữa tại nhà.
Một monolith khổng lồ, chậm, dễ sập, khách hàng phàn nàn, thay đổi tốn kém.

Từ đây, họ lần lượt tách dữ liệu, chia module, đo trade-off, ghi ADR, rồi tự động hóa governance bằng fitness functions.
Không có bước nào hoàn hảo — chỉ có những lựa chọn ngày càng “ít tệ hơn”.


Kết

Không có “best practice”.
Có những lựa chọn được hiểu rõ, được ghi lại, và được kiểm chứng liên tục.

Một kiến trúc vững không phải vì nó đúng,
mà vì người thiết kế hiểu vì sao nó đáng để chịu sai ở đâu.

Đừng tìm giải pháp hoàn hảo – hãy tìm lựa chọn ít tác hại nhất, và hiểu nó thật sâu.

Để lại một bình luận