Sau khi hiểu khái niệm Architecture Quantum và các yếu tố như High Functional Cohesion, Static Coupling hay Dynamic Coupling, câu hỏi tiếp theo là: những khái niệm này được áp dụng như thế nào trong các kiến trúc phần mềm phổ biến?
Architecture Quantum là gì? Giải thích Lượng tử Kiến trúc trong Software Architecture
Trên thực tế, không phải
cứ chia hệ thống thành nhiều dịch vụ thì sẽ có nhiều Architecture Quantum. Ranh
giới của một lượng tử kiến trúc còn phụ thuộc vào nhiều yếu tố như cơ sở dữ liệu,
giao diện người dùng, bộ điều phối hay cách các thành phần được triển khai và vận
hành cùng nhau. Hai hệ thống có cấu trúc hoàn toàn khác nhau cũng có thể chỉ tạo
thành một lượng tử kiến trúc duy nhất nếu vẫn tồn tại các điểm phụ thuộc tĩnh
quan trọng.
1. Kiến trúc nguyên khối (Monolith)
Một hệ thống nguyên khối có thể được coi là một "lượng tử kiến trúc" vì nó triển khai độc lập được (vì chỉ có một khối để đẩy lên), nhưng nó không có tính gắn kết chức năng cao. Đơn giản vì nó chứa đựng chức năng của toàn bộ hệ thống, mọi thứ bị trộn lẫn vào nhau. Nguyên khối càng to thì tính gắn kết càng thấp.
Có 3 kiểu kiến trúc nguyên
khối phổ biến:
- Layered Monolith (Nguyên khối phân lớp):
Code được chia thành các tầng (như tầng giao diện, tầng nghiệp vụ, tầng dữ
liệu). Dù chia lớp rõ ràng, nhưng khi chạy, tất cả phải nằm chung trong một
"hộp" duy nhất.
- Modular Monolith (Nguyên khối
mô-đun): Code được chia thành các khối
(component) có vẻ độc lập hơn về mặt logic. Tuy nhiên, tất cả các khối này
vẫn được đóng gói và triển khai cùng nhau.
- Microkernel:
Có một lõi trung tâm và các plug-in đính kèm. Dù các plug-in có vẻ tách rời,
nhưng chúng vẫn phụ thuộc hoàn toàn vào lõi để hoạt động và thường triển
khai cùng lúc với lõi.
Tại sao tất cả đều là
"một lượng tử kiến trúc"? Lý do quan trọng nhất là: Dù bạn có tổ
chức code bên trong khéo léo đến đâu, nếu toàn bộ hệ thống vẫn dùng chung một
Cơ sở dữ liệu (Database) duy nhất, thì đó vẫn chỉ là một lượng tử.
- Sự ràng buộc của Database:
Trong cả 3 sơ đồ, bạn sẽ thấy một biểu tượng Database màu xanh ở dưới
cùng, nối với toàn bộ phần app bên trên bằng một mũi tên hai đầu. Điều này
có nghĩa là ứng dụng và database bị phụ thuộc tĩnh (static coupling)
cực kỳ chặt chẽ.
- Nếu bạn thay đổi cấu trúc bảng trong database, bạn buộc phải cập nhật và triển khai lại toàn bộ ứng dụng để không gây lỗi. Bạn không thể tách rời chúng ra để nâng cấp riêng lẻ.
- Vì tất cả các thành phần (từ UI đến Logic và DB) đều phải "đi cùng nhau" trong mỗi lần triển khai, chúng tạo thành một khối thống nhất không thể chia cắt về mặt vận hành.
Tóm lại:
trong thế giới Nguyên khối (Monolith), ranh giới triển khai là "tất cả hoặc
không có gì". Cơ sở dữ liệu dùng chung chính là "sợi dây" thắt
chặt mọi thứ lại, khiến hệ thống dù lớn đến đâu cũng chỉ được tính là một lượng tử duy nhất.
2. Kiến trúc dựa trên dịch vụ (Service-based architecture)
Kiến trúc này gồm ba lớp
chính:
- Giao diện người dùng (User
Interface): Được triển khai riêng biệt.
- Các dịch vụ (Services):
Các dịch vụ này có tính "thô mịn" (coarse-grained), được triển
khai từ xa và riêng biệt với nhau. Nhìn bề ngoài, chúng trông rất giống
các Microservices vì mỗi dịch vụ nằm trong một "hộp" riêng.
- Cơ sở dữ liệu nguyên khối (Monolithic
Database): Đây là điểm mấu chốt. Tất cả các dịch
vụ ở trên đều kết nối chung vào một cơ sở dữ liệu quan hệ duy nhất.
Dù các dịch vụ có vẻ tách
rời, nhưng hệ thống này vẫn chỉ có "một lượng tử kiến trúc".
Lý do là vì:
- Sự ràng buộc của Cơ sở dữ liệu:
Trong hình, bạn sẽ thấy các mũi tên từ mọi dịch vụ đều trỏ xuống một
Database duy nhất. Cơ sở dữ liệu này đóng vai trò là một điểm phụ thuộc
tĩnh (static coupling point) cực lớn.
- Vi phạm tính triển khai độc lập:
Một lượng tử kiến trúc đúng nghĩa phải có khả năng triển khai độc lập.
Trong mô hình này, nếu bạn thay đổi cấu trúc bảng (schema) trong database
dùng chung, bạn thường phải cập nhật, kiểm thử và triển khai lại tất cả
các dịch vụ liên quan cùng một lúc (triển khai kiểu lockstep).
- Thiếu sự bao hàm dữ liệu:
Để đạt được nhiều lượng tử, mỗi dịch vụ cần phải sở hữu dữ liệu của riêng
mình. Khi dữ liệu vẫn bị gom vào một cục Monolith, ranh giới độc lập của
các dịch vụ bị phá vỡ.
Ý nghĩa đối với Kiến trúc
sư: Đừng nhầm lẫn giữa việc chia nhỏ code và việc chia nhỏ kiến trúc.
- Bạn có thể chia hệ thống thành 10 dịch vụ chạy riêng biệt, nhưng nếu 10 dịch vụ đó vẫn "dính" vào một database chung, bạn vẫn đang vận hành một khối Monolith về mặt dữ liệu và triển khai.
- Kiến trúc này thường là bước đệm khi chuyển đổi từ Monolith lên Microservices vì nó cho phép chia nhỏ logic nghiệp vụ mà chưa cần đụng vào phần dữ liệu phức tạp.
Tóm lại: Cơ
sở dữ liệu chính là "trọng lực" giữ mọi thứ lại với nhau. Chừng
nào database còn là một khối dùng chung, thì dù bạn có bao nhiêu dịch vụ đi nữa,
toàn bộ hệ thống vẫn chỉ được coi là một lượng tử kiến trúc duy nhất.
3. Kiến trúc hướng sự kiện có một bộ điều phối
Trong sơ đồ này, chúng ta
thấy ba dịch vụ (Service) riêng biệt, nhưng chúng không giao tiếp trực
tiếp với nhau. Thay vào đó, mọi yêu cầu (Request) đều đi qua một thành
phần trung tâm gọi là Request Orchestrator (Bộ điều phối yêu cầu). Tất cả
các dịch vụ này sau đó đều kết nối xuống một Cơ sở dữ liệu (Database)
dùng chung.
Mặc dù đây là một kiến
trúc phân tán (distributed), nhưng nó vẫn chỉ hình thành một lượng tử kiến
trúc duy nhất vì có hai "điểm phụ thuộc" (coupling points) mang
tính sống còn được đánh dấu bằng mũi tên đỏ:
- Bộ điều phối yêu cầu (Request
Orchestrator): Đây là "bộ não" điều khiển
toàn bộ quy trình. Bất kỳ điểm phụ thuộc mang tính tổng thể (holistic) nào
cần thiết để kiến trúc có thể vận hành được đều sẽ tạo ra một ranh giới lượng
tử bao quanh nó. Nếu bộ điều phối này thay đổi hoặc gặp sự cố, toàn bộ quy
trình nghiệp vụ sẽ bị ảnh hưởng, khiến các dịch vụ không còn tính độc lập
thực sự.
- Cơ sở dữ liệu (Database):
Giống như trong các kiến trúc nguyên khối, việc dùng chung một database
duy nhất buộc các dịch vụ phải "khớp bước" với nhau về mặt dữ liệu.
Nếu bạn thay đổi cấu trúc bảng để phục vụ một dịch vụ, bạn có thể làm hỏng
các dịch vụ khác hoặc chính bộ điều phối.
Bài học quan trọng ở đây:
Sự phân tán về mặt vật lý không đồng nghĩa với sự độc lập về mặt kiến trúc.
- Nếu hệ thống của bạn có các thành phần trung tâm (như orchestrator) mà mọi thứ đều phải phụ thuộc vào đó để hoạt động, bạn đã tạo ra một điểm thắt nút.
- Chính những điểm phụ thuộc tĩnh này (Database và Orchestrator) đã "kéo" tất cả các dịch vụ riêng lẻ lại thành một đơn vị triển khai duy nhất.
Tóm lại: Tính
chất "Mediated" (có một bộ điều phối trung tâm) thường triệt
tiêu khả năng hình thành nhiều lượng tử kiến trúc, vì nó tạo ra sự phụ thuộc chặt
chẽ vào một điểm kiểm soát duy nhất để hệ thống có thể vận hành.
4. Kiến trúc hướng sự kiện kiểu môi giới (broker)
Ngay cả một kiến trúc
phân tán như kiến trúc hướng sự kiện kiểu môi giới vẫn có thể chỉ là một lượng
tử duy nhất.
Đây là một kiến trúc hướng
sự kiện kiểu môi giới (broker-style)—nghĩa là các dịch vụ nói chuyện với
nhau thông qua tin nhắn và không có một "nhạc trưởng" nào đứng ra điều
khiển. Nhìn vào sơ đồ, bạn thấy các khối dịch vụ nằm rời rạc, có vẻ rất độc lập.
Điểm mấu chốt nằm ở phần
dưới cùng: Tất cả các dịch vụ, dù phân tán đến đâu, đều kết nối vào một Cơ sở
dữ liệu (Database) duy nhất.
Ranh giới của một
"lượng tử kiến trúc" (Architecture Quantum) được xác định bởi sự phụ
thuộc tĩnh (static coupling) cao. Phụ thuộc tĩnh là tất cả những gì cần thiết
để một dịch vụ có thể khởi động và vận hành được.
- Trong hình này, cơ sở dữ liệu chính là điểm phụ thuộc tĩnh chung.
- Vì mọi dịch vụ đều "dính" vào cái database này, nên nếu bạn thay đổi cấu trúc dữ liệu để phục vụ dịch vụ A, bạn có nguy cơ làm hỏng dịch vụ B hoặc C.
Dù đã loại bỏ được
"bộ điều phối trung tâm", hệ thống này vẫn thất bại trong việc tạo ra
nhiều lượng tử vì:
- Không thể triển khai độc lập:
Bạn không thể tự tin đẩy một dịch vụ lên mà không lo lắng về sự thay đổi
tương ứng ở database dùng chung.
- Ràng buộc vận hành:
Mọi đặc tính như hiệu suất, khả năng mở rộng hay tính chịu lỗi của các dịch
vụ đều bị trói buộc vào khả năng đáp ứng của cái database duy nhất đó.
Thông điệp cốt lõi ở đây:
"Sự phân tán về mặt
logic không cứu được sự ràng buộc về mặt dữ liệu".
Một hệ thống có thể dùng những công nghệ hiện đại nhất như Event-driven hay
Broker, nhưng nếu vẫn còn dùng chung một "cục" database thì về mặt kiến
trúc, nó vẫn chỉ là một khối thống nhất (Quantum of One) không thể tách rời khi
triển khai và vận hành.
5. Kiến trúc Microservices
Trong kiến trúc Microservices mỗi dịch vụ là một "Lượng tử kiến trúc" riêng biệt. Điều này là do mỗi dịch vụ sở hữu một kho lưu trữ dữ liệu riêng biệt. Đây chính là yếu tố quyết định để tách rời phụ thuộc tĩnh.
Mỗi dịch vụ và database
đi kèm trong một ô nét đứt màu cam riêng lẻ. Mỗi ô này chính là một Architecture
Quantum (Lượng tử kiến trúc) vì nó hội đủ các điều kiện:
- Triển khai độc lập:
Bạn có thể thay đổi và đẩy một dịch vụ lên môi trường thật mà không cần
quan tâm đến các dịch vụ còn lại.
- Phụ thuộc tĩnh cao bên trong:
Code và database của dịch vụ đó được "đi dây" chặt chẽ với nhau
để vận hành ổn định.
- Gắn kết chức năng cao:
Mỗi dịch vụ đóng vai trò là một ngữ cảnh giới hạn (bounded context),
chỉ tập trung giải quyết một phần việc nghiệp vụ cụ thể.
Khi mỗi dịch vụ là một lượng
tử độc lập, kiến trúc sư có thể thiết kế các đặc tính riêng cho từng dịch vụ:
- Ví dụ:
Dịch vụ A có thể được thiết kế để có khả năng mở rộng (scalability)
cực cao để đáp ứng hàng triệu người dùng, trong khi dịch vụ B có thể ưu
tiên tối đa cho bảo mật (security).
- Trong các kiến trúc nguyên khối (Monolith), bạn buộc phải áp dụng cùng một mức độ bảo mật hay mở rộng cho toàn bộ hệ thống, gây lãng phí hoặc thiếu hiệu quả.
Các nhóm làm việc trên từng
dịch vụ có thể di chuyển nhanh nhất có thể. Họ không cần lo lắng việc
mình sửa đổi code hay thay đổi cấu trúc dữ liệu sẽ làm hỏng các phụ thuộc của
nhóm khác.
Tóm lại:
Bằng cách chia nhỏ hệ thống thành các lượng tử độc lập cả về logic lẫn dữ liệu,
chúng ta có thể xây dựng một hệ thống linh hoạt, cho phép từng phần tiến hóa
riêng biệt tùy theo nhu cầu nghiệp vụ.
6. Giao diện người dùng phụ thuộc chặt chẽ
Một giao diện người dùng phụ
thuộc chặt chẽ có thể kéo số lượng tử của kiến trúc microservices xuống còn 1.
Bạn thấy rất nhiều dịch vụ
riêng lẻ, mỗi dịch vụ đều có Database riêng. Theo lý thuyết, đây là một cấu
trúc microservices lý tưởng để hình thành nên nhiều lượng tử kiến trúc
(Architecture Quantum) riêng biệt.
Tuy nhiên, có một ô nét đứt
màu cam bao trùm toàn bộ hệ thống (bao gồm cả UI, API layer và tất cả dịch
vụ). Lý do nằm ở khối trên cùng: User interface.
- Điểm phụ thuộc (Coupling Point):
Tất cả các dịch vụ backend đều phải kết nối với một bộ UI duy nhất. UI này
đóng vai trò như một lớp "keo" dính chặt các phần lại với nhau.
- Lời gọi đồng bộ (Synchronous calls):
Các mũi tên hai đầu ghi "Synchronous calls" giữa UI và backend
cho thấy sự phụ thuộc lẫn nhau về mặt thời gian thực. Nếu một vài dịch vụ
backend gặp sự cố, UI nguyên khối này thường sẽ không thể hoạt động bình
thường, từ đó làm tê liệt toàn bộ trải nghiệm người dùng.
Khả năng triển khai độc lập
và đặc tính kiến trúc đã bị phá vỡ:
- Mất tính độc lập vận hành:
Bạn khó có thể thiết kế dịch vụ A có khả năng mở rộng (scalability) cực
cao trong khi dịch vụ B chỉ ở mức trung bình, nếu cả hai đều bị buộc phải
phối hợp cùng nhau trong một cái "khung" UI duy nhất.
- Sự "vướng víu"
(Entanglement): Vì UI là một khối thống nhất, nên
khi bạn muốn thay đổi giao diện cho một tính năng mới ở một dịch vụ
backend, bạn thường phải kiểm thử và triển khai lại toàn bộ bộ UI đó cùng
lúc.
Thông điệp cốt lõi: Đừng
chỉ tập trung vào việc chia nhỏ code hay database. Nếu bạn có 100 microservices
siêu hiện đại nhưng tất cả đều phải "nuôi" chung một bộ UI nguyên khối
theo kiểu đồng bộ, thì về mặt kiến trúc, bạn vẫn đang vận hành một nguyên khối (Monolith)
khổng lồ.
7. Kiến trúc Micro-frontend
Trong một kiến trúc micro-frontend, mỗi dịch vụ kết hợp với thành phần giao diện người dùng tạo thành một lượng tử kiến trúc.
Ngay cả khi bạn đã chia
nhỏ backend thành các microservices với database riêng, hệ thống vẫn có thể bị
"dính" lại thành một khối nếu tất cả dùng chung một bộ UI nguyên khối
(Monolithic UI).
Trong sơ đồ này, bạn sẽ
thấy một sự thay đổi mang tính cách mạng:
- User Interface (Giao diện người
dùng): Không còn là một khối liền nhất. Nó
đóng vai trò như một "tấm vải bạt" (canvas).
- UI Components (Các thành phần UI):
Những ô vuông nhỏ bên trong UI đại diện cho các mảnh giao diện được phát
ra trực tiếp từ các dịch vụ tương ứng.
- Ô nét đứt màu cam:
Đây là điểm quan trọng nhất. Các ô này bao quanh bộ ba: Thành phần UI +
Dịch vụ backend + Cơ sở dữ liệu.
Để được gọi là một Lượng
tử kiến trúc (Architecture Quantum), một đơn vị phải đảm bảo tính triển
khai độc lập và phụ thuộc tĩnh cao bên trong.
Trong sơ đồ này:
- Tính độc lập hoàn toàn:
Nếu bạn muốn thay đổi cách hiển thị của "Dịch vụ A" (ví dụ: thay
đổi nút bấm trên màn hình), bạn có thể cập nhật cả UI component lẫn
backend của dịch vụ đó và triển khai chúng cùng lúc mà không cần phải đụng
đến hay triển khai lại toàn bộ giao diện của các dịch vụ khác.
- Phi phụ thộc (Decoupling):
Các mũi tên cho thấy sự kết nối diễn ra trực tiếp giữa mảnh UI và backend
tương ứng. Điều này ngăn chặn việc các dịch vụ bị "vướng víu"
vào nhau thông qua một lớp UI chung.
Khi mỗi dịch vụ cùng với
phần giao diện của nó tạo thành một lượng tử độc lập, kiến trúc sư có thể:
- Tùy biến đặc tính:
Một lượng tử có thể yêu cầu tính bảo mật cực cao, trong khi lượng tử khác
lại ưu tiên hiệu năng và khả năng mở rộng.
- Tăng tốc độ phát triển:
Các đội ngũ có thể làm việc trên toàn bộ "lát cắt" của tính năng
(từ giao diện xuống đến database) mà không sợ làm hỏng các phần khác của hệ
thống.
Tóm lại: đây
là đại diện cho trạng thái lý tưởng của Microservices. Nó chứng minh rằng chỉ
khi bạn chia nhỏ được cả phần giao diện người dùng và gắn nó chặt chẽ
vào dịch vụ backend tương ứng, bạn mới thực sự đạt được sự tự do và linh hoạt tối
đa của một hệ thống phân tán.
8. Cơ sở dữ liệu dùng chung
Một cơ sở dữ liệu dùng
chung tạo thành điểm phụ thuộc giữa hai hệ thống, tạo ra một lượng tử kiến trúc
duy nhất.
Trong sơ đồ này có hai hệ
thống có vẻ ngoài hoàn toàn khác biệt:
- Bên trái:
Một kiến trúc nguyên khối phân lớp (Layered Monolith) truyền thống.
- Bên phải:
Một kiến trúc hiện đại hơn với lớp API và các dịch vụ riêng lẻ.
- Điểm mấu chốt:
Cả hai hệ thống này đều được kết nối với một khối ở giữa gọi là Integration
DB (Cơ sở dữ liệu tích hợp).
Dù hệ thống bên phải có
thể đã được chia nhỏ thành các dịch vụ, nhưng việc cả hai hệ thống cùng
"nhìn" vào một cơ sở dữ liệu chung đã tạo ra một điểm phụ thuộc
tĩnh (static coupling point) cực mạnh.
Bất kỳ điểm phụ thuộc
mang tính tổng thể nào cần thiết để kiến trúc vận hành đều sẽ kéo các thành phần
xung quanh nó vào chung một ranh giới lượng tử. Trong trường hợp này, vì cơ sở
dữ liệu tích hợp là sợi dây liên kết chung:
- Mất tính độc lập:
Nếu hệ thống Monolith thay đổi cấu trúc bảng để phục vụ tính năng mới, hệ
thống API bên phải cũng có nguy cơ bị hỏng nếu nó sử dụng chung các bảng
đó.
- Triển khai Lockstep:
Bạn không thể tự tin triển khai riêng rẽ hệ thống bên phải mà không phải
phối hợp cực kỳ chặt chẽ với đội ngũ quản lý hệ thống Monolith và database
chung.
Sơ đồ này giúp kiến trúc
sư:
- Xác định phạm vi ảnh hưởng:
Biết chính xác những hệ thống nào sẽ bị tác động khi có một thay đổi nhỏ
diễn ra ở phần lõi dữ liệu.
- Nhận diện "di sản"
(Legacy): Đây là kịch bản cực kỳ phổ biến khi
các công ty cố gắng hiện đại hóa hệ thống bằng cách xây dựng cái mới bên cạnh
cái cũ nhưng lại "tiết kiệm" bằng cách dùng chung database.
9. Kết luận
Architecture Quantum
không phụ thuộc vào việc hệ thống được chia thành bao nhiêu dịch vụ hay sử dụng
công nghệ hiện đại đến đâu. Điều quan trọng là các thành phần có thực sự triển
khai độc lập hay vẫn bị ràng buộc bởi những điểm phụ thuộc như cơ sở dữ liệu
dùng chung, giao diện người dùng nguyên khối hoặc bộ điều phối trung tâm.








Comments
Post a Comment