Clean Architecture: Як побудувати ідеальну архітектуру для проєктів на Node.js та TypeScript
Вставка
- Опубліковано 10 лют 2025
- Хочеш створювати масштабовані та підтримувані застосунки на Node.js та TypeScript?
У цьому відео розбираємо Clean Architecture: принципи, рівні, залежності та кращі практики на прикладі Node.js + TypeScript.
Дізнайся, як правильно організувати код, використовувати DDD, Repository Pattern, Dependency Injection та зробити свій застосунок гнучким і зрозумілим.
Repository with code: github.com/ser...
🔹 Що ти дізнаєшся у відео?
✅ Основи Чистої Архітектури
✅ Як правильно розділяти рівні: Domain, Application, Infrastructure, Presentation
✅ Патерни Repository, Use Case, Entity, DTO
✅ Як зменшити залежності та зробити код тестованим
✅ Реальний приклад проєкту на Node.js і TypeScript
📌 Підписуйся, якщо хочеш більше якісного контенту про архітектуру та розробку! 🚀
Дуже класна робота, дякую!
Дякую. Дякую що Українською. Чудовий курс.
Мені подобається, як ти подаєш матеріал і наскільки ретельно підготувався до цього відео - це справді велика робота!
Дуже дякую за ваші старання
Дякую, дуже корисно нагадати собі такі речі
Дякую. Дуже цікаво і приємно було послухати. Гарно пояснюєте
Дуже корисно, дякую
🙌
Дякую, все дуже добре і зрозуміло, особливо з готовим кодом на гітхабі.
Неочікувано було зустріти інтерфейс до репозиторія в папці domain/repositories, чи не краще його розташувати власне в infrastructure?
З тем які цікаво було б послухати
- application VS domain logic на реальних прикладах
- event bus для послаблення залежностей між модулями та як не перетворити цю взаємодію на dumpster fire
- сенс розбиття сервісу на окремі use case
- про транзакції в БД вже вище питали
>> чи не краще його розташувати власне в infrastructure?
Якщо ми це зробимо, ми порушимо DIP, бо тоді домен буде залежати від infrastructure (у випадку автора - не домен, а application, але тільки тому, що у нього service лежить не там де треба)
@@G0rynych Чому, все там буде нормально, у автора сервіс залежить не від реалізації, а від інтерфейсу репозиторія, як DIP власне і передбачає.
import type ... from "infrastructure/repositories" - це не залежність від infrastructure
Ну або винести всі типи в types. ts або окрему директорію types, щоб не було плутанини
@ Так, формально у нього DIP не порушений, я про не кажу. А порушена Clean Architecture, про яку він і розповідає. Бо коли він з таким підходом буде шось писати далі, то в результаті бізнес-логіка буде розмана тонкім шаром по всьому проєкту.
Сам зараз замаюсь цією темою-було б цікаво подивитись реалізацію:
1 взаємодії між модулями
2 реалізацію db-transactions особливо в цій дебільній прізмі(вибачте це особисте 😂) коли треба наприклад з двох різних модулів зберегти моделі але консистентно, тобто в рамках однієї транзакції
Подивіться в сторону патерну "Unit Of Work", де привʼязуючись до концепції DDD на інфраструктурному рівні реалізуємо логіку роботи з різними репозитаріями(використовуються методи-фабрики для створення обʼєктів репок) та транзаціями. Таким чином, не випускаючи деталі PRISMA в доменний рівень, бо тут є величезна спокуса втулити цю логіку в useCase або навіть controller.
Розширю свій приклад, цією імплементацією згодом.
@@patern_donchenkoдля того, щоб декілька послідовних запитів до бази даних зробити, треба використовувати Query builders з prepared statements, потім вже в певному репозиторії огортати ці об'єднані запити в транзакцію і тільки потім виконувати. Тобто має бути ще розділення коду по формуванню SQL запиту, від колу його виконання!
А щоб запити були ще чистіше, робити треба stored functions в самій Postgres DB. По факту треба буде в застосунку лише прописати Input/Output параметри без деталей самого SQL.
А якщо ми пишемо проект на несті, то ж виходить що наш application залежний від фреймворку, бо там декоратори всякі використовуємо і тд???
Дивіться, мова йде про ізоляцію бізне-логіки від впливу "третіх"-бібліотек, що використовуються в проектові. Декоратори - це мовна конструкція, яка використовуєтьcя широко Nest для своєї роботи. Зрозуміло, що 100 % ізоляція - просто не можлива, але тримати це на контролі потрібно.
Щось постійно читаю шароварна архітектура :)
О, вже краще )). В цьому відео вже виправлені моменти стосовно залежностей між шарами, але всеж-таки, нажаль, з самою архітектурою недорозібралися (.
Що не так:
1) Знову звідкись з'являється історія про data transfer structures між шарами, тобто там, де немає ніякого data transfer. Поясніть мені - нашо, от нашо вони вам там? І ні - вони нічого не захищають, і нема там ніякої оптимізації, і нема ніякої різниці між DTO/SDTO =)
2) В коді - ProductItemDTO знаходиться на рівні application, а має бути в presentation/controllers
3) Проблема з розподіленням реалізації - ProductService, який має бути в домені, а на рівні application має бути... якійсь Application. Зараз це не дуже очевидно, бо в прикладі є там два запити та немає жодної команди. А коли ви почнете їх додавати - стикнетесь з проблемкою.
Одразу підписка
Domain logic це набір окремих модулів/бібліотек, які живуть у своїх репозиторіях і своїх неймспейсах.
Не повинно воно бути у одному репозиторії з application. Ще і замішано у одну структуру.
Дякую Вам за відповідь, але тут питання до того, що ви написали.
1. "Domain logic" - цей термін використовується, як "stand-alone" чи в привʼязці до певної концепції типу "DDD"?
2. "Не повинно воно бути у одному репозиторії з application. Ще і замішано у одну структуру. " . Не зрозумів? А хто казав, що доменна логіка повинна мати один репозиторій, та ще й мішатися в одній структурі?
1. Цей термін використовується у тому вигляді, у якому він реалізовується на практиці.
2. Це так виглядає виходячи зі структури вашого репозиторія. Що в одному репозиторії з domain роблять папки application та presentation, незрозуміло. Чи давайте по іншому - як у вашій структурі ви створюєте декілька додатків з однією кодовою базою бізнес логіки?
@@D9ID9I о, тепер все чітко і зрозуміло, мова йде про код з репки, що. япошарив.
Код наведений в прикладі - має модульну структуру, де кожен модуль (це абстрактне поняття, не має відношення до модульної системи Node.js) - обʼєднує в собі описану структуру. Доменна логіка - живе в розділі "domain" для кожного модуля. Аналогічно, кожен модуль має розділ "presentation" - де згрупована логіка, що має відношення до презентаційного рівня і так далі.
Ця структура, ні в якому разі не суперечить жодному зі згаданих принципів.
Аналогічну структуру (можливо і не дословно) має Nest та інші рішення.
Сподіваюся, що відповів на Ваше запитання?
Бля какой ты царь
Благодаря тебе я заработал 150 тыс спасибо ❤
відділити бізнес логіку від бази даних то мабуть правильно, але все ж таки від база даних залежить дуже багато, як ми дані в базі структурно розмістим, які у нас будуть звʼязки, які поля будуть проіндексовані від цього залежить наскільки швидко буде працювати наш застосунок, і наскільки легко нам буде добавити новий функціонал. Переїзжають на іншу базу не так вже й часто, та і структуру даних на працюючому проєкті поміняти не легко...
Абсолютно вірно Ви зауважили, щодо переїзду на нову БД… так і є.
Не дарма існують дата-центричні архітектури, де робота з даними виноситься на перший план. І в цьому є свій сенс.
DDD - це як раз про те щоб бізнес ідею поставити в центр, а як дані зберігаються та вибираються - це деталь.
Що в першому кейсі, що в другому, питання оптимізації вирішуються приблизно однаково. За рахунок індексів, шардінгу, кешування та оптимізації запитів.
@ так, головне розуміти для чого ми приймаємо ту чи іншу архітектуру, архітектура заради архітектури вона майбутнього теж не потрібна. Люба архітектура вона не безкоштовна і забирає час сили і як наслідок гроші, і коли ми говоримо, що не дотримуючись якихось принципів ми можемо вистрілити собі в ногу, хотілось би знати яким чином це можна зробити.
@@blazheiko777 Я Вас зрозумів.
Приведу простий приклад з життя одного проекту у великій компаній.
Архітектурний стек, був запропонований клієнтом з величезним акцентом на сервіси AWS. Іншими словами, ДевоПси - закривали лаги архітектури за рахунок ресурсів... (компанія або менеджерський склад - могли собі це дозволити). База, не аналізуючи вимоги бізнес-відділу, була визначена як DynamoDB (one big table). Все рухалося по моделі "write-as-we-go" і приїхали до того моменту, що бізнас-вимоги вже не могли бути адекватно вкладені в проект. З одного боку плоска структура БД цього не давала зробити, з іншого - архітектура проекту, яка не мала ні персістент, ні доменного шару, як таких. Я отримав цей проект на такому етапі.
Ми переїхали за 4 спрінта на реляційну БД, мігрували дані, побудували відповідні слої та налаштували потоки даних. А головне відділили доменний слой з усіма абстрактними бізнес-моделями, що значно покращило, в кінцевому рахунку моделювання та розширення системи.
Ціна такої стрільби "по ногах" була висока. Це і кошти, і людський ресурс, який втратився на шляху до дати релізу.
@@patern_donchenko дякую за відповідь, так стріляти по ногах коштує дорого і розуміння того, що через декілька ітерацій почнеш стріляти собі по ногам приходить тільки з досвідом. Я про те що не розуміння для того ми витримуємо якусь архітектуру приводить до того що до архітектури починають відноситись як до ритуалу, чи як до релігії...якщо в проєкт додаємо будь яку складність то варто розуміти навіщо, які бонуси отримаємо в майбутньому . В наведеному прикладі все ж таки було важливо яка база даних використовується, як не крути цей момент потрібно враховувати відразу в залежності від потреб бізнесу
Для цього існує Repository pattern.
Він інкапсулює взаємодію з базою даних.
Ну а сервіс працює вже з Repository.
Якщо я правильно вас зрозумів...
Як би круто не робив архітектуру ховаючи все по папочках, я гарантую, що в середньостатистичному сервісі людині яка перший раз його бачить буде набагато легше розібратись в плоскій структурі, де все скопом лежить в папках controller, service, repository, entity чім бродить по модулях і гадать - а куди ж саме відноситься те що мені потрібно знайти...
Як би просто не було розібратися в середньостатистичному сервісі спочатку, я гарантую, що після 10 спринтів активної розробки він перетвориться на таке кубло навіть не коду, а просто тексту, что всі потім ще півроку будуть ходити та думати як цього шматка лайна позбутись ))
Вже довго думаю над запитанням організації бізнес логіки, можливо ви підкажете, що думаєте з цього приводу.
В більшості bekend’ів монолітів, які я бачив використовувалась simple domain model (SDM), де entity - це грубо кажучи, контейнер для даних, а рівень сервісів містить всю бізнес логіку, але з точки зору, Р.Мартіна та М.Фаулера, такий підхід не дуже хороший.
З іншої сторони є rich domain model (RDM), тобто entity, яка містить дані та певні методи бізнес-логіки для роботи з цими даними, що по логіці ООП є правильніше ніж SDM. Саме таку entity побачив у відео. Але при розробці великих додатків з RDM, де в одного доменного entity може бути велика вкладеність інших entity, агрегатів, value objects і т.д. Відповідно всю бізнес логіку по створенню і редагуванню таких обʼєктів, потрібно помістити в головну entity і вона може бути дуже великою, що не зручно.
Підкажіть вашу думку стосовно цього і який піхід до організації бізнес логіки ви частіше обираєте ?
>> в одного доменного entity може бути велика вкладеність інших entity, агрегатів, value objects
Маленьке уточнення - в такій картинці оця "доменна entity" називається агрегатом, а самі entity агрегатів всередині не мають.
Відповідь на ваше питання - це організація системи у вигляді окремих модулів (наприклад, мікросервіси для розподілених систем, або бандли для OSGi, або будь-які інші структурні одиниці вашого коду), де кожний з цих модулів отримує свою single responsibility. Таким чином, агрегати всередині цих модулів мать посилання один на одного (типу, ідентифікатор) яке виглядає як один value object заміст цілої вкладеної сутності.