Kiến Trúc Sạch

Đặc điểm chung của các ý tưởng về thiết kế tốt là chúng đều gợi ý để giúp chúng ta xây dựng những hệ thống mà không phụ thuộc vào bất kỳ thư viện hay framework nào, cũng như có thể kiểm thử được những quy tắc nghiệp vụ mà không cần tới bất kỳ thành phần ngoại vi nào khác như UI, Database… hay nói cách khác các quy tắc nghiệp vụ không biết tới thứ gì khác bên ngoài thế gới của nó.

Kiến trúc sau đây là một ý tưởng như thế.

Luật Phụ Thuộc

Những vòng tròn đồng tâm thể hiện những phạm vi khác nhau của phần mềm. Càng vào sâu thì cấp bậc của phần mềm càng cao. Những vòng tròn bên ngoài là các cơ chế. Những vòng ở trong là các chính sách.

Kiến trúc này hoạt động được nhờ được bao trùm bởi một thứ gọi là Luật Phụ thuộc. Theo đó:

Những phụ thuộc mã nguồn chỉ được hướng vào phía trong, về phía những chính sách cấp cao.

Như thế, không thứ gì của vòng trong có thể biết tới sự tồn tại của bất kỳ thứ gì ở vòng ngoài. Cụ thể, tên của bất kỳ thứ gì được khai báo ở vòng ngoài không bao giờ được xuất hiện tại mã của vòng trong. Bao gồm tên hàm, lớp, biến, hay bất kỳ thực thể phần mềm nào khác. Tương tự, những định dạng dữ liệu được dùng ở những vòng ngoài không được mang đi dùng ở những vòng phía trong, đặc biệt là những định dạng được tạo ra bởi framework ở vòng ngoài. Chúng ta không muốn bất kỳ thứ gì ở vòng ngoài tạo được sức ảnh hưởng tới vòng trong.

Các Entity

Các Entity bao gói các Quy tắc Nghiệp vụ Cốt lõi. Chúng có thể là đối tượng hoặc cũng có thể là một tập hợp của các cấu trúc dữ liệu và hàm. Chúng chính là những đối tượng nghiệp vụ của chương trình, bao gói những luật nghiệp vụ tổng quát cấp cao nhất. Chúng rất ít khi thay đổi bởi các yếu tố ngoại vi. Cho dù hệ thống được triển khai ở dạng thức ứng dụng nào đi chăng nữa cũng không kéo theo thay đổi lên các Entity.

Các Use Case

Phần mềm ở tầng Use Case chứa những quy tắc nghiệp vụ mức ứng dụng. Nó bao gói và triển khai tất cả các use case của hệ thống. Những use case đó điều phối luồng dữ liệu đến và đi từ các Entity, và lèo lái các Entity sử dụng Quy tắc Nghiệp vụ Cốt lõi để đạt được mục đích của use case.

Chúng ta không muốn sự thay đổi trên tầng này gây ảnh hưởng tới các Entity. Chúng ta cũng không muốn tầng này bị ảnh hưởng bởi các thay đổi tới từ các ngoại tố như Database, UI hay bất kỳ framework nào. Tầng này được cô lập khỏi những mối quan tâm đó đó.

Tuy nhiên, nếu cách hoạt động của ứng dụng cần được thay đổi, điều đó sẽ gây ảnh hưởng đến use case, và theo đó kéo theo thay đổi tới phần mềm ở tầng này.

Các Interface Adapter

Phần mềm ở tầng này là một tập hợp các bộ tiếp chuyển giúp chuyển đổi dữ liệu từ định dạng tự nhiên nhất với các use case và Entity sang định dạng tự nhiên nhất cho các ngoại tố như Datasabe hay Web. Lấy ví dụ, tầng này chính là toàn bộ khu vực chứa kiến trúc MVC. Các Presenter, các View, các Controller, tất cả đều được nằm ở đây. Các model đơn thuần là các cấu trúc dữ liệu được chuyển từ controller tới use case hay ngược từ use case tới presenter và view.

Tương tự như vậy, dữ liệu được chuyển đổi trong tầng này từ định dạng tự nhiên nhất cho các Entity và use case thành hình thái tự nhiên nhất cho tầng lưu tồn (ví dụ như Database). Không mã nào gọi đến vòng này được biết về cơ sở dữ liệu. Nếu cơ sở dữ liệu là một SQL database thì toàn bộ mã SQL phải được đặt trong tầng này, trong những bộ phận chuyên biệt làm nhiệm vụ giao tiếp với cơ sở dữ liệu.

Nói chung tầng này chứa mọi bộ tiếp chuyển cần thiết để chuyển đổi hình thái dữ liệu từ bất kỳ nguồn ngoại vi nào thành hình thái nội bộ mà các use case và các Entity có thể sử dụng.

Các framework và driver.

Tầng ngoài cùng nhìn chung được tạo nên từ các framework và công cụ như Database, Web Framework… Ở đây hầu như chỉ có những mã kết dính làm nhiệm vụ giao tiếp với tầng trong.

Tầng này là nơi những chi tiết xuất hiện. Web là một chi tiết. Cơ sở dữ liệu là một chi tiết. Chúng ta giữ những chi tiết ở vòng ngoài cùng, nơi mà chúng có thể gây ra ít ảnh hưởng nhất.

Không phải chỉ có bốn tầng

Kiến trúc trong hình chỉ là một dự định. Không có quy tắc nào giới hạn chỉ được dùng những vòng tròn được nói tới ở đây. Có thể sẽ có lúc cần nhiều hơn bốn vòng tròn, nhưng trong mọi trường hợp Luật Phụ thuộc phải được tuân thủ: các phụ thuộc mã chỉ được hướng vào trong. Càng vào trong mức trừu tượng càng tăng lên. Vòng ngoài cùng là những chi tiết ở mức thấp. Càng đi vào trong phần mềm càng trừu tượng hơn và bao gói những chính sách cấp cao hơn. Vòng trong cùng là khu vực có mức khái quát cao nhất và có cấp cao nhất.

Cắt qua các biên

Góc dưới bên phải của sơ đồ là một ví dụ về cách vượt qua đường biên giữa các tầng. Ở đây là trường hợp các controller và presenter giao tiếp với các use case ở tầng trong. Luồng điều khiển đi từ controller tới các use case sau đó quay lại các presenter. Vẫy mà tất cả các phụ thuộc mã nguồn vẫn tuân thủ luật phụ thuộc: hướng vào trong, về phía các use case.

Mâu thuẫn đó được giải quyết bằng Nguyên tắc Đảo ngược Phụ thuộc. Ví dụ, giả sử ca sử dụng cần gọi presenter (luồng điều khiển hướng ra ngoài), để không vi phạm Nguyên tắc Phụ thuộc, chúng ta cho ca sử dụng gọi tới một interface (Use Case Output Port) đặt tại vòng trong, và cho triển khai interface tại presenter nằm ở vòng ngoài.

Kỹ thuật tương tự được sử dụng để vượt qua tất cả các đường biên trong kiến trúc. Chúng ta lợi dụng đa hình động để tạo ra những mối phụ thuộc mã nguồn ngược hướng với luồng điều khiển, nhờ đó tuân thủ Nguyên tắc Phụ thuộc.

Dữ liệu nào sẽ đi qua đường biên?

Thông thường cấu trúc dữ liệu được chuyển giao giữa các biên không phức tạp. Một đối tượng mang dữ liệu, hoặc một bộ đối số, một hashmap… đều có thể đảm nhiệm được vai trò. Quan trọng nhất với hàng vượt biên là chúng phải có tính cô lập và đơn giản. Chúng ta sẽ không muốn cho các Entity, hay các database row được vượt biên. Không được làm cho vòng trong biết đến một chi tiết nào đó ở vòng ngoài, như thế vi phạm Luật Phụ thuộc.

Thành ra khi một dữ liệu vượt qua đường biên, nó luôn ở trong định dạng thuận tiện nhất cho vòng trong.

Một kịch bản điển hình

Sơ đồ dưới đây thể hiện một kịch bản điển hình cho một hệ thống Java nền web hoạt động với một database. DS là ký hiệu của Data Structure. Để ý hướng của các phụ thuộc, tất cả chúng băng qua các đường biên theo cùng một hướng, vào phía trong, tuân theo Luật Phụ Thuộc.

Kết luận

Tuân theo những luật đó là không khó, và điều đó sẽ giúp loại bỏ rất nhiều công sức vô ích. Bằng cách phân tách phần mềm thành các tầng, và tuân thủ Luạt Phụ thuộc, hệ thống sẽ có khả năng kiểm thử được một cách tự nhiên, điều đó sẽ tạo ra rất nhiều lợi ích đi kèm. Khi có thành phần nào đó của hệ thống trở nên lỗi thời, chằng hạn như database hay web framework, chúng có thể thay thế được theo một cách ít phiền toái nhất.

Loading

Leave a Reply

Your email address will not be published. Required fields are marked *