Как да пишете надеждни приложения всеки път, използвайки „Чистата архитектура“

Като разработчици, ние не можем да не използваме външни библиотеки и рамки в нашите системи. Ръцете на общността създават чудесни инструменти и използването им е само естествено. Всичко обаче има и недостатък.

Небрежните екипи и хора могат да изпаднат в опасна ситуация, като структурират своите системи около инструментите, които използват. Правилата на бизнеса се смесват с подробности за внедряването. Това може да доведе до крехка система, трудна за разширяване и поддържане. Това, което трябва да бъде бърза промяна в GUI, се превръща в лов на бъгове, който продължава с часове. Но не е нужно да е така.

Софтуерната архитектура предлага модели и правила за определяне на структурите (като класове, интерфейси и структури) в системата и как те се свързват помежду си. Тези правила насърчават повторното използване и разделянето на проблемите за тези елементи. Това улеснява промяната на детайли за внедряването, като например СУБД или библиотека от предния край. Рефакторите и корекциите на грешки засягат възможно най-малко части от системата. А добавянето на нови функции става полъх.

В тази статия ще обясня архитектурен модел, предложен през 2012 г. от Робърт К. Мартин, чичо Боб. Той е автор на класики като Clean Code и The Clean Coder. През октомври тази година той ще пусне още една книга „Чиста архитектура“.

Моделът има същото име като книгата и е изграден върху прости концепции:

Разделете състава на системата на слоеве с ясно изразени и добре определени роли. И ограничават връзките между субектите в различни слоеве. Няма нищо ново в разделянето на приложението ви на слоеве. Но избрах този подход, тъй като той беше най-простият за разбиране и изпълнение. И прави тестовите случаи на употреба мъртви прости.

Просто трябва да се уверим, че Интеракторите работят правилно и е добре да вървим. Не се притеснявайте, ако думата „Интерактори“ ви се стори чужда, ще научим за тях скоро.

Отвътре навън ще разгледаме всеки от слоевете малко по-нататък. Ще използваме примерно приложение, което ни е доста познато: броячи. Това не отнема време да разберем, така че можем да се съсредоточим върху темата на тази статия.

Можете да намерите демонстрация на приложението тук, а примерите с кодове ще бъдат в TypeScript. Някои от списъците с кодове по-долу използват React и Redux. Някои знания за тези решения могат да помогнат за разбирането им. И все пак концепциите на Clean Architecture са много по-универсални. Ще можете да го разберете дори без предварително познаване на споменатите инструменти.

Субекти

Предприятията са в диаграмата като Enterprise Business Rules. Предприятията включват бизнес правила, които са универсални за дадена компания. Те представляват образувания, които са основни за неговата област на дейност. Те са компонентите с най-високо ниво на абстракция.

В нашата извадка на броячите има много очевидна субект: самият брояч.

Случаи на употреба

Случаите за използване са посочени като Правила за приложение на бизнеса. Те представляват всеки от случаите на използване на едно приложение. Всеки елемент от този слой осигурява интерфейс към външния слой и действа като център, който комуникира с други части на системата. Те са отговорни за цялостното изпълнение на случаите на употреба и обикновено се наричат ​​Интерактори.

В нашата извадка имаме случай за използване за увеличаване или намаляване на брояча:

Обърнете внимание, че фабричната функция за ChangeCounterInteractor получава параметър от типа CounterGateway. Ще обсъдим съществуването на този тип по-късно в статията. Но можем да кажем, че Gateways са това, което стои между Use Cases и следващия слой.

Интерфейсни адаптери

Този слой се състои от границата между бизнес правилата на системата и инструментите, които й позволяват да взаимодейства с външния свят, като бази данни и графични интерфейси. Елементите в този слой действат като посредници, получават данни от един слой и го предават напред на другия, приспособявайки данните според нуждите.

В нашата извадка имаме няколко интерфейсни адаптера. Един от тях е компонентът React, който представя брояча и неговите контроли на увеличение и намаляване:

Обърнете внимание, че компонентът не използва екземпляр Counter, за да представи стойността си, а екземпляр от CounterViewData. Направихме тази промяна, за да отделим представянето на логиката от бизнес данните. Пример за това е логиката на излагане на брояча въз основа на режима на изглед (римски или хиндуарабски цифри). Следва реализация на CounterViewData по-долу:

Друг пример за интерфейсен адаптер е прилагането на Redux на нашето приложение. Модулите, отговорни за заявките към сървър и използването на локално съхранение, също ще живеят вътре в този слой.

Рамки и драйвери

Инструментите, които вашата система използва за комуникация с външния свят, съставят най-външния слой. Обикновено не пишем код в този слой, който включва библиотеки като React / Redux, API на браузъра и т.н.

Правилото за зависимостта

Това разделение на слоеве има две основни цели. Една от тях е да се изяснят отговорностите на всяка част от системата. Другото е да се уверите, че всеки от тях изпълнява своите роли възможно най-независимо една от друга. За да се случи това, има правило, което посочва как елементите трябва да зависят един от друг:

Елементът не трябва да зависи от всеки елемент, принадлежащ към слой извън неговия собствен.

Например, елемент в слоя Use Cases не може да има никакви познания за който и да е клас или модул, свързани с GUI или постоянство на данните. По същия начин, субектът не може да знае кои случаи на използване го използват.

Това правило може да породи въпроси в главата ви. Вземете примерно калъф за употреба. Той се задейства в резултат на взаимодействие на потребителя с потребителския интерфейс. Изпълнението му включва актуализацията в някои устойчиви хранилища на данни, като например база данни. Как Интеракторът може да извършва съответните обаждания към процедурите за актуализиране без в зависимост от интерфейсен адаптер, който е отговорен за постоянството на данните?

Отговорът се крие в елемент, който споменахме преди: Gateways. Те са отговорни за създаването на интерфейса, необходим на Случаите за използване, за да вършат работата си. След като установят този интерфейс, зависи от интерфейсните адаптери да изпълнят своята страна на договора, както е показано на диаграмата по-горе. Имаме интерфейс CounterGateway и конкретна реализация, използвайки Redux по-долу:

Може да не ви е нужен

Разбира се, това примерно приложение беше малко сложно за приложение за брояч на увеличение / намаляване. И бих искал да поясня, че не се нуждаете от всичко това за малък проект или прототип. Но повярвайте ми, тъй като приложението ви става все по-голямо, ще искате да увеличите възможностите за повторна употреба и поддръжка. Добрата софтуерна архитектура прави проектите устойчиви на времето.

Добре ... И какво?

С тази статия открихме подход за отделяне на съществата на нашите системи. Това ги прави по-лесни за поддържане и разширяване. Например, за да изградим едно и също приложение с помощта на Vue.js, ще трябва да пренапишем само CounterPage и CounterWidget компоненти. Изходният код на примерното приложение е на връзката по-долу:

Тази история беше преведена на португалски от мен! Той е достъпен тук.

Какви плюсове и минуси виждате в този подход? Използвали ли сте нещо подобно в производството? Споделете вашия опит в отговорите. Ако ви харесва статията, моля, ръкопляскайте за мен!