The Clean Architecture

Với những ai đã từng phát triển phần mềm thì có lẽ đã từng bắt gặp 1 số kiến trúc phần mềm sau:

Tất cả các kiến trúc trên đều hướng đến việc xây dựng một phần mềm đạt được những tiêu chí sau:

  1. Độc lập với các Frameworks. Kiến trúc không bị phụ thuộc vào một số thư viện. Điều này cho phép bạn sử dụng các Frameworks chỉ như là các công cụ thay vì hệ thống của bạn bị ràng buộc bởi chúng.

  2. Có thể kiểm thử. Logic nghiệp vụ có thể dễ dàng kiểm thử mà không cần UI, Database, Web Server hoặc bất kỳ thành phần bên ngoài nào.

  3. Độc lập với UI. Có thể thay đổi UI một cách dễ dàng mà không ảnh hưởng đến các phần còn lại của hệ thống. Ví dụ bạn có thể thay đổi Web UI bằng Mobile UI mà không làm ảnh hưởng đến các logic nghiệp vụ.

  4. Độc lập với Cơ sở dữ liệu. Bạn có thể chuyển đổi linh hoạt giữa các loại Cơ sở dữ liệu như Oracle, SQL Server, MongoDb hay bất kỳ Cơ sở dữ liệu nào.

  5. Độc lập với bất kỳ thành phần nào bên ngoài. Trong thực tế thì logic nghiệp vụ không có liên quan gì hay không biết gì về các thành phần bên ngoài nó.

Với những tiêu chí đặt ra ở trên, kết hợp với những kiến trúc trước đấy, Uncle Bob đã đưa ra 1 kiến trúc mới gọi là Clean Architecture.

Hình vẽ trên bao gồm các vòng tròn đồng tâm đại diện cho những tầng riêng biệt của phần mềm. Những vòng tròn bên trong là Policy, còn những vòng tròn bên ngoài là Mechanism. Vậy policy và mechainsm là gì?

  • Policy - chính sách: là 1 tập các ý tưởng hoặc kế hoạch để làm 1 việc gì đó (chứa logic nghiệp vụ)
  • Mechanism - cơ chế: là 1 quá trình/kỹ thuật triển khai/thực thi các chính sách để đạt được kết quả (chứa implement chi tiết)

Clean Architecture là kiến trúc phần mềm được xây dựng dựa trên nguyên tắc sự phụ thuộc - The Dependency Rule.

The Dependency Rule

Source code dependencies can only point inwards

  • Các tầng bên ngoài chỉ có thể phụ thuộc vào các tầng bên trong và không có chiều ngược lại.
  • Các tầng bên ngoài không hề biết gì về các tầng bên trong.
  • Tên của bất kỳ thành phần nào của tầng bên ngoài không nên được đề cập đến ở các tầng bên trong. Ví dụ function, class, biến hay bất kỳ thực thể nào của hệ thống.
  • Định dạng dữ liệu ở các tầng bên ngoài không nên được sử dụng bởi các tầng bên trong, đặc biệt là các định dạng dữ liệu được tạo ra bởi các Frameworks. Đơn giản vì không muốn có bất kỳ ảnh hưởng nào từ các tầng bên ngoài đến các tầng bên trong.

The Properties of The Dependency Rule

  • Khi đi từ trong ra ngoài tính policy giảm dần, đồng thời tính mechanism tằng dần và ngược lại.
  • Càng vào trong mức độ trừu tượng của phần mềm càng tăng.
  • Tầng trong cùng sẽ là chung nhất, tầng ngoài cùng sẽ là chi tiết nhất.
  • Độc lập với các Frameworks, Database, UI
  • Có thể kiểm thử

Entities

Entities encapsulate Enterprise wide business rules

  • Phần mềm ở tầng này bao gồm các thực thể được sử dụng trong toàn hệ thống. Thực thể có thể là 1 đối tượng gồm nhiều phương thức hoặc có thể là 1 tập các cấu trúc dữ liệu và chức năng.
  • Nếu bạn chỉ có 1 ứng dụng đơn lẻ thay vì 1 hệ thống lớn, thì lúc này các thực thể chính là các đối tượng được sử dụng trong các logic nghiệp vụ của ứng dụng.
  • Các thực thể này được thiết kế nằm trong cùng của phần mềm nên chúng ít có khả năng bị thay đổi bởi các thành phần bên ngoài.

Use Cases

The software in this layer contains application specific business rules

  • Phần mềm ở tầng này bao gồm các logic nghiệp vụ cụ thể của ứng dụng, cụ thể đó là tất cả các use case của hệ thống. Các use case này điều phối luồng dữ liệu đến và đi từ các thực thể.
  • Khi các logic nghiêp vụ ở tầng này thay đổi cũng không làm ảnh hưởng đến tầng thực thể. Và các logic nghiệp vụ ở tầng này cũng không bị ảnh hưởng khi có sự thay đổi từ các thành phần bên ngoài như Cơ sở dữ liệu, UI hay bất kỳ Frameworks nào.
  • Tuy nhiên, nếu ứng dụng thay đổi yêu cầu thì tất nhiên các logic nghiệp vụ ở tầng này sẽ bị thay đổi ít nhiều và sẽ có những ảnh hưởng nhất định đến 1 số phần code.

Interface Adapters

The software in this layer is a set of adapters that convert data between usecases, entities and Database, UI

  • Phần mềm ở tầng này bao gồm 1 tập các adapter để chuyển đổi dữ liệu từ use cacse/entities thành định dạng dữ liệu phù hợp với các thành phần bên ngoài và ngược lại. Nếu bạn sử dụng mô hình MVC, MVP hay MVVM thì Controller, Presenter, ViewModel, Repository đóng vai trò là các Interface Adapters.
  • Nếu ứng dụng sử dụng Database, cụ thể là SQL database thì tất cả các câu lệnh SQL hay thao tác với database nên đưọc viết ở đây.

Frameworks and Drivers

The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework

  • Phần mềm ở tầng này bao gồm các Frameworks và công cụ khác như Database hay Web UI…
  • Đây là nơi chứa các chi tiết, Database chi tiết, Web UI chi tiết…

Only Four Circles?

1 phần mềm chỉ gồm 4 tầng?

  • Không, đây chỉ là tư tưởng, phần mềm của bạn có thể có nhiều hoặc ít hơn 4 tầng này.
  • Không có quy tắc nào nói phần mềm chỉ bao gồm 4 tầng, nhưng dù có bao nhiều tầng đi chăng nữa, bạn sẽ phải tuần thủ quy tắc sự phụ thuộc như đã nói ở trên.

Crossing boundaries

Tương tác giữa các tầng

  • Sử dụng nguyên lý Dependency Inversion Principle
  • Tuyệt đối không được phá vỡ quy tắc sự phụ thuộc
  • Với 1 số ngôn ngữ như Java/kotlin có thể sử dụng interfaces/inheritance

Tổng kết

Việc tuân thủ các quy tắc này không khó và nó sẽ rất hữu ích cho bạn trong tương lai. Bằng việc phân tách phần mềm thành các tầng khác nhau theo nguyên tắc sự phụ thuộc, bạn sẽ tạo nên 1 phần mềm có thể kiểm thử dễ dàng. Khi có 1 thành phần bên ngoài của phần mềm bị lỗi thời như Database hay Web Frameworks, bạn có thể dễ dàng thay đổi chúng bằng các thành phần khác mà chỉ tiêu tốn ít công sức.

Tham khảo: The Clean Architecture của Robert C. Martin (Uncle Bob).