Как и обещал, начнём с наброса 😃
Что такое чистая архитектура, зачем нужна, плюсы, издержки.

Если вы работали c ytq, расскажите о своём опыте? Что было круто, что было неудобно? Будем разбираться, действительно ли это полезный инструмент, или просто переусложнённый хайп.
Начнём с того, что такое «Чистая архитектура».

Behold!
В общих чертах: весь код поделен на слои.

Центральный слой (домен) — ядро приложения, максимально независим и отвечает за то, чем приложение отличается от других.

Прикладной слой рулит сценариями, которые специфичны конкретно этому приложению.
Адаптеры и порты — это связь со внешним миром: БД, UI, вот это всё.

Если хочется узнать побольше, то вот пара ссылок:
- herbertograca.com/2017/11/16/exp…
- habr.com/ru/post/269589/
- bespoyasov.ru/blog/clean-arc…

Особенно советую — первую. Просто офигенная статья!
Когда я начал постить в блоге (о фронтенде) конспекты книг и статьи об архитектуре, мне стали прилетать вопросы типа «А кому и нафига это вообще надо?».
Я отчасти понимаю природу этих вопросов.

Кажется, что архитектура это что-то далёкое от фронтенда: мы же просто формочки шлёпаем да кнопочки двигаем.

А все эти Мартины и Физерсы как-то уж очень сильно переусложняют.
Вот нафига мне выделять «слои» в приложении, если _всё_ моё приложение — это небольшое PWA с парой кнопочек?

Есть аргументы вида «будет проще переехать с React на что-то ещё» — но я не собираюсь переезжать с React, зачем мне тогда адаптеры для него?
Да чтобы просто _нарисовать_ схему приложения по такой архитектуре у меня времени уйдёт больше, чем на то, чтобы написать его 😃

В чём профит?
Я предлагаю начать с того, что архитектура — это прежде всего инструмент.

У любого инструмента есть область применения и ограничения.
Я, пожалуй, не стану покупать шуруповёрт, чтобы вкрутить один саморез.

Но если саморезов 1000, то я уже подумаю: потратить 5 тысяч на шуруповёрт или лечить в будущем артрит кисти за бóльшие деньги 😃
Архитектура, как и шуруповёрт, стоит ресурсов.

Поддержка сложного проекта с лапше-кодом, как и артрит, — тоже стоит ресурсов, и тоже, как правило, больше.
Свой первый вывод я сделал для себя, когда сравнил маленький и простой проект с навороченной архитектурой и большой и сложный проект без какой-либо архитектуры вовсе.
Дело было так: я однажды попал в сложный проект на PHP с кучей легаси и запутанным кодом.

Ни о какой архитектуре там речи, разумеется, не шло. Ребята зафигачили стартап, он полетел, побежали фичи и баги, а потом пришёл я 😃
Тогда я только-только начинал знакомиться с хорошими практиками в разработке софта, книжки там читать начал, всё такое.

Но уже тогда было понятно, что ясного понимания, как работает система — нет, причём ни у кого 😃
Работать было невозможно, потому что добавишь чё-нибудь-куда-нибудь, где-нибудь-что-то-ещё отвалится.

— Так написали бы тестов, чё.



Ага, мы тоже так подумали 🙂
Не писались там тесты, как бы мы ни старались 😃
Код был написан так, что чтобы протестировать какой-то модуль, приходилось мокать вообще всё подряд.

(Говоря умными словами, код был сильно зацеплен: ru.wikipedia.org/wiki/Зацеплени…)
Поддерживать этот комбайн было трудно. И не только потому, что тестировать было неудобно — было трудно даже понять, что и какой модуль должен делать 😃

Сейчас я бы сказал, что _код доменного слоя_ был размазан ровным слоем (будум-тсс) по всему приложению.
Всё было намешано в кучу.

Держать в голове даже один модуль было трудно: модуль мог и за шаблонизацию отвечать, и за преобразование данных.

(Умными словами, нарушал SRP: ota-solid.vercel.app/srp)
Как и куда направлены зависимости тоже ясно не было. (Циклические зависимости себя не заставили долго ждать 😅)
Теперь контр-пример: прототип приложения на React.

Надо быстро, поддерживать будет, скорее всего, не нужно. А если и нужно — то всё равно переписывать, потому что дизайн будет другой, UX поменяется и т. д.
Страдая от, кхм, ПТСР с прошлого опыта, я накрутил туда архитектуры по всем правилам: вот тебе и домен, вот тебе прикладной слой, адаптеры, всё независимо, найс.

Только прототип никому не понадобился, а проект затух 😃
Вместо того, чтобы проверить гипотезу, приложив минимум усилий, я вбухал кучу ресурсов.

Ладно хоть писал сам, а то стыдно бы потом было смотреть в глаза команде! 😃
И вот мой первый тогдашний вывод:
== Издержки должны быть меньше выгоды ==

Да, вот так очевидно 😃
После того проекта я решил порефлексировать на него.

Что бы произошло, если бы всё-таки прототип пришлось переписать.

- Сколько кода я бы _мог_ переиспользовать?
- Какой код _надо_ было бы переиспользовать?
*Сейчас пойду поработаю, а после расскажу:
- какие выводы получилось сделать после этого,
- как я использую ЧА сейчас,
- какое минимальное количество усилий стоит прикладывать,
- как понять, что пора расширять инструментарий.
Обед! Продолжим 😃

Итак, что бы произошло, если бы всё-таки прототип пришлось переписать.

- Сколько кода я бы _мог_ переиспользовать?
- Какой код _надо_ было бы переиспользовать?
Кто-то уже мог догадаться, что я клоню к домену.

Домен — это самое главное, что есть в приложении. Та функциональность, которая отличает идею одного приложения от другого.
То, что мне точно пришлось бы перенести из прототипа в продукт — именно домен.

Да, вероятно, с изменениями, возможно, что-то пришлось бы добавить. Но именно этот код _пришлось_ бы переносить.
Второй вывод:
=== Стоит начать с домена ===

Сперва можно и не городить оставшиеся слои, не писать адаптеры к библиотекам, всего этого можно на первом этапе не делать.

Но выделить домен — стоит обязательно.
Я это называю, кхм, «прагматичной архитектурой» 😃

Это как правило 20/80, только про дизайн систем.
ru.wikipedia.org/wiki/Закон_Пар…
Без выделенного домена очень сложно вообще понять, что происходит.

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

Ядро системы было бы проще для понимания.
— Ок, допустим. Но вот я читаю книжки об архитектуре, там сплошное ООП. А я не хочу в свой проект его тащить.

Понимаю. Могу обрадовать: архитектура и ООП — вещи ортогональные 😃
Ну то есть понятно, что большая часть книг написана с примерами на ОО-языках, но это не значит, что нам нельзя взять идею и использовать только её.

(Почему с ООП проще строить грамотную архитектуру мы поговорим завтра.)
Домен можно вообще писать как хочется. Главное, чтобы код был понятным и независимым.

Я, если пишу не в ОО-стиле, то люблю описывать домен в виде типов и чистых функций, которые оперируют данными этих типов:

github.com/bespoyasov/www…
Профит в том, что если проект выстрелит и начнёт быстро расти, вам будет проще накрутить мяса вокруг самого важного кода, чем искать этот самый важный код по всей кодовой базе.
Чем проще и прямолинейнее домен, тем очевиднее, что в системе можно вытворять, а что нет.

А чем очевиднее правила, тем легче выстраивать вокруг них потоки данных и использовать дополнительные инструменты.
— Ладно, это всё, конечно, круто, но ты кажется забыл, что мы тут вс же на JS пишем. Какие нафиг типы? 😃

Отсутствие типов тоже не проблема для выделения домена 🙂
Ну то есть да, статичная типизация помогает проектировать, но и без неё можно справиться.

Ну там JSDoc, объекты-стабы для тестов, те же классы в конце концов.

(Хотя признаю, я начал по-настоящему задумываться о проектировании, когда перелез на TypeScript.)
(Без интерфейсов сложно сконцентрироваться на взаимодействии между сущностями.

Труднее выделять публичное API, абстрагироваться от реализаций. У меня есть ощущение, что JS меня как бы подталкивает думать сперва о реализации, а TS — наоборот.)
Я для прототипов тесты, например, не пишу.

Но как только становится понятно, что из прототипа надо делать продукт, гораздо проще покрыть тестами _уже выделенный код_.
В целом считаю, что выделенный домен — это то самое минимальное необходимое количество ресурсов, которое стоит выделить на архитектуру в самом начале проекта.

Всё остальное, мне кажется, стоит добавлять по мере роста сложности.
— Окей, ладно. С доменом разобрались, допустим. Но вот зачем остальные слои? Они нужны?

Короткий ответ: не всегда. Длинный ответ ↓ 😃

Когда я думал, что «используя слой адаптеров, проще съехать с React», я отвечал себе, что я и не собирался съезжать с React.
И это правда, перебраться с него на какой-то другой шаблонизатор сложно. У него богатая экосистема, куча уже написанных компонентов.

Но что, если я заменю “React” на “Redux” 🙂
Кто-то наверняка задумывался о том, чтобы сменить стейт-менеджер.

Кто-то, наверное, даже успешно его менял на какой-нибудь MobX или что-нибудь ещё.

Так вот, заменить стейт-менеджер обычно — затратное мероприятие.
Он обычно затрагивает много кода: хранилище, события всякие, привязка к UI.

Вместе со всем этим кодом надо и тесты переписывать — а это ещё раза в два больше работы.
С адаптером для стейт-менеджера переезд попроще 🙂

Слой адаптеров — это барьер, который говорит, где заканчивается сторонний код и начинается наш.
Адаптеры и порты делят внешний мир от нашего приложения как мембрана клетки отделяет её от окружающей среды.

И все изменения окружающей среды влияют только на мембрану: появилось что-то, что можно съесть — съели, остальное отсеиваем.
Адаптеры как бы ограничивают распространение изменений. Мы пишем такие «переходники», которые делают внешний мир более удобным для нашего приложения.
Из-за этого и API _приложения_ меняется редко. Адаптеры же можно написать (в идеале) для любой сущности, с которой приложение хочет взаимодействовать.
Это, кстати, ещё и ограничивает распространение ошибок 🙂

Об этом писал Ганмер в «Паттернах отказоустойчивых приложений»:
bespoyasov.ru/blog/patterns-…

(Офигенная книжка, очень советую.)
Кроме ошибок, это ещё и помогает рефакторить код.

Разделение по слоям — идеальный «шов», как называет его Физерс в «Эффективной работе с легаси»:

bespoyasov.ru/blog/working-e…

(Тоже советую 😃)
Итак, к этому моменту:

- Архитектура — это инструмент. У неё есть издержки и выгоды.
- В какой мере инструмент использовать — определяет разница между издержками и выгодами.
- Не знаете, с чего начать — начните с домена.
- Старайтесь привязывать 3-party код адаптерами...
...Но если это очень дорого и бессмысленно (проект точно не доживёт до момента, когда мы захотим поменять React на что-то ещё) просто держите это в уме (а лучше в документации).
Хорошо, вот мы поняли, что нашему проекту на Реакте _нужна_ суровая масштабируемость, и одним выделением доменного слоя мы не обойдёмся. Что делать?
1. Писать код ещё рано. Проектиурем!
2. Прорабатываем взаимодействие модулей:
3. Пишем код домена.

Начать лучше именно с него, потому что всё приложение мы будет строить под его нужды.

Можно писать как угодно: если достаточно типов пары сущностей и одной-двух функций — замечательно. Это будет доменом.

При необходимости дополняем.
4. Прикладной слой и порты с адаптерами.

Я, кстати, видел даже неплохие стартовые шаблоны для того же Реакта:
- github.com/eduardomoroni/…
- github.com/bailabs/react_…
5. Держим при написании в голове разницу между выгодами и издержками.

Если вы чувствуете, что оверижинирите, опишите в документации описание и не плодите лишний код.

Если вы чувствуете, что вот это место, как слишком сильно сцеплено с 3-party кодом — добавляйте адаптеры.
Архитектура — это не только возможность масштабироваться, но ещё и увеличенный порог входа. Возможно, будет нужен онбординг.

(Я, например, когда маленьким был, долго не мог вдуплить, что такое DI и как он работает.)
7. Стараемся не плодить абстракций сверх меры.

Слои нам нужны, чтобы не смешивать зоны ответственности модулей.
Плохо умею в треды, продолжение вот тут, простите 😅

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with jsunderhood

jsunderhood Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @jsunderhood

27 Apr
Доброе утро! Сегодня вторник, а значит поговорим об ООП на фронте.

Пока я заливаю в себя кофе, давайте проведём опрос. Как вы думаете, ООП и фронтенд:
А пока идёт голосование, обсудим, чем ООП плохо и хорошо, а что его не любят и наоборот.

Начнём с хейта 😃
Сразу начну с того, что не каждому проекту ООП нужно.

Иногда гораздо проще написать пару функций с объектами, и никаких солидов не надо. Об этом подробнее в конце 🙂
Read 52 tweets
26 Apr
Теперь немного ссылок на Гитхабы!

Есть несколько шаблончиков для Реакта:
- github.com/eduardomoroni/…
- github.com/bailabs/react_…
Есть и без Реакта!
Вот я писал недавно пост об архитектуре, есть репозиторий для с исходниками:
- github.com/bespoyasov/tre…

А есть и с Реактом, и с Next!
Вот сайт недавно переписывал:
- github.com/bespoyasov/www
Структура файлов в двух последних репозиториях не отражает слои, но из поведение — вполне.

Кстати, репозиторий с сайтом ещё и хорошо показывает, как и когда можно остановить глубину проработки.
Read 5 tweets
26 Apr
У меня есть смутное подозрение, что это именно то, что в статье “How I put it all together” называют компонентом.

(Такой кусок гексагонального пирога.)

herbertograca.com/2017/11/16/exp… Image
То есть там конечно есть особенности, и оно не «точь в точь такое же», но кажется, будто бы идея где-то очень близко.
Кстати!

Структура папок никак не влияет на архитектуру и не отражает её 😃

То есть мы можем поделить приложение на слайсы/компоненты, которые будут содержать в себе код фичи под каждый случай.

Но при этом деплоить, например, по слоям. Вообще без проблем.
Read 4 tweets
26 Apr
Теперь немного о собственно проектировании.

Допустим, мы знаем, что нашему проекту _нужна_ суровая масштабируемость. Что делать?

Первым делом стоит взять ручку, бумажку и пойти «программировать ногами» 😃
Мы (люди) плохо умеем прогнозировать будущее. Проектирование систем — это прогнозирование будущего.

Чем больше исходных данных мы насобираем, тем больше вероятность, что мы правильно составим «карту территории».
(О соотношении карты и территории: ru.wikipedia.org/wiki/Соотношен…)
Read 22 tweets
26 Apr
Да! ^_^

Недавно меня уже спрашивали в Твитере, что-куда-и-как можно вынести.

Я ответил на примере приложения с котиками 😼
Сейчас в дополнение к котикам сделаем онлайн-магазин печенек! 🍪
Допустим, наш магазин продаёт разные виды печенек. У каждой печеньки есть цена, состав и срок изготовления.

Пользователи могут покупать печеньки на сайте, указывая предпочтения по вкусам, аллергиям, наличию молока и проч.
Read 11 tweets
26 Apr
На эту неделю хочу предложить вам вот такой план:

В понедельник поговорим об архитектуре. День начнём с наброса — а нужна ли вообще «Чистая архитектура» 😃

Обсудим, что это такое, какие плюсы она даёт, какие у неё издержки, применимо ли это всё ко фронтенду.
Во вторник набросим ещё больше и обсудим ООП и современный фронт.

Кто они: «хорошие друзья» или «заклятые враги»? Мертво ли ООП, устарело ли морально, почему его хейтят.
В среду поговорим о чистоте кода и обсудим, как сделать его читаемым и тестируемым.

Начнём с нейминга, закончим принципами и эвристиками и заумных книжек 🙂
Read 8 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!