Cách viết các ứng dụng mạnh mẽ mọi lúc, bằng cách sử dụng “Clean Architecture”

Là nhà phát triển, chúng ta không thể tiếp tục sử dụng các thư viện và framework bên ngoài trong hệ thống của mình. Việc cộng đồng tạo ra những công cụ hữu ích và việc sử dụng chúng là điều đương nhiên. Tuy nhiên, mọi thứ đều có mặt trái của nó.

Các nhóm và các cá nhân bất cẩn có thể rơi vào tình huống nguy hiểm bằng cách cấu trúc hệ thống của họ xung quanh các công cụ mà họ sử dụng. Các business logic có thể bị lẫn lộn với các chi tiết thực hiện. Điều này có thể dẫn đến một hệ thống khó mở rộng và bảo trì. Những gì nên thay đổi nhanh chóng trong GUI cuối cùng lại biến thành một cuộc truy tìm lỗi kéo dài hàng giờ. Nhưng câu chuyện không cần thiết phải như thế này.

Kiến trúc phần mềm đề xuất các mô hình và quy tắc để xác định cấu trúc (classes, interfaces, và structs) trong một hệ thống và cách chúng liên quan với nhau. Các quy tắc này thúc đẩy khả năng tái sử dụng và sự tách biệt các mối quan tâm đối với các yếu tố này. Điều này giúp dễ dàng thay đổi các chi tiết triển khai như DBMS hoặc thư viện front-end. Trình tái cấu trúc và sửa lỗi ảnh hưởng đến càng ít bộ phận của hệ thống càng tốt. Và thêm các tính năng mới trở nên dễ dàng.

Trong bài viết này, tôi sẽ giải thích một mô hình kiến trúc được đề xuất vào năm 2012 bởi Robert C. Martin, Uncle Bob. Ông là tác giả của những tác phẩm kinh điển như Clean Code và The Clean Coder…vv.

Mô hình có được xây dựng dựa trên các khái niệm đơn giản:

Chia thành phần của hệ thống thành các lớp với các vai trò riêng biệt và được xác định rõ ràng. Và hạn chế các mối quan hệ giữa các entities ở các tầng khác nhau. Không có gì mới trong việc tách ứng dụng của bạn thành các lớp. Nhưng tôi đã chọn cách tiếp cận này vì nó là cách đơn giản nhất để nắm bắt và thực hiện. Và nó làm cho các usecase thử nghiệm trở nên đơn giản.

Chúng tôi chỉ cần đảm bảo Tương tác hoạt động bình thường. Đừng lo lắng nếu từ “Tương tác” có vẻ xa lạ với bạn, chúng ta sẽ tìm hiểu về chúng ngay sau đây.

Từ trong ra ngoài, chúng ta sẽ khám phá từng lớp sâu hơn một chút. Chúng tôi sẽ sử dụng một ứng dụng mẫu khá quen thuộc với chúng ta: counter app. Không mất thời gian để hiểu, vì vậy chúng ta có thể tập trung vào chủ đề của bài viết này.

Entities

Entities trong sơ đồ là Business Rules. Các Entities bao gồm các Business Rules phổ biến cho một công ty. Chúng đại diện cho các entities cơ bản đối với lĩnh vực hoạt động của nó. Chúng là những thành phần có mức độ trừu tượng cao nhất.

Trong ví dụ counter app của chúng tôi, có một Thực thể rất rõ ràng: chính là Counter.

Use Cases

Các trường hợp sử dụng được chỉ ra dưới dạng Application Business Rules. Chúng đại diện cho từng trường hợp sử dụng của một ứng dụng. Mỗi phần tử của lớp này cung cấp một giao diện cho lớp bên ngoài và hoạt động như một trung tâm giao tiếp với các phần khác của hệ thống. Chúng chịu trách nhiệm thực hiện hoàn chỉnh các usecase và thường được gọi là Tương tác.

Trong ví dụ của chúng tôi, chúng tôi có một Trường hợp sử dụng để tăng hoặc giảm bộ đếm của chúng

Lưu ý rằng factory function cho ChangeCounterInteractor nhận một tham số kiểu CounterGateway. Chúng ta sẽ thảo luận về sự tồn tại của loại hình này ở phần sau của bài viết. Nhưng chúng ta có thể nói rằng Gateways là thứ đứng giữa các usecase và layer tiếp theo.

Interface Adapters

Lớp này bao gồm ranh giới giữa các business rules của hệ thống và các công cụ cho phép hệ thống tương tác với các phần bên ngoài, như database và UI. Các phần tử trong lớp này hoạt động như các phần tử trung gian, nhận dữ liệu từ một lớp và chuyển nó sang lớp kia, điều chỉnh dữ liệu khi cần thiết.

Trong ví dụ của chúng tôi, chúng tôi có một vài Interface Adapters. Một trong số đó là React component trình bày Bộ đếm và các điều khiển của nó để tăng và giảm:

Lưu ý rằng component không sử dụng một thể hiện Counter để trình bày giá trị của nó mà thay vào đó là một instance của CounterViewData. Chúng tôi đã thực hiện thay đổi này để tách present logic khỏi business data. Một ví dụ về điều này là logic hiển thị của counter dựa trên view mode. Cách triển khai CounterViewData ở bên dưới:

Một ví dụ khác về Interface Adapter sẽ là triển khai Redux của ứng dụng của chúng tôi. Các mô-đun chịu trách nhiệm về các yêu cầu tới máy chủ và việc sử dụng bộ nhớ cục bộ cũng sẽ nằm trong layer này.

Frameworks and Drivers

Các công cụ mà hệ thống của bạn sử dụng để giao tiếp với các phần bên ngoài tạo nên lớp ngoài cùng. Chúng tôi thường không viết mã trong lớp này, bao gồm các thư viện như React / Redux, browser API, v.v.

The Dependency Rule

Sự phân chia thành các lớp này có hai mục tiêu chính. Một trong số đó là làm rõ trách nhiệm của từng bộ phận trong hệ thống. Hai là đảm bảo vai trò của mỗi lớp độc lập với nhau nhất có thể. Để điều này xảy ra, có một quy tắc nêu rõ các yếu tố phải phụ thuộc vào nhau như thế nào:

Một phần tử không được phụ thuộc vào bất kỳ phần tử nào thuộc một lớp bên ngoài lớp của nó.

Ví dụ: một phần tử trong Use Cases layer không được có bất kỳ thông tin nào về bất kỳ lớp hoặc mô-đun nào liên quan đến GUI hoặc tính ổn định của dữ liệu. Tương tự như vậy, một Entity không thể biết các Use Cases nào sử dụng nó.

Quy tắc này có thể đã đặt ra câu hỏi trong đầu bạn. Lấy ví dụ về một Use Case. Nó được kích hoạt do tương tác của người dùng với UI. Việc thực thi nó liên quan đến việc cập nhật trong một số bộ lưu trữ dữ liệu liên tục như cơ sở dữ liệu. Làm cách nào Interactor có thể thực hiện các lệnh gọi liên quan đến quy trình cập nhật mà không phụ thuộc vào Interface Adapter chịu trách nhiệm về tính ổn định của dữ liệu?

Câu trả lời nằm trong một yếu tố mà chúng tôi đã đề cập trước đây: Gateways. Họ chịu trách nhiệm thiết lập giao diện mà các usecase cần để thực hiện công việc của họ. Sau khi họ đã thiết lập giao diện này, Interface Adapters có thể thực hiện các khía cạnh của chúng, như thể hiện trong sơ đồ ở trên. Chúng tôi có CounterGateway interface và triển khai cụ thể bằng Redux bên dưới:

Có thể bạn không cần nó

Tất nhiên, ứng dụng mẫu này hơi phức tạp đối với counter app tăng / giảm. Và tôi muốn nói rõ rằng bạn không cần tất cả những điều này cho một dự án nhỏ hoặc nguyên mẫu. Nhưng hãy tin tôi, khi ứng dụng của bạn lớn hơn, bạn sẽ muốn tối đa hóa khả năng tái sử dụng và khả năng bảo trì. Kiến trúc phần mềm tốt giúp cho các dự án có khả năng chống chọi với thời gian.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.