Search
Write a publication
Pull to refresh
9
0
Максим Юдаков @MJ_Viks

User

Send message
  1. На сколько я знаю для транзакций есть отдельные механизмы в каждой ORM
    В нашем примере можно сделать так:
    const session = await mongoose.startSession();
    session.startTransaction();
    try {
    await Lesson.create([{ name: 'test' }], { session });
    await Course.create([{ name: 'test' }], { session });
    await session.commitTransaction();
    } catch (e) {
    await session.abortTransaction();
    throw e;
    } finally {
    session.endSession();
    }

  2. В нашем реализации используется mongoDB. lesson это поле документа course, так что при удалении course lesson удалится каскадом

  3. Опять же в нашей реализации с mongo мы можем получить как весь документ course, так и отдельные его поля и под-документы lesson. В других ORM вроде тоже есть механизмы select и lazy select которые тянут либо сущность, либо сущность и ее связи

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

  5. Механизмов выпуска событий пока что не делали

  6. http был выбран для примера. Обычно присутствует весь зоопарк, и graphQL и брокеры и секеты и тд

Как я писал выше

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

Тут уже фундаментальное поле для дискуссий — оправдано ли существование вообще хоть какой либо архитектуры.

Вот тут не понял. Если у вас этот слой использует различные интерфейсы из инфраструктурного слоя

В инфраструктурном слое нет интерфейсов. Интерфейс репозитория это слой Domain или Application. Сами репозитории лишь реализуют интерфейсы из слоев выше и => зависят от них.

Ясно указывает, что слой приложения зависит и от слоя представления

Прямой связи нет. Все через абстрактные классы.

Да, благодаря внедрению зависимостей и абстрактным интерфейсам разорвана связь между реализацией, но сама связь сохранена.

Разорвана. Не могу понять где вы видите ее сохранение.

Причем вы даже ниже сами же используете такую же фигуру речи: "Таким образом, когда в Application-слое в конструкторе LessonService мы зависим от VideoLessonRepository (абстракции), NestJS внедрит нам экземпляр VideoLessonRepositoryImpl из инфраструктуры. "

Не вижу противоречий. Взяли абстракцию, ничего не знаем о реализации, в нужном месте указали реализацию.

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

Именно. "от слоя, который обслуживает вот эти интерфейсы" - т.е. от самих себя? Да, так и есть.

Такая же фигня. Я совершенно запутался кто и от кого зависит. Каким образом инфраструктура зависит от приложения?

Тем что реализует абстракции из него.

 В вашем примере как раз все наоборот инфраструктура зависит от абстрактного интерфейса инфраструктуры, от которого зависит уровень приложения. При этом инфраструктура так же зависит и от Домена.

Если как вы посчитали " инфраструктура зависит от абстрактного интерфейса инфраструктуры", то где вы увидели что "При этом инфраструктура так же зависит и от Домена"?

У вас другие зависимости. Как минимум в показанных примерах.

Все еще не могу понять где вы это увидели?

вашем примере Application зависит от Infrastructure

Вы путаете зависимость от абстрактных классов Infrastructure и от самого Infrastructure.

Все таки ознакомьтесь дополнительно с "принципом инверсии зависимостей"

Возможно я не понял изначально вопроса, прошу прощения.

Мы убирали связь реализации инфраструктуры от бизнес логики, но не связь как таковую вообще.

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

Тут уже фундаментальное поле для дискуссий — оправдано ли существование вообще хоть какой либо архитектуры.

Спасибо за интерес к статье!

Ознакомьтесь пожалуйста с "принципом инверсии зависимостей". Он даст вам понять почему зависимость от интерфейса != зависимости от реализации интерфейса и поможет разобраться в примерах.

Большое спасибо за такой развернутый комментарий!

Постараемся учесть все замечания в будущем

типовая боль таких экспериментов - маппинг

В DDD для этого рекомендуют делать и выносить отдельные мапперы, как мы и сделали в итоге: маппер из схемы БД в домен и обратно, и маппер с dto в доменный объект и обратно

декораторы для валидации звучит всрато, это у вас в js такое изобрели?

Почти весь nest на декораторах держится) https://docs.nestjs.com/techniques/validation

Если для сборки доменной сущности нужно много join'ов — это нормально, и это допустимо внутри именно репозитория, потому что задача репозитория — восстановить агрегат из хранилища.

Главное правило: обо всей этой сложности домен знать не должен, а сложность инфраструктуры не должна лезть в домен.

Как база архитектуры вообще — Роберт Мартин "Чистая архитектура"

А если по DDD то, наверное, Вон Вернон "Domain-Driven Design Distilled" или Влад Хононов "Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy"

Если честно, не искал.

Но гугл не дал никаких положительных результатов

Насчет Interface, интересное замечание. В нашем случае у нас там сложены все dto для валидации входных данных с внешнего мира, т.е. с rest хендлеров (в nest.js это классы с стандартными декораторами полей из class-validator). Т.к их достаточно много, выглядело удобно выделить в отдельный слой.

А что касается NestJS, да он действительно вдохновлён Spring и использует похожие подходы: DI-контейнер, декораторы, модули. Но это не обязательно минус - для некоторых проектов это даёт хорошую масштабируемость и структурированность. "ад зависимостей" — хаос можно устроить в любом проекте и на любой платформе. В этом мире все стремится к энтропии))

Продукт активно развивается?

Именно. Это и послужило одной из мотиваций

...была подсистема LMS — управление курсами и видео-уроками, — которая со временем показала потенциал роста...

Если взять легаси в 1000 строк, созданный за 7 человеко-дней, то переход на архитектуру с слоями и DDD-Lite мог занять порядка 10-12 человеко-дней. При этом мы смогли сохранить примерно 50–60% существующего кода, который уже отвечал базовым требованиям качества. Скорость внедрения новых фич увеличилась примерно на 20–25%.

Считали как Δ = (Told – Tnew) / Told × 100%
Если раньше на новую функциональность уходило бы примерно 5 дней (Told = 5), а после изменений — 4 дня (Tnew = 4). Тогда получается что: Δ = (5 – 4) / 4 × 100% = 25%

Учитывая вариации в оценках и специфику задач, мы наблюдали увеличение скорости внедрения новых фич в диапазоне 25–30%. Эти цифры подтверждаются сокращением времени от идеи до выпуска, ну и самое главное, снижением количества ошибок и скорости погружения в проблематику задачи.

Information

Rating
Does not participate
Works in
Registered
Activity

Specialization

Fullstack Developer
Senior
Node.js
Database
TypeScript
SQL
OOP
C#