Sau khi hoàn thành chương này, người đọc có khả năng:
Hiểu được tại sao Coupling và Cohesion luôn đi song hành và giống như hai mặt của một đồng xu trong thiết kế phần mềm.
Giải thích được nguyên lý "High Cohesion, Low Coupling" dưới góc độ phân bổ chi phí thay đổi.
Nhận thức được sự nguy hiểm của việc tối ưu hóa cực đoan một khía cạnh mà bỏ qua khía cạnh còn lại (ví dụ: hội chứng Ravioli code hoặc God Object).
1. Vì sao hai khái niệm luôn xuất hiện cùng nhau
Coupling và Cohesion không phải là hai khái niệm độc lập; chúng là kết quả của cùng một hành động duy nhất trong thiết kế phần mềm: vẽ ranh giới (drawing boundaries).
Khi bạn quyết định gom các hàm, thuộc tính lại thành một class, hoặc tách một module lớn thành các module nhỏ, bạn đang đồng thời can thiệp vào cả hai chỉ số này. Ranh giới đó quyết định những gì nằm ở bên trong (Cohesion) và những gì phải đi xuyên qua ranh giới để giao tiếp với bên ngoài (Coupling).
Một nguyên lý bất biến: Bất kỳ logic nào bị đẩy ra khỏi ranh giới của một module (làm giảm Cohesion của module đó nếu logic đó thực sự thuộc về nó), thì hệ thống sẽ phải bù đắp lại bằng một sợi dây liên kết giữa module đó với nơi chứa logic mới (tạo ra Coupling).
2. Nguyên lý "High Cohesion, Low Coupling"
Đây là câu thần chú kinh điển nhất trong Software Architecture. Tuy nhiên, thay vì coi nó là một khẩu hiệu sáo rỗng, hãy phân tích cơ chế hoạt động của nó:
High Cohesion đảm bảo rằng "những thứ thay đổi cùng nhau sẽ nằm cùng với nhau". Khi một yêu cầu nghiệp vụ (ví dụ: thay đổi logic tính toán tồn kho) xuất hiện, bạn chỉ cần mở đúng một class hoặc một module, sửa đổi và đóng lại.
Low Coupling đảm bảo rằng "sự thay đổi ở một nơi không làm vỡ một nơi khác". Khi bạn đã sửa xong logic tính toán tồn kho ở trên, bạn tự tin rằng class quản lý đơn hàng (Order) hay class thanh toán (Payment) không bị ảnh hưởng, vì chúng chỉ phụ thuộc vào một hợp đồng (interface) ổn định.
Sự kết hợp này tối thiểu hóa chi phí bảo trì (blast radius nhỏ nhất) và tối đa hóa khả năng đọc hiểu (cognitive load thấp nhất).
3. Vì sao không thể tối ưu một phía một cách độc lập
Hãy thử tưởng tượng hai kịch bản cực đoan để thấy sự phụ thuộc lẫn nhau của hai chỉ số này:
Cực đoan 1: Tối ưu Low Coupling bằng mọi giá (God Object)
Nếu bạn muốn loại bỏ hoàn toàn Coupling giữa các class, cách duy nhất là gom toàn bộ biến và hàm của hệ thống vào chung một class khổng lồ (ví dụ: SystemManager). Lúc này, Coupling giữa các class bằng 0 (vì chỉ có 1 class). Nhưng hệ quả là Cohesion chạm đáy (Coincidental Cohesion). Class này chứa mọi lý do để thay đổi trên đời, và việc đọc hiểu nó là bất khả thi.
Cực đoan 2: Tối ưu High Cohesion bằng mọi giá (Micro-classes / Ravioli Code)
Nếu đẩy Functional Cohesion đến mức cực đoan, bạn có thể tách hệ thống thành rất nhiều class nhỏ, mỗi class chỉ làm đúng một việc. Từng class đều có Cohesion cao, nhưng một nghiệp vụ đơn giản như tạo đơn hàng lại phải đi qua hàng chục class để kiểm tra sản phẩm, tính giá, lưu đơn và gửi thông báo. Giống như một dây chuyền bị chia thành quá nhiều công đoạn: mỗi công đoạn rõ ràng, nhưng tổng thể trở nên khó theo dõi, trong khi Coupling và chi phí phối hợp tăng mạnh.
4. Khi giảm Coupling quá mức lại sinh ra vấn đề mới
Trong các hệ thống backend hiện đại (đặc biệt khi xử lý các bài toán như thương mại điện tử, microservices, hoặc multi-tenant), việc ám ảnh với Low Coupling thường dẫn đến lạm dụng Event-Driven hoặc Pub/Sub nội bộ.
// Lạm dụng decouple: Không ai biết luồng thực thi thực sự đi về đâu
class OrderService {
async placeOrder(order: Order) {
await this.db.save(order);
// Bắn event và quên đi, hy vọng ai đó sẽ trừ tồn kho và gửi email
this.eventBus.publish('OrderCreated', order);
}
}
Trong ví dụ trên, Coupling bề mặt có vẻ bằng 0. OrderService không gọi hàm của InventoryService (khóa kho) hay NotificationService (gửi email). Tuy nhiên:
Vấn đề: Nếu quá trình khóa kho thất bại (hết hàng), làm sao để rollback lại Order?
Hệ quả: Logic nghiệp vụ bị phân mảnh. Việc giảm thiểu Coupling một cách mù quáng đã phá vỡ mất Cohesion của luồng nghiệp vụ tạo đơn hàng (một luồng vốn mang tính tuần tự và transaction chặt chẽ).
Ta nhận thấy được rằng giảm Coupling là tốt, nhưng không được phép phá vỡ Cohesion của những quy trình nghiệp vụ (Business Workflow) vốn sinh ra là để đi liền với nhau.