Kiến trúc sạch của chú Bob - Một lớp thực thể / mô hình cho mỗi lớp?


44

LÝ LỊCH :

Tôi đang cố gắng sử dụng kiến ​​trúc sạch của chú Bob trong ứng dụng Android của tôi. Tôi đã nghiên cứu nhiều dự án nguồn mở đang cố gắng chỉ ra cách thức phù hợp để thực hiện nó và tôi đã tìm thấy một triển khai thú vị dựa trên RxAndroid.

Những gì tôi thông báo:

Trong mỗi lớp (bản trình bày, tên miền và dữ liệu), có một lớp mô hình cho cùng một thực thể (nói UML). Thêm vào đó, có các lớp trình ánh xạ đảm nhiệm việc chuyển đổi đối tượng bất cứ khi nào dữ liệu vượt qua các ranh giới (từ lớp này sang lớp khác).

CÂU HỎI:

Có bắt buộc phải có các lớp mô hình trong mỗi lớp không khi tôi biết rằng tất cả chúng sẽ kết thúc với cùng một thuộc tính nếu tất cả các hoạt động CRUD là cần thiết? Hoặc, đó là một quy tắc hoặc thực tiễn tốt nhất khi sử dụng kiến ​​trúc sạch?

Câu trả lời:


52

Theo tôi, đó hoàn toàn không phải là ý nghĩa của nó. Và đó là vi phạm DRY.

Ý tưởng là đối tượng thực thể / miền ở giữa được mô hình hóa để đại diện cho miền tốt nhất và thuận tiện nhất có thể. Nó nằm ở trung tâm của mọi thứ và mọi thứ có thể phụ thuộc vào nó vì bản thân tên miền không thay đổi hầu hết thời gian.

Nếu cơ sở dữ liệu của bạn ở bên ngoài có thể lưu trữ các đối tượng đó trực tiếp, thì ánh xạ chúng sang định dạng khác nhằm mục đích tách các lớp không chỉ là vô nghĩa mà còn tạo ra các bản sao của mô hình và đó không phải là ý định.

Để bắt đầu, kiến ​​trúc sạch đã được thực hiện với một môi trường / kịch bản điển hình khác nhau trong tâm trí. Các ứng dụng máy chủ doanh nghiệp với các lớp ngoài cùng cần các loại đối tượng đặc biệt của riêng chúng. Ví dụ cơ sở dữ liệu sản xuất SQLRowcác đối tượng và cần SQLTransactionstrả lại để cập nhật các mục. Nếu bạn sử dụng những người ở trung tâm, bạn đã vi phạm hướng phụ thuộc vì cốt lõi của bạn sẽ phụ thuộc vào cơ sở dữ liệu.

Với các ORM nhẹ tải và lưu trữ các đối tượng thực thể không phải là trường hợp. Họ thực hiện ánh xạ giữa nội bộ của họ SQLRowvà tên miền của bạn. Ngay cả khi bạn cần đặt một @Entitiychú thích của ORM vào đối tượng miền của mình, tôi vẫn cho rằng điều này không thiết lập "đề cập" của lớp bên ngoài. Bởi vì các chú thích chỉ là siêu dữ liệu, không có mã nào không đặc biệt tìm kiếm chúng sẽ thấy chúng. Và quan trọng hơn, không có gì cần thay đổi nếu bạn xóa chúng hoặc thay thế chúng bằng chú thích của cơ sở dữ liệu khác.

Ngược lại, nếu bạn thay đổi tên miền của mình và bạn đã tạo ra tất cả những người lập bản đồ đó, bạn phải thay đổi rất nhiều.


Sửa đổi: Trên đây là một chút đơn giản hóa và thậm chí có thể sai. Bởi vì có một phần trong kiến ​​trúc sạch sẽ muốn bạn tạo một đại diện cho mỗi lớp. Nhưng điều đó phải được nhìn thấy trong bối cảnh của ứng dụng.

Cụ thể như sau đây https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-arch architecture.html

Điều quan trọng là các cấu trúc dữ liệu đơn giản, đơn độc được truyền qua các ranh giới. Chúng tôi không muốn gian lận và vượt qua các hàng Thực thể hoặc Cơ sở dữ liệu. Chúng tôi không muốn các cấu trúc dữ liệu có bất kỳ loại phụ thuộc nào vi phạm Quy tắc phụ thuộc.

Việc chuyển các thực thể từ trung tâm sang các lớp bên ngoài không vi phạm quy tắc phụ thuộc, tuy nhiên chúng được đề cập. Nhưng điều này có một lý do trong bối cảnh của ứng dụng được hình dung. Vượt qua các thực thể xung quanh sẽ di chuyển logic ứng dụng ra bên ngoài. Các lớp bên ngoài sẽ cần phải biết cách diễn giải các đối tượng bên trong, chúng thực sự sẽ phải làm những gì các lớp bên trong như lớp "trường hợp sử dụng" phải làm.

Bên cạnh đó, nó cũng tách riêng các lớp để thay đổi lõi không nhất thiết yêu cầu thay đổi ở các lớp bên ngoài (xem bình luận của SteveCallender). Trong bối cảnh đó, thật dễ dàng để xem các đối tượng nên thể hiện cụ thể mục đích mà chúng được sử dụng như thế nào. Ngoài ra, các lớp nên nói chuyện với nhau về các đối tượng được tạo riêng cho mục đích giao tiếp này. Điều này thậm chí có thể có nghĩa là có 3 biểu diễn, 1 trong mỗi lớp, 1 để vận chuyển giữa các lớp.

Và có https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Arch architecture.html địa chỉ trên:

Những người khác đã lo lắng rằng kết quả ròng của lời khuyên của tôi sẽ là rất nhiều mã trùng lặp và rất nhiều bản sao dữ liệu từ cấu trúc dữ liệu này sang cấu trúc dữ liệu khác trên các lớp của hệ thống. Chắc chắn tôi cũng không muốn điều này; và không có gì tôi đã đề xuất chắc chắn sẽ dẫn đến sự lặp lại các cấu trúc dữ liệu và sự không phù hợp của sao chép trường.

IMO đó ngụ ý rằng việc sao chép các đối tượng đơn giản 1: 1 là một mùi trong kiến ​​trúc bởi vì bạn không thực sự sử dụng các lớp và / hoặc trừu tượng thích hợp.

Sau đó, anh giải thích cách anh tưởng tượng tất cả sự "sao chép"

Bạn tách giao diện người dùng khỏi các quy tắc kinh doanh bằng cách chuyển các cấu trúc dữ liệu đơn giản giữa hai quy tắc. Bạn không để bộ điều khiển của bạn biết bất cứ điều gì về các quy tắc kinh doanh. Thay vào đó, các bộ điều khiển giải nén đối tượng HttpRequest vào cấu trúc dữ liệu vanilla đơn giản và sau đó chuyển cấu trúc dữ liệu đó đến một đối tượng tương tác thực hiện trường hợp sử dụng bằng cách gọi các đối tượng kinh doanh. Sau đó, bộ tương tác tập hợp dữ liệu phản hồi vào một cấu trúc dữ liệu vanilla khác và chuyển nó trở lại UI. Các quan điểm không biết về các đối tượng kinh doanh. Họ chỉ nhìn vào cấu trúc dữ liệu đó và trình bày phản hồi.

Trong ứng dụng này, có một sự khác biệt lớn giữa các đại diện. Dữ liệu chảy không chỉ là các thực thể. Và điều này đảm bảo và yêu cầu các lớp khác nhau.

Tuy nhiên, áp dụng cho một ứng dụng Android đơn giản như trình xem ảnh trong đó Photothực thể có khoảng 0 quy tắc kinh doanh và "trường hợp sử dụng" liên quan đến chúng gần như không tồn tại và thực sự quan tâm nhiều hơn đến bộ nhớ đệm & tải xuống (quá trình đó nên IMO được trình bày rõ ràng hơn), điểm để tạo các biểu diễn riêng biệt của một bức ảnh bắt đầu biến mất. Tôi thậm chí có cảm giác rằng chính bức ảnh là đối tượng truyền dữ liệu trong khi lớp lõi logic kinh doanh thực sự bị thiếu.

Có một sự khác biệt giữa "tách giao diện người dùng khỏi quy tắc kinh doanh bằng cách chuyển các cấu trúc dữ liệu đơn giản giữa hai""khi bạn muốn hiển thị ảnh đổi tên nó 3 lần trên đường đi" .

Bên cạnh đó, điểm mà tôi thấy các ứng dụng demo đó thất bại trong việc thể hiện kiến ​​trúc sạch là chúng chú trọng rất lớn vào việc tách các lớp nhằm mục đích tách các lớp nhưng che giấu hiệu quả những gì ứng dụng làm. Điều đó trái ngược với những gì được nói trong https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Arch architecture.html - cụ thể là

kiến trúc của một ứng dụng phần mềm hét lên về các trường hợp sử dụng của ứng dụng

Tôi không thấy sự nhấn mạnh vào việc tách các lớp trong kiến ​​trúc sạch. Đó là về hướng phụ thuộc và tập trung vào việc đại diện cho cốt lõi của ứng dụng - các thực thể và trường hợp sử dụng - trong java đơn giản lý tưởng mà không phụ thuộc vào bên ngoài. Đó không phải là quá nhiều về sự phụ thuộc vào cốt lõi đó.

Vì vậy, nếu ứng dụng của bạn thực sự có lõi đại diện cho các quy tắc kinh doanh và trường hợp sử dụng và / hoặc những người khác nhau làm việc trên các lớp khác nhau, vui lòng tách chúng theo cách đã định. Mặt khác, nếu bạn chỉ viết một ứng dụng đơn giản thì đừng quá lạm dụng nó. 2 lớp với giới hạn trôi chảy có thể là quá đủ. Và các lớp có thể được thêm vào sau này là tốt.


1
@RamiJemli Lý tưởng nhất là các thực thể giống nhau trong tất cả các ứng dụng. Đó là sự khác biệt giữa "quy tắc kinh doanh trên toàn doanh nghiệp" và "quy tắc kinh doanh ứng dụng" (đôi khi là kinh doanh so với logic ứng dụng). Cốt lõi là một đại diện rất trừu tượng của các thực thể của bạn đủ chung chung để bạn có thể sử dụng nó ở mọi nơi. Hãy tưởng tượng một ngân hàng có nhiều ứng dụng, một cho hỗ trợ khách hàng, một chạy trên máy rút tiền, một là web ui cho chính khách hàng. Tất cả những người có thể sử dụng giống nhau BankAccountnhưng với các quy tắc cụ thể của ứng dụng, bạn có thể làm gì với tài khoản đó.

4
Tôi nghĩ một điểm quan trọng trong kiến ​​trúc sạch là bằng cách sử dụng lớp bộ điều hợp giao diện để chuyển đổi (hoặc như bạn nói bản đồ) giữa biểu diễn của các lớp khác nhau của thực thể mà bạn giảm sự phụ thuộc vào thực thể đã nói. Nếu có sự thay đổi trong các lớp Usecase hoặc Entity (hy vọng là không thể nhưng vì các yêu cầu sẽ thay đổi các lớp này) thì tác động của thay đổi được chứa trong lớp bộ điều hợp. Nếu bạn chọn sử dụng cùng một đại diện của thực thể thông qua kiến ​​trúc của mình, tác động của thay đổi này sẽ lớn hơn nhiều.
SteveCallender

1
@RamiJemli thật tốt khi sử dụng các khung làm cho cuộc sống đơn giản hơn, vấn đề là bạn nên cẩn thận khi kiến ​​trúc của bạn dựa vào chúng và bạn bắt đầu đặt chúng vào trung tâm của mọi thứ. Đây thậm chí là một bài viết về blog RxJava.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html - không nói rằng bạn không nên sử dụng nó. Giống như: Tôi đã thấy điều này, nó sẽ khác trong một năm và khi ứng dụng của bạn vẫn ở xung quanh bạn bị mắc kẹt với nó. Làm cho nó trở thành một chi tiết và làm những điều quan trọng nhất trong java cũ đơn giản trong khi áp dụng các nguyên tắc RẮN cũ đơn giản.
zapl

1
@zapl Bạn có cảm thấy giống như vậy về một lớp dịch vụ web không? Nói cách khác, bạn có đặt @SerializedNamechú thích Gson trên mô hình miền không? Hoặc bạn sẽ tạo một đối tượng mới chịu trách nhiệm ánh xạ phản hồi web vào mô hình miền?
tir38

2
@ tir38 Bản thân sự phân tách không mang lại lợi ích, đó là chi phí cho những thay đổi trong tương lai đi cùng với nó. => Phụ thuộc vào ứng dụng. 1) bạn tốn thời gian để tạo và duy trì giai đoạn được thêm vào để chuyển đổi giữa các biểu diễn khác nhau. Ví dụ: thêm một trường vào miền và quên thêm nó vào một nơi khác không phải là chưa từng thấy. Không thể xảy ra với cách tiếp cận đơn giản. 2) Chi phí để chuyển sang thiết lập phức tạp hơn sau này trong trường hợp hóa ra bạn cần nó. Thêm các lớp không dễ, do đó, dễ dàng hơn trong các ứng dụng lớn để chứng minh nhiều lớp không cần thiết ngay lập tức
zapl

7

Bạn thực sự đã làm đúng. Và không có vi phạm DRY vì bạn chấp nhận SRP.

Ví dụ: Bạn có Phương thức tạo doanh nghiệp (Tên chuỗi) thì bạn có thể có Phương thức tạoX (Tên chuỗi) trong Lớp DAO, được gọi trong Phương thức nghiệp vụ. Họ có thể có cùng chữ ký và có thể chỉ có một phái đoàn nhưng họ có những mục đích khác nhau. Bạn cũng có thể có một createX (Tên chuỗi) trên UseCase. Ngay cả sau đó nó không dư thừa. Ý tôi là với điều này là: Chữ ký giống nhau không có nghĩa là cùng một ngữ nghĩa. Chọn tên khác cho nó để có ngữ nghĩa rõ ràng. Đặt tên cho chính nó không ảnh hưởng đến SRP.

UseCase chịu trách nhiệm về logic dành riêng cho ứng dụng, đối tượng kinh doanh chịu trách nhiệm về logic độc lập với ứng dụng và DAO chịu trách nhiệm lưu trữ.

Do ngữ nghĩa khác nhau, tất cả các lớp có thể có mô hình đại diện và truyền thông riêng. Thường thì bạn thấy "thực thể" là "đối tượng kinh doanh" và thường thì bạn không thấy sự cần thiết phải tách chúng ra. Nhưng trong các dự án "khổng lồ", cần nỗ lực để phân tách các lớp một cách hợp lý. Dự án càng lớn, khả năng tăng lên mà bạn cần các ngữ nghĩa khác nhau được thể hiện trong các lớp và các lớp khác nhau.

Bạn có thể nghĩ về các khía cạnh khác nhau của cùng một ngữ nghĩa. Một đối tượng người dùng phải được hiển thị trên màn hình, nó có một số quy tắc nhất quán bên trong và nó phải được lưu trữ ở đâu đó. Mỗi khía cạnh nên được đại diện trong một lớp khác nhau (SRP). Tạo các mapper có thể là một vấn đề khó khăn vì vậy trong hầu hết các dự án tôi làm trong các khía cạnh này đều bị tan chảy thành một lớp. Đây rõ ràng là một sự vi phạm SRP nhưng không ai thực sự quan tâm.

Tôi gọi ứng dụng của kiến ​​trúc sạch và RẮN là "không được xã hội chấp nhận". Tôi sẽ làm việc với nó nếu tôi được phép. Hiện tại tôi không được phép làm điều đó. Tôi chờ đợi thời điểm chúng ta phải suy nghĩ về việc nghiêm túc thực hiện.


Tôi nghĩ rằng không có phương thức nào trong lớp Dữ liệu nên có cùng chữ ký với bất kỳ phương thức nào trong lớp Miền. Trong lớp Miền, bạn sử dụng các quy ước đặt tên liên quan đến kinh doanh như đăng ký hoặc đăng nhập và trong lớp Dữ liệu, bạn sử dụng lưu (nếu mẫu DAO) hoặc thêm (nếu Kho lưu trữ vì mẫu này sử dụng Bộ sưu tập làm ẩn dụ). Cuối cùng, tôi không nói về các thực thể (Dữ liệu) và mô hình (Miền), tôi nhấn mạnh sự vô dụng của UserModel và Mapper (lớp trình bày) của nó. Bạn có thể gọi lớp Người dùng của tên miền trong bản trình bày và điều này không vi phạm quy tắc phụ thuộc.
Rami Jemli

Tôi đồng tình với Rami, trình ánh xạ là không cần thiết vì bạn có thể thực hiện ánh xạ trực tiếp trong quá trình thực hiện tương tác.
Christopher Perry

5

Không, bạn không cần tạo các lớp mô hình trong mỗi lớp.

Thực thể ( DATA_LAYER) - là biểu diễn đầy đủ hoặc một phần của đối tượng Cơ sở dữ liệu.DATA_LAYER

Mapper ( DOMAIN_LAYER) - thực sự là một lớp chuyển đổi Entity thành ModelClass, sẽ được sử dụng trênDOMAIN_LAYER

Hãy xem: https://github.com/lifedemons/photoviewer


1
Tất nhiên, tôi không chống lại các thực thể trong lớp dữ liệu, nhưng, trong ví dụ của bạn, lớp PhotoModel trong lớp trình bày có cùng thuộc tính của lớp Photo trong lớp miền. Về mặt kỹ thuật là cùng một lớp. có cần thiết không

Tôi nghĩ có gì đó không ổn trong ví dụ của bạn vì lớp miền không nên phụ thuộc vào các lớp khác vì trong ví dụ của bạn, người lập bản đồ của bạn phụ thuộc vào các thực thể trong lớp dữ liệu của bạn mà IMO, nó phải ngược lại
navid_gh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.