Trong quá trình thiết kế hệ thống phần mềm, một trong những câu hỏi khó nhất không phải là nên sử dụng Monolith hay Microservices, mà là đâu là ranh giới phù hợp cho mỗi thành phần của hệ thống. Nếu chia quá nhỏ, các dịch vụ sẽ phụ thuộc lẫn nhau quá nhiều, làm tăng độ phức tạp trong triển khai và vận hành. Ngược lại, nếu chia quá lớn, hệ thống sẽ mất đi tính linh hoạt và khó mở rộng khi nhu cầu thay đổi.
Để giải quyết bài toán
này, các kiến trúc sư không chỉ xem xét chức năng nghiệp vụ mà còn phải đánh
giá các mối quan hệ phụ thuộc giữa mã nguồn, dữ liệu, hạ tầng và cách các thành
phần tương tác với nhau trong quá trình vận hành. Từ đó xuất hiện khái niệm Architecture
Quantum (Lượng tử Kiến trúc) – đơn vị nhỏ nhất của một hệ thống có thể triển
khai độc lập nhưng vẫn đảm bảo tính gắn kết chức năng.
1. Định nghĩa về Phụ thuộc (Coupling)
Trong kiến trúc phần mềm các
yếu tố như hiệu suất, khả năng mở rộng và dữ liệu thường bị đan xen vào nhau một
cách rối rắm. Để phân tích sự đánh đổi, kiến trúc sư trước tiên phải gỡ rối
các chiều không gian của vấn đề, xác định phần nào đang phụ thuộc vào phần
nào và tác động của sự thay đổi đó là gì.
Định nghĩa đơn giản về Phụ
thuộc (Coupling): Hai phần của hệ thống được coi là phụ thuộc
nếu một thay đổi ở phần này có thể buộc phần kia phải thay đổi theo.
2. Khái niệm Architecture Quantum (Lượng tử Kiến trúc)
Thuật ngữ "lượng tử"
trong “lượng tử kiến trúc” được mượn từ vật lý để chỉ đơn vị nhỏ nhất trong
kiến trúc có thể triển khai độc lập mà vẫn đảm bảo tính gắn kết chức năng
cao. Đây là một "đơn vị nhỏ nhất có thể tự đứng vững" trong một
hệ thống phần mềm.
Một “lượng tử kiến
trúc” bao gồm ba đặc điểm chính:
- Có thể triển khai độc lập (Independently Deployable): Nó chứa đầy đủ các thành phần cần thiết để chạy mà không cần dựa vào các phần khác của hệ thống tại thời điểm triển khai.
- Gắn kết chức năng cao (High Functional Cohesion): Các thành phần bên trong nó cùng phục vụ một mục đích nghiệp vụ rõ ràng.
- Phụ thuộc tĩnh chặt chẽ (High Static Coupling): Các thành phần bên trong lượng tử này (như code và cơ sở dữ liệu) được nối dây chặt chẽ với nhau.
3. High Functional Cohesion (Tính gắn kết chức năng cao)
Về mặt cấu trúc, tính gắn
kết chức năng cao đề cập đến sự gần gũi của các yếu tố liên quan như các
lớp (classes), các thành phần (components) hay các dịch vụ. Thay vì để các đoạn
mã thực hiện các nhiệm vụ khác nhau nằm rải rác, chúng ta gom chúng lại một chỗ
nếu chúng cùng thực hiện một quy trình nghiệp vụ.
Kỹ thuật này trùng khớp với
mục tiêu của Bounded Context trong Thiết kế hướng miền (Domain-Driven
Design - DDD): Nó bao gồm cả hành vi (mã nguồn) và dữ liệu cần
thiết để thực hiện một quy trình nghiệp vụ cụ thể.
Tính gắn kết không phải
là việc các dịch vụ tương tác với nhau như thế nào, mà là về việc mỗi dịch vụ
độc lập đến mức nào.
- Khi một dịch vụ có tính gắn kết cao, nó sẽ "làm một việc và làm thật tốt".
- Nó giúp đội ngũ phát triển có thể thay đổi mã nguồn bên trong mà không lo lắng về việc làm hỏng các phần không liên quan khác.
- Đặc biệt, để đạt được sự gắn kết này, cơ sở dữ liệu phải được bao hàm trong ranh giới đó. Nếu dữ liệu bị tách rời khỏi chức năng, tính gắn kết sẽ bị phá vỡ.
Tóm lại:
Nếu "Independently Deployable" là cái vỏ bên ngoài (giúp bạn đưa code
lên server), thì "High Functional Cohesion" chính là phần ruột bên
trong (đảm bảo code bên trong dịch vụ đó đồng nhất và tập trung). Một dịch vụ
thực sự mạnh mẽ khi nó chỉ chứa những thứ cần thiết cho một nhiệm vụ nghiệp vụ
duy nhất.
4. High Static Coupling (Phụ thuộc tĩnh cao)
Bạn có thể hiểu đây là
"sợi dây liên kết cứng" về mặt vận hành và mã nguồn giữa các thành phần
bên trong một dịch vụ.
Định nghĩa: Phụ thuộc tĩnh là gì?
Phụ thuộc tĩnh mô tả cách
các thành phần trong kiến trúc được "đi dây" (wired) lại với
nhau. Nếu phụ thuộc động là cách các dịch vụ gọi nhau khi đang chạy (runtime),
thì phụ thuộc tĩnh đại diện cho các phụ thuộc cần thiết để dịch vụ có thể khởi
động và hoạt động được. Để dễ hình dung, hãy tự hỏi: "Tôi cần những gì để
có thể khởi động dịch vụ này từ con số 0?". Câu trả lời chính là các điểm phụ
thuộc tĩnh: hệ điều hành, framework, thư viện, cơ sở dữ liệu, và các cấu hình vận
hành khác.
Tại sao lượng tử kiến trúc cần phụ thuộc tĩnh "cao"?
Một lượng tử kiến trúc phải
bao hàm tất cả các thành phần bị ràng buộc chặt chẽ với nhau để nó có thể tự đứng
vững:
- Các yếu tố bên trong một lượng tử (như các lớp, thành phần) cần được kết nối chặt chẽ qua các hợp đồng (contracts) như chữ ký phương thức hoặc các phụ thuộc vận hành.
- Việc hiểu rõ ranh giới phụ thuộc tĩnh giúp kiến trúc sư biết được "nếu tôi thay đổi X, thì Y có bị buộc phải thay đổi theo hay không".
"Điểm nghẽn" lớn nhất: Cơ sở dữ liệu (Database)
Đây là điểm quan trọng nhất
cần lưu ý: Cơ sở dữ liệu là một phần của phụ thuộc tĩnh.
- Nếu nhiều dịch vụ cùng dùng chung một cơ sở dữ liệu quan hệ duy nhất, chúng sẽ bị "phụ thuộc tĩnh" vào nhau qua cơ sở dữ liệu đó.
- Trong trường hợp này, dù bạn có bao nhiêu dịch vụ đi nữa, toàn bộ hệ thống vẫn chỉ được tính là một lượng tử kiến trúc duy nhất (quantum of one).
- Chỉ khi mỗi dịch vụ có cơ sở dữ liệu riêng (như trong kiến trúc Microservices lý tưởng), chúng mới thực sự được coi là các lượng tử độc lập vì ranh giới phụ thuộc tĩnh đã được phân tách.
Vai trò của Giao diện người dùng (UI)
Giao diện người dùng cũng
có thể trở thành một điểm phụ thuộc tĩnh cực mạnh. Nếu một hệ thống có các dịch
vụ backend riêng biệt nhưng tất cả đều bị buộc phải hoạt động thông qua một bộ
UI duy nhất, thì UI đó sẽ "dính" các dịch vụ lại thành một lượng tử
duy nhất. Ngược lại, kiến trúc Micro-frontends cho phép mỗi phần UI đi
kèm với dịch vụ backend tương ứng, giúp duy trì các lượng tử kiến trúc riêng biệt.
Ý nghĩa thực tế: Phân tích rủi ro và khả năng thay đổi
Việc xác định phụ thuộc tĩnh
giúp đội ngũ phát triển:
- Giảm thiểu rủi ro:
Hiểu rõ khi thay đổi một thư viện hay một cấu hình hệ điều hành thì những
gì sẽ bị ảnh hưởng.
- Xác định phạm vi kiểm thử:
Biết được cần phải kiểm thử lại những gì khi có một thành phần trong
"dây chuyền" bị thay đổi.
- Đo lường đặc tính kiến trúc:
Phụ thuộc tĩnh xác định phạm vi mà các đặc tính như hiệu suất, khả năng mở
rộng hay bảo mật có thể thực thi độc lập.
Tóm lại: Phụ
thuộc tĩnh cao có nghĩa là mọi thứ cần thiết để một dịch vụ "sống" được
phải nằm gọn trong ranh giới của nó. Nếu dịch vụ của bạn không thể chạy nếu thiếu
một cơ sở dữ liệu chung hay một bộ UI tổng, thì ranh giới lượng tử của bạn thực
tế lớn hơn bạn tưởng.
5. Dynamic Quantum Coupling" (Phụ thuộc lượng tử động)
Dynamic Quantum Coupling tập
trung vào cách các dịch vụ tương tác với nhau trong thời gian chạy (runtime) để
hoàn thành một quy trình nghiệp vụ.
Phân biệt: Phụ thuộc Tĩnh vs. Phụ thuộc Động
- Phụ thuộc Tĩnh (Static Coupling):
Là cách các dịch vụ được "đi dây" với nhau. Nó trả lời câu hỏi:
"Tôi cần những gì (thư viện, database, hệ điều hành...) để có thể khởi
động dịch vụ này từ con số 0?".
- Phụ thuộc Động (Dynamic Coupling):
Là cách các dịch vụ "gọi nhau" khi đang chạy. Nó trả lời câu hỏi:
"Khi quy trình nghiệp vụ bắt đầu, các dịch vụ trao đổi thông tin với
nhau như thế nào?".
Ba chiều không gian của Phụ thuộc động
Phụ thuộc động giống như
một không gian ba chiều, nơi mỗi quyết định kiến trúc là sự đánh đổi giữa ba yếu
tố:
- Giao tiếp (Communication):
- Đồng bộ (Synchronous):
Bên gọi phải đợi bên nhận phản hồi mới làm việc tiếp.
- Bất đồng bộ (Asynchronous):
Bên gọi gửi tin nhắn vào hàng đợi rồi tiếp tục làm việc khác ngay lập tức.
- Tính nhất quán (Consistency):
- Tức thì (Atomic):
Dữ liệu phải chính xác ở tất cả mọi nơi ngay lập tức (thành công tất cả
hoặc thất bại tất cả).
- Sau cùng (Eventual):
Chấp nhận dữ liệu tạm thời chưa khớp nhau, nhưng sẽ đồng bộ sau một khoảng
thời gian.
- Điều phối (Coordination):
- Tập trung (Orchestrated):
Có một "nhạc trưởng" điều phối luồng công việc.
- Tự điều phối (Choreographed):
Các dịch vụ tự phối hợp với nhau qua tin nhắn mà không cần người chỉ huy.
Khái niệm về sự "Vướng víu" lượng tử (Entanglement)
Phụ thuộc động có thể gây
ra hiện tượng "vướng víu tạm thời" giữa các dịch vụ.
- Ví dụ:
Nếu dịch vụ A gọi đồng bộ sang dịch vụ B, thì dù hai dịch vụ này được thiết
kế độc lập về mặt tĩnh, chúng vẫn bị "dính" vào nhau khi
chạy.
- Hệ quả: Nếu dịch vụ bị gọi chạy chậm hoặc sập, dịch vụ gọi cũng sẽ bị ảnh hưởng theo, làm giảm hiệu năng và khả năng chịu lỗi của toàn hệ thống.
6. Kết luận
Architecture Quantum là một
trong những khái niệm quan trọng giúp kiến trúc sư xác định ranh giới phù hợp
cho các thành phần trong hệ thống phần mềm. Thay vì chỉ tập trung vào số lượng
dịch vụ hay công nghệ được sử dụng, khái niệm này nhấn mạnh khả năng triển khai
độc lập, tính gắn kết chức năng và các mối quan hệ phụ thuộc giữa mã nguồn, dữ
liệu và hạ tầng.
Comments
Post a Comment