Чистая архитектура в next.js
Это репо является примером того, как добиться чистой архитектуры в next.js. Есть видеоурок, который проходит этот проект. Нажмите на изображение, чтобы проверить его на YouTube:
Вы можете запустить проект, просто запустив npm install и npm run dev .
Чистая архитектура

Примечание
? Я нарисовал эту упрощенную версию оригинальной диаграммы чистой архитектуры. Я упростил это таким образом, чтобы это было больше для меня, и это легче понять. Я надеюсь, что это вам тоже поможет.
Я настоятельно рекомендую вам прочитать оригинальную статью дяди Боба, если вы впервые слышат о чистой архитектуре, но я постараюсь подвести итог ее для вас ниже.
Чистая архитектура - это набор правил , которые помогают нам структурировать наши приложения таким образом, что их легче поддерживать и тестировать, а их кодовые базы предсказуемы. Это как общий язык, который понимают разработчики, независимо от их технического опыта и предпочтений языка программирования.
Чистая архитектура и аналогичные/полученные архитектуры, все имеют одинаковую цель - разделение проблем . Они вводят слои , которые объединяют аналогичный код вместе. «Слои» помогает нам достичь важных аспектов в нашей кодовой базе:
- Независимо от пользовательского интерфейса - бизнес -логика не сочетается с использованием структуры пользовательского интерфейса (в данном случае Next.js). Та же самая система может использоваться в приложении CLI, без необходимости изменить бизнес -логику или правила.
- Независимо от базы данных - реализация/операции базы данных изолирована в их собственном уровне, поэтому остальная часть приложения не заботится о том, какая база данных используется, но общается с использованием моделей .
- Независимо от фреймворков - бизнес -правила и логика просто ничего не знают о внешнем мире. Они получают данные, определенные с помощью простого JavaScript, используют простые JavaScript, услуги и репозитории для определения собственной логики и функциональности. Это позволяет нам использовать рамки в качестве инструментов, вместо того, чтобы «формировать» нашу систему в их реализации и ограничения. Если мы используем обработчики маршрута в нашем приложении и хотим реорганизовать некоторые из них на действия сервера, все, что нам нужно сделать, это просто вызвать конкретные контроллеры в действии сервера вместо обработчика маршрута, но основная бизнес -логика остается неизменной.
- Testable - бизнес -логика и правила могут быть легко проверены, поскольку она не зависит от структуры пользовательского интерфейса, или базы данных, или веб -сервера, или любого другого внешнего элемента, который создает нашу систему.
Чистая архитектура достигает этого благодаря определению иерархии зависимости - слои зависят только от слоев под ними , но не выше.
Структура проекта (только важные биты)
-
app - Слои Frameworks & Drivers - в основном все Next.js (страницы, действия сервера, компоненты, стили и т. Д.) Или что -то еще «потребляет» логику приложения -
di - Инъекция зависимостей - папка, где мы настраиваем контейнер DI и модули -
drizzle - все DB - Инициализация клиента DB, определение схемы, миграции -
src - «корень» системы-
application - Уровень приложения - удерживает сценарии и интерфейсы для репозиториев и услуг -
entities - сущности слой - удерживает модели и пользовательские ошибки -
infrastructure - уровень инфраструктуры - содержит реализацию репозитории и услуг и выводит интерфейсы из application -
interface-adapters - Адаптеры интерфейса. Слой - удерживает контроллеры, которые служат точкой входа в систему (используется в слое Frameworks & Drivers для взаимодействия с системой)
-
tests - здесь живут модульные тесты - структура подпапки unit соответствует src -
.eslintrc.json - где определяется плагин eslint-plugin-boundaries - это мешает вам нарушать правило зависимости -
vitest.config.ts - обратите внимание на то, как определяется псевдоним @
Слои объяснение
- Frameworks & Drivers : сохраняет все функциональность Framework и все остальное, что взаимодействует с системой (Ex. AWS Lambdas, Stripe Webhooks и т. Д.). В этом сценарии это следующее.
- Этот слой должен использовать только контроллеры , модели и ошибки и не должен использовать варианты использования , репозитории и услуги .
- Адаптеры интерфейса : определяет контроллеры :
- Контроллеры выполняют проверки подлинности и проверку ввода перед передачей ввода в конкретные варианты использования.
- Контроллеры организуют варианты использования. Они не реализуют никакой логики, но определяют все операции, используя варианты использования.
- Ошибки из более глубоких слоев пузырится и обрабатываются там, где используются контроллеры.
- Контроллеры используют докладчиков для преобразования данных в удобный пользовательский формат непосредственно перед тем, как вернуть их «потребителю». Это помогает нам отправить меньше JavaScript в клиенту (логика и библиотеки для преобразования данных), помогает предотвратить утечку любых конфиденциальных свойств, таких как электронные письма или хэшированные пароли, а также помогает нам уменьшить количество данных, которые мы отправляем обратно клиенту.
- Приложение : где живет бизнес -логика. Иногда называют ядром . Этот слой определяет варианты использования и интерфейсы для служб и репозитории.
- Примеры использования :
- Представляйте индивидуальные операции, такие как «Создать тодо» или «Войти» или «Перевернуть Тодо».
- Примите предварительно проверенный вход (от контроллеров) и обработайте проверки авторизации .
- Используйте репозитории и услуги для доступа к источникам данных и общения с внешними системами.
- Варианты использования не должны использовать другие варианты использования . Это запах кода. Это означает, что вариант использования делает несколько вещей и должен быть разбит на множественные варианты использования.
- Интерфейсы для репозитории и услуг:
- Они определены на этом уровне, потому что мы хотим разбить зависимость их инструментов и структур (драйверы базы данных, услуги по электронной почте и т. Д.), Поэтому мы реализуем их на уровне инфраструктуры .
- Поскольку интерфейсы живут в этом слое, варианты использования (и транзитивно верхние слои) могут получить доступ к ним посредством инъекции зависимостей .
- Инъекция зависимости позволяет нам отделить определения (интерфейсы) от реализаций (классов) и держать их в отдельном уровне (инфраструктура), но все же позволяет использовать их использование.
- Сущности : где модели и ошибки определены.
- Модели :
- Определите формы данных «домена» с помощью простого JavaScript, без использования технологий «базы данных».
- Модели не всегда привязаны к базе данных - отправка электронных писем требует внешней почтовой службы, а не базы данных, но нам все еще нужно иметь форму данных, которая поможет другим уровням сообщать «отправка электронной почты».
- Модели также определяют свои собственные правила валидации, которые называются «предпринимаемыми правилами бизнеса». Правила, которые обычно не изменяются или с меньшей вероятностью изменяются, когда что -то внешнее изменяется (навигация страницы, безопасность и т. Д.). Примером является
User модель, которая определяет поле имени пользователя, которое должно составлять не менее 6 символов и не включать специальные символы .
- Ошибки :
- Мы хотим свои собственные ошибки, потому что мы не хотим, чтобы ошибки базы данных, специфичные для базы данных, или любые ошибки, характерные для библиотеки или структуры.
- Мы
catch ошибки, которые поступают из других библиотек (например, дождь), и конвертируем эти ошибки в наши собственные ошибки. - Вот как мы можем сохранить наше ядро независимы от любых структур, библиотек и технологий - одного из наиболее важных аспектов чистой архитектуры.
- Инфраструктура : где определяются репозитории и услуги .
- Этот слой притягивает интерфейсы репозиториев и услуг с уровня приложения и реализует их в их собственных классах.
- Репозитории - это то, как мы реализуем операции базы данных. Это классы, которые разоблачают методы, которые выполняют одну операцию базы данных - например,
getTodo , createTodo , или updateTodo . Это означает, что мы используем библиотеку / драйвер базы данных только в этих классах. Они не выполняют какую -либо проверку данных, просто выполняют запросы и мутации в отношении базы данных и либо бросают наши пользовательские ошибки , либо возвращают результаты. - Услуги - это общие услуги, которые используются по всему приложению - например, служба аутентификации или услуга электронной почты, или реализуют внешние системы, такие как Stripe (создание платежей, проверки квитанций и т. Д.). Эти службы также используют и зависят от других структур и библиотек. Вот почему их реализация хранится здесь вместе с репозиториями.
- Поскольку мы не хотим, чтобы какой -либо уровень зависел от этого (и транзисивно зависел от базы данных и всех сервисов), мы используем принцип инверсии зависимости . Это позволяет нам зависеть только от интерфейсов, определенных в приложении , вместо реализаций на уровне инфраструктуры . Мы используем инверсию библиотеки управления, как ioctopus, для абстрагирования реализации интерфейсов и «инъекции» ее всякий раз, когда нам это нужно. Мы создаем абстракцию в каталоге
di . Мы «связываем» репозитории, услуги, контроллеры и варианты использования символов, и мы «разрешаем» их, используя эти символы, когда нам нужна фактическая реализация. Вот как мы можем использовать реализацию, без необходимости явно зависеть от нее (импортируйте).
Часто задаваемые вопросы
Кончик
Если у вас есть вопрос, не покрытый FAQ, не стесняйтесь открывать проблему в этом репо, либо присоединиться к моему серверу Discord и запустите разговор там.
Чистая архитектура / эта реализация без сервера, удобна для сервера? Могу ли я развернуть это в Vercel?
Да! Вы можете использовать его с маршрутизатором Page, маршрутизатором приложений, промежуточным программным обеспечением, обработчиками API, серверными действиями, чем угодно! Обычно достижение впрыскивания зависимости в проектах JavaScript осуществляется с помощью библиотеки Inversify.js, которая несовместима с другими временем выполнения, кроме узла. Этот проект реализует Ioctopus, простой контейнер IOC, который не полагается на reflect-metadata и работает во всех времени запуска.
Должен ли я начать внедрять чистую архитектуру немедленно, когда я создаю свой проект следующего.js?
Я бы сказал нет . Если вы начинаете совершенно новый проект, я бы посоветовал вам сосредоточиться на достижении статуса MVP как можно быстрее (чтобы вы могли проверить свою идею / посмотреть, есть ли будущее для вашего проекта). Когда все начинает становиться серьезными (больше функций начинают реализовывать, ваша пользовательская база переживает значительный рост, или вы собираете других разработчиков в своем проекте), то когда вы захотите инвестировать некоторое время в адаптацию этой архитектуры (или любую архитектуру в этом отношении).
Если вы уже глубоко в сорняках в проекте, вы (и ваша команда) можете планировать постепенное рефакторинг, начиная с следующего спринта. В этом случае у вас уже есть написанный код, вам просто нужно немного реорганизовать его, и вы можете сделать эту часть по частям, обработчик маршрута по обработчику маршрута, действие сервера по действию сервера. Кстати, я говорю это слегка: «Вам просто нужно немного реорганизовать это» , но это может быть далеко не так просто. Всегда принимайте во внимание «дела идут не так», когда вы планируете рефакторинг. И поставьте некоторое время на написание тестов!
Это выглядит как переоборудование и усложняет разработку функций.
Если вы не тратите более 3 минут, думая об этом, то да, это похоже на перегрузку. Но если вы это сделаете, вы поймете эту архитектуру = дисциплина . Архитектура - это контракт между разработчиками, который определяет, что идет куда. Это на самом деле упрощает разработку функций, потому что это делает кодовую базу предсказуемой, и принимает эти решения для вас.
Вы не можете устойчиво развивать проект, если каждый разработчик, работающий над ИТ, пишет код, где он самый удобный. Кодовая база превратится в кошмар для работы, и именно тогда вы почувствуете очень сложный процесс разработки функций. Чтобы сразиться с этим, в конечном итоге вы подавите несколько правил. Эти правила будут расти, когда ваша команда сталкивается и решает новые проблемы. Поместите все эти правила в документ, и есть ваше собственное определение архитектуры. Вы все еще реализуете какую -то архитектуру, вы только что достигли этой точки очень медленно и мучительно.
Чистая архитектура дает вам ярлык и предопределенную архитектуру, которая была протестирована. И да, конечно, вам нужно изучить все это, но вы делаете это один раз в своей жизни, а затем просто примените принципы на любом языке или структуре, которые вы будете использовать в будущем.
Должен ли я применить чистую архитектуру во всех моих проектах?
Нет . Нет, если вы не ожидаете, что проект будет расти, как по количеству функций, так и по количеству пользователей, так и в количестве разработчиков, работающих над ним.
Каковы другие подобные архитектуры для чистой архитектуры?
Как упоминалось в оригинальном сообщении в блоге, которое я упоминал в верхней части Readme, вы получили:
- Гексагональная архитектура (он же порты и адаптеры) от Alistair Cockburn
- Архитектура лука Джеффри Палермо
- Кричащая архитектура дяди Боба (тот же парень за чистой архитектурой)
- И еще пара (проверьте оригинальный пост в блоге)