Чиста архітектура дядька Боба - клас сутності / моделі для кожного шару?


44

Передумови:

Я намагаюся використовувати чисту архітектуру дядька Боба в моєму додатку для android. Я вивчив багато проектів з відкритим кодом, які намагаються показати правильний спосіб зробити це, і знайшов цікаву реалізацію на основі RxAndroid.

ЩО Я СПІЧИЛИ:

У кожному шарі (презентація, домен та дані) є клас моделі для тієї ж сутності (говорить UML). Крім того, існують класи картографів, які опікуються перетворенням об'єкта, коли дані перетинають межі (від шару до іншого).

ПИТАННЯ:

Чи потрібно мати класи класів у кожному шарі, коли я знаю, що всі вони матимуть однакові атрибути, якщо всі CRUD-операції потрібні? Або це правило чи найкраща практика використання чистої архітектури?

Відповіді:


52

На мою думку, це абсолютно не так, як це мається на увазі. І це порушення DRY.

Ідея полягає в тому, щоб об'єкт / об'єкт домену посередині моделювався, щоб представити домен максимально добре та максимально зручно. Він знаходиться в центрі всього і все може залежати від цього, оскільки сам домен не змінюється більшу частину часу.

Якщо ваша база даних ззовні може зберігати ці об’єкти безпосередньо, то відображення їх у інший формат заради розділення шарів не просто безглуздо, а створення дублікатів моделі, і це не є наміром.

Для початку, чиста архітектура була зроблена з іншим типовим середовищем / сценарієм на увазі. Програми для бізнес-серверів із зовнішніми шарами, які потребують власних типів спеціальних об'єктів. Наприклад, бази даних, які створюють SQLRowоб'єкти і потребують SQLTransactionsвзамін оновлення елементів. Якщо ви використовуєте ті, хто знаходиться в центрі, ви повинні порушити напрямок залежності, оскільки ваше ядро ​​залежатиме від бази даних.

З легкими ОРМ-кодами, які завантажують і зберігають об'єкти об'єктів, це не так. Вони роблять відображення між внутрішнім SQLRowта вашим доменом. Навіть якщо вам потрібно помістити @Entitiyанотацію ORM до вашого доменного об’єкта, я стверджую, що це не встановлює "згадки" зовнішнього шару. Оскільки анотації - це лише метадані, жоден код, який спеціально не шукає їх, не побачить їх. І що ще важливіше, нічого не потрібно змінювати, якщо ви вилучите їх або заміните іншою анотацією до бази даних.

На відміну від цього, якщо ви змінили свій домен і зробили всі ці картографи, вам доведеться багато чого змінити.


Поправка: Вище трохи спрощена і навіть може бути помилковою. Тому що є частина чистої архітектури, яка хоче, щоб ви створили представлення на кожен шар. Але це потрібно бачити в контексті заявки.

А саме наступне тут https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Важливим є те, що окремі, прості структури даних передаються через межі. Ми не хочемо обманювати та передавати рядки Entities або Database. Ми не хочемо, щоб структури даних мали будь-яку залежність, яка б порушувала правило Dependency.

Проходження сутностей від центру до зовнішніх шарів не порушує правила залежності, проте вони згадуються. Але це є причиною в контексті передбаченої заяви. Передача сутностей навколо перемістить логіку програми назовні. Зовнішні шари повинні знати, як інтерпретувати внутрішні об'єкти, вони повинні ефективно робити те, що має робити внутрішні шари, такі як шар "використання випадку".

Крім того, він також розв'язує шари, так що зміни в ядрі не обов'язково потребують змін у зовнішніх шарах (див. Коментар SteveCallender). У цьому контексті легко зрозуміти, як саме об'єкти повинні представляти мету, для якої вони використовуються. Крім того, ці шари повинні говорити один з одним щодо об'єктів, створених спеціально для цього повідомлення. Це навіть може означати, що є 3 зображення, по 1 у кожному шарі, 1 для транспортування між шарами.

І є https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html, адреса якої вище:

Інші люди стурбовані тим, що чистим результатом моїх порад буде багато дублюваного коду та велика кількість копіювання даних із однієї структури даних в іншу по всіх шарах системи. Звичайно, я теж цього не хочу; і нічого, що я запропонував, неминуче призвело б до повторення структур даних і до непомірного копіювання на місцях.

ІМО означає, що просто копіювання об'єктів 1: 1 - це запах в архітектурі, оскільки ви насправді не використовуєте належні шари та / або абстракції.

Пізніше він пояснює, як уявляє всі "копіювання"

Ви відокремлюєте інтерфейс користувача від бізнес-правил, передаючи прості структури даних між ними. Ви не повідомляєте контролерам нічого про правила бізнесу. Натомість контролери розпаковують об'єкт HttpRequest в просту структуру даних ванілі, а потім передають цю структуру даних об'єкту-інтерактору, який реалізує випадок використання, викликаючи бізнес-об’єкти. Потім інтерактор збирає дані відповіді в іншу структуру даних ванілі і передає їх назад в інтерфейс користувача. Погляди не знають про бізнес-об’єкти. Вони просто заглядають у цю структуру даних і представляють відповідь.

У цій програмі існує велика різниця між представленнями. Дані, що надходять, не є лише сутностями. І це вимагає різних класів.

Однак застосовано до простого додатка Android, наприклад, для перегляду фотографій, у якого Photoорганізація має близько 0 бізнес-правил, а "випадок використання", який займається ними, майже не існує і насправді більше стурбований кешуванням та завантаженням (цей процес повинен бути IMO представлене більш явно), пункт робити окремі уявлення про фотографію починає зникати. Я навіть відчуваю, що саме фотографія є об'єктом передачі даних, тоді як реальний бізнес-логічний ядро-шар відсутній.

Існує відмінність "відокремити інтерфейс користувача від правил бізнесу, передавши прості структури даних між двома" і ", коли ви хочете відобразити фотографію, перейменуйте її 3 рази на шляху" .

Крім того, я бачу, що ці демонстраційні програми не вдається представити чисту архітектуру - це те, що вони додають величезного акценту на розділення шарів заради розділення шарів, але ефективно приховують те, що робить додаток. Це на відміну від сказаного в https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html, а саме -

архітектура програмного забезпечення кричить про випадки використання програми

Я не бачу такого акценту на розділенні шарів в чистій архітектурі. Йдеться про напрямок залежності та зосередження уваги на представленні основи програми - сутностей та випадків використання - в ідеальній простоті Java без залежностей від зовнішньої сторони. Справа не стільки в залежності від цього ядра.

Тож якщо у вашій програмі насправді є ядро, яке представляє ділові правила та випадки використання, та / або різні люди працюють на різних шарах, будь ласка, розділіть їх за призначенням. Якщо ви, з іншого боку, просто пишете просту програму, не перестарайтеся. 2 шари з плавними рамками можуть бути і більш ніж достатніми. І шари можна також додати пізніше.


1
@RamiJemli В ідеалі, об'єкти однакові у всіх програмах. Ось така різниця між "правилами для бізнесу на підприємстві" та "правилами бізнес-прикладних програм" (іноді логіка бізнесу проти додатків). Ядро - це дуже абстрактне представлення ваших сутностей, яке є загальним, щоб ви могли використовувати його скрізь. Уявіть собі банк, який має багато додатків, один для підтримки клієнтів, один працює на банкоматах, один як веб-інтерфейс для самих клієнтів. Усі вони можуть використовувати одне і те ж, BankAccountале з певними правилами програми, що ви можете зробити з цим обліковим записом.

4
Я думаю, що важливим моментом чистої архітектури є те, що за допомогою перехідного рівня інтерфейсу для перетворення (або як ви говорите на карту) між представництвом сутності різних шарів ви зменшуєте залежність до зазначеної сутності. Якщо будуть зміни в шарах Usecase або Entity (сподіваємось, малоймовірно, але в міру зміни вимог ці шари будуть), вплив змін міститься в шарі адаптера. Якщо ви вирішили використовувати одне і те ж представлення сутності у всій архітектурі, вплив цих змін був би набагато більшим.
SteveCallender

1
@RamiJemli добре використовувати рамки, які роблять життя простішим, справа в тому, що ви повинні бути обережними, коли ваша архітектура покладається на них, і ви починаєте ставити їх у центр всього. Ось навіть стаття про блог RxJava blog.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html - це не говорить про те, що не слід його використовувати. Це більше схоже: я бачив це, це стане різним через рік, і коли ваша заявка все ще буде, ви зациклюєтесь на цьому. Зробіть це деталлю і виконайте найважливіші речі у звичайній старій Java, застосовуючи звичайні старі принципи SOLID.
запл

1
@zapl Ви так само ставитесь до рівня веб-сервісу? Іншими словами, чи поклали б ви @SerializedNameанотації Gson на доменну модель? Або ви створили новий об’єкт, відповідальний за відображення веб-відповіді на доменну модель?
tir38

2
@ tir38 Поділ сам по собі не дає користі, це пов'язано з витратами на майбутні зміни. => Залежить від програми. 1) це коштує вам часу на створення та підтримку доданого етапу, який перетворюється між різними уявленнями. Наприклад, додавання поля до домену та забуття додати його десь ще не є нечуваним. Не може статися з простим підходом. 2) Перехід до більш складних налаштувань коштує пізніше, якщо виявиться, що вам це потрібно. Додавання шарів непросте, тому у великих додатках простіше виправдати більше шарів, які не потрібні негайно
zapl

7

Ви насправді правильно зрозуміли. І порушення DRY немає, оскільки ви приймаєте SRP.

Наприклад: у вас є бізнес-метод createX (ім'я рядка), тоді ви можете мати метод createX (ім'я рядка) в DAO-шарі, який називається в бізнес-методі. Вони можуть мати однаковий підпис і, можливо, є лише делегація, але вони мають різні цілі. Ви також можете мати createX (ім'я рядка) на UseCase. Навіть тоді це не зайве. Що я маю на увазі під цим: ті ж підписи не означають однакової семантики. Виберіть для цього інші назви, щоб семантика була чистою. Сама назва цього не впливає на SRP.

UseCase відповідає за логіку, що залежить від додатків, бізнес-об'єкт відповідає за логіку, незалежну від додатків, і DAO відповідає за зберігання.

Через різну семантику всі шари можуть мати власну модель представлення та комунікації. Часто ви бачите "сутності" як "бізнес-об'єкти", і часто не бачите необхідності їх відокремлення. Але у "величезних" проектах потрібно докласти зусиль для правильного розділення шарів. Чим більше проект збільшує можливість того, що вам потрібна різна семантика, представлена ​​в різних шарах і класах.

Можна думати про різні аспекти одного і того ж семантичного. На екрані повинен бути відображений User-Object, він має деякі внутрішні правила узгодженості, і його потрібно десь зберігати. Кожен аспект повинен бути представлений в іншому класі (SRP). Створення карток може бути болем у попці, тому в більшості проектів, над якими я працював, ці аспекти переплавляються в один клас. Це явно є порушенням СРП, але насправді ніхто не хвилює.

Я називаю застосування чистої архітектури та SOLID "не соціально прийнятним". Я би працював з цим, якби мені дозволено. На даний момент мені це не дозволено. Я чекаю моменту, коли ми подумаємо про те, щоб сприйняти SOLID серйозно.


Я думаю, що жоден метод на рівні даних не повинен мати такий самий підпис, як будь-який метод на рівні домену. У шарі Домен ви використовуєте пов'язані з бізнесом умови іменування, такі як SignUp або логін, а в рівні даних ви використовуєте save (якщо шаблон DAO) або додаєте (якщо Repository, оскільки ця модель використовує Collection як метафору). Нарешті, я не кажу про сутності (Дані) та модель (Домен), я підкреслюю непотрібність UserModel та його Mapper (презентаційний шар). Ви можете викликати клас користувача домену всередині презентації, і це не порушує правила залежності.
Рамі Джемлі

Я погоджуюся з Рамі, картограф не є потрібним, оскільки ви можете робити відображення безпосередньо в інтеракторній реалізації.
Крістофер Перрі

5

Ні, вам не потрібно створювати класи класів у кожному шарі.

Entity ( DATA_LAYER) - це повне або часткове представлення об'єкта Бази даних.DATA_LAYER

Mapper ( DOMAIN_LAYER) - насправді це клас, який перетворює Entity в ModelClass, на якому буде використовуватисяDOMAIN_LAYER

Погляньте: https://github.com/lifedemons/photoviewer


1
Звичайно, я не проти сутностей на рівні даних, але, у вашому прикладі, клас PhotoModel у шарі презентації має ті ж атрибути класу Photo у доменному шарі. Це технічно той самий клас. це потрібно?

Я думаю, що у вашому прикладі щось не вдається, оскільки доменний шар не повинен залежати від інших шарів, так як у вашому прикладі ваші картографи залежать від сутностей у вашому шарі даних, які IMO, це має бути навпаки
navid_gh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.