.NET Core vs Node.js. Аргументы и факты

Данная статья является продолжением предыдущей, которая вызвала много хороших и интересных вопросов. Когда я начал писать ответы на комментарии, то понял, что о многом не рассказал. Поэтому решился написать новую полноценную статью, но уже под другим углом: добавил личный опыт и причинно следственные связи. Если вы не читали предыдущую, то ничего страшного. Можете и не читать.

Основная мысль — в середине 2017 года я инициировал смену основного стека разработки бэкенд приложений, разрабатываемых нашей компанией с .Net (C#) на Node.js (typescript). Как это вышло, жалею ли о принятом решении и не собираюсь ли назад? Об этом ниже.

Предыстория


Сначала небольшой рассказ о себе. Родился я программистом, не менеджером. Свое первое приложение, о котором не стыдно рассказать, сделал в 2003 году на С++ в IDE Visual Studio 6 будучи студентом. Это была игра «Трансформеры» с применением D3D, DirectSound и почти всех других модулей, входивших на тот момент в состав DirectX 9, не ну а чё, «я ж программист».



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

Тогда казалось, что для студентов 3 курса без опыта работы и внятного портфолио это была единственная возможность устроиться на работу в очень популярную в то время GameDev компанию. Но у нас ничего не вышло. После 4х часового собеседования, на котором, среди прочего, мы писали на бумаге алгоритм пересечения двух прямых на assembler нам отказали, сославшись на то, что не готовы сейчас связываться со студентами.

C# .Net


Спустя пару месяцев после этой неудачи я оказался в большой (более 100 программистов) софтверной компании, где с появлением C#, перешел на него.



В результате общения с более опытными и высококвалифицированными коллегами, я на практике овладел такими интересными штуками, как:

  • аспектно-ориентированное программирование (АОП) для устранения служебного кода из бизнес логики;
  • кодогенерация на tt-шаблонах, для уменьшения рутины по созданию однотипного кода;
  • NServiceBus, MSMQ, WCF для межсервисного взаимодействия;
  • Dependency Injection (DI);
  • принцип построения интерфейсов с учетом Data Load Options (DLO) для указания связанных объектов, информация о которых также должна быть получена в результате выполнения запроса. Что-то типа самописного GraphQL, которого на тот момент не существовало в публичном виде;
  • профилирование и оптимизация SQL запросов;
  • создание веб-приложений на ASP.Net MVC, knockout и angular.js.

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



Подразделение, в котором я работал, разрабатывало интерфейсы, по которым данные из СУБД становились доступными другим системам. Год за годом в голове стала крепнуть мысль, что в бездушном enterprise все именно так и что ООП для задач типа «возьми вот это, немного покрути и отдай вот туда» так себе идея.

На уровне реляционной СУБД и контрактов данных в REST API ООП поддерживается только энтузиазмом разработчика, который хочет писать на ООП и только на нем. А логика промежуточного слоя (между взять из базы и отдать потребителю) по сложности и связанности — это ни разу не GameDev и может быть легко написана в функциональном стиле.

В середине 2015 года я с 8-ми летним опытом работы на C# отправился за интересными задачами в компанию SoftMediaLab.



Благодаря накопленному опыту как на бэкенде, так и на фронте я сразу стал руководителем разработки нового достаточно крупного проекта — online-площадки p2p и b2p кредитования. На бэкенде был C# .Net Framework 4.6, микросервисная архитектура, общение микросервисов по WCF, postgreSQL 9.5, ORMLite, кодогенерация ORM-классов для хисторных таблиц и скриптов раскатки/перенакатки объектов СУБД, в СУБД около 150 таблиц, postsharp 2.1 для АОП, сокеты на SignalR, посадочные страницы и WEB API на основе ASP.Net MVC. На фронте angular.js и JS т.к. typescript в то время еще не имел широкого распространения. CI/CD, потоковая репликация на дублирующий узел.

После завершения проекта в середине 2016 года мы совместно с основными разработчиками провели ретроспективу. На ней обозначили следующие проблемы, они же перспективы для развития:

  • в фронтенд части любой рефакторинг может легко привести к ошибкам по невнимательности из-за отсутствия статической типизации JavaScript и дороговизне полноценных тестов на UI;
  • в системе много логики, которую пришлось писать и поддерживать в двух местах — на C# в бэкенде и на фронте в JS. Эта логика была связана с работой кредитного калькулятора: расчеты плана платежей, пролонгации, досрочного закрытия договора и т.д. По требованиям этот калькулятор также должен был присутствовать на WEB-сайте и работать без обращений в бэкенд;
  • часто возникала необходимость провести небольшую по сложности доработку, затрагивающую бэк и фронт одновременно, которую один фулстек делал за такое же время, как и два программиста — отдельный бэкендер и отдельный фронтендер. При этом, в случае с одним фулстеком, разработку и тестирование таких задач было существенно проще планировать в спринтах;
  • в системе много кода, связанного с преобразованиями моделей данных в/из DTO. Лучше было бы этот код писать и поддерживать в актуальном состоянии не руками, а кодогенерацией;
  • найти хорошего фулстек программиста на стеке (C#, angular или react) для усиления команды сложно.

После этого проекта был следующий, который мы писали также на C#. Но чтобы закрыть проблему рефакторинга JavaScript кода на фронте мы единогласно приняли решение перейти с angular.js на angular.io с поддержкой typescript из коробки.

У меня к Typescript возникла любовь с первого взгляда.



Конечно, это был не такой строго типизированный C#, а скорее намек на типизацию, с помощью которой программист описывал свои ожидания от структуры объекта с которым работает. А наши ожидания, это… ну вы помните…



Но все таки, это был настоящий прорыв после чистого JavaScript и рефакторинг фронтенд части на этом проекте уже перестал быть ощутимой проблемой. А значит поставленная цель достигнута!

Node.js


В начале 2017 года, после того, как typescript показал себя с лучшей стороны на фронте, идея попробовать писать бэкенд на typescript показалась мне логичной.

На тот момент к разработке бэкенда у меня были следующие требования:

  • код для раскатки/перераскатки СУБД, включая триггерные функции и хисторные таблицы должен быть создан автоматически — кодогенерацией или ORM;
  • код создания моделей DTO и мапперов в/из них должен быть создан автоматически с помощью кодогенерации;
  • swagger-описание REST API должно автоматически генерироваться из исходного кода;
  • должна существовать полноценная ORM-система, позволяющая работать с MS SQL, PostgreSQL, mongoDb, а также писать кастомные запросы для особых случаев;
  • должны существовать библиотеки интеграции с redis, rabbitMQ, socket, т.к. вероятность использования данных инструментов на новых проектах очень высока;
  • кросплатформенность — чтобы у разработчиков был выбор ОС;
  • стабильность и отказоустойчивость;
  • возможность оптимизации узких по производительности мест на проверенной и хорошо зарекомендовавшей себя технологии;
  • удобная IDE, которая позволяет легко писать, рефакторить и отлаживать код.

Исследования пунктов, не связанных с кодогенерацией, я провел в фоновом режиме в течении месяца и нашел такие стоящие решения как typeorm, express.js, socket.io, redis, typescript-rest-swagger.

В середине 2017 года node.js уже была достаточно стабильной технологией. За пару часов в Google я нашел с десяток сервисов, работающих в условиях высокой реал-тайм нагрузки, реализованных на ноде. Также узнал, что оптимизацию узких по производительности мест нужно выполнять на C++.

Для исследования возможностей кодогенерации я принял на работу толкового выпускника технического института с небольшим опытом работы на C# и вообще без опыта работы на JS/TypeScript. Чтобы, среди прочего, проверить порог вхождения в TypeScript.

Месячным результатом работы толкового выпускника стало три библиотеки — grunt-generate-view-model, grunt-generate-history-model, grunt-generate-database, которыми наша компания пользуется на проектах до сих пор. Только префикс «grunt-» сейчас уже не актуален. Он тянется из исходных версий, когда они работали еще и как плагины сборщика проектов grunt.

В конце лета 2017 года я собрал совещание технических специалистов, на котором презентовал возможности node.js, и, большинством голосов, мы приняли решение сделать следующий коммерческий проект в связке node.js (TS) + angular.io (TS).

Так получилось, что в это время в нашей компании одновременно началось два проекта:

  • коммуникационная платформа, позволяющая сотрудникам офиса общаться с клиентами по Telegram, WhatsApp, FB, VK и Viber в одном окне;
  • и проект по автоматизации внутренних бизнес процессов одного из наших клиентов.

Оба проекта были успешно реализованы на новом стеке. С тех пор, все новые проекты мы делаем на TS. Бэкенд на node.js, фронт на react (почему перешли с angular.io на react — тема для отдельного материала), а мобильные приложения на react-native.

Ретроспектива три года спустя


С момента внедрения node.js наша компания реализовала более 10 проектов на данном стеке.



Какие плюсы получили от перехода с .Net C# на node.js typescript?

Разработчики:

  • легко и с удовольствием увеличивают свои компетенции в смежных областях. Например, к нам в компанию в начале этого года пришел верстальщик, который в течении испытательного срока успел поучаствовать в проекте, как бэкенд разработчик и как разработчик мобильных приложений. С учетом того, что у нас на проектах обязательно CI, CD и code review, я доволен результатом его работы (CI проходит, кол-во замечаний по code review уменьшается от пул реквеста к пул реквесту);
  • получают удовлетворение от быстрого перезапуска обновленной версии в режиме разработки с помощью инструмента nodemon. Достаточно сохранить изменение в файле, как утилита мгновенно на это отреагирует, соберет и запустит обновленную версию бэкенда;
  • пишут максимально переиспользуемый код (один на бэк, web и mobile);
  • работают в привычной для себя ОС и IDE;
  • имеют больше возможностей для обмена опытом и собственном развитии т.к. вокруг все фулстеки, но кто-то круче в одном, а кто то в другом;

Тимлиды:

  • не разруливают споры между бэкендерами и фронтендерами;
  • планируют небольшие доработки по функционалу, затрагивающему бэк и фронт из расчета на одного фулстек-разработчика;
  • выбирают исполнителя для задачи в зависимости от индивидуальных особенностей разработчиков безотносительно того бэкендер это или фронтендер. Например, более аккуратный и дотошный разработчик чаще верстает pixel-perfect, а программист с хорошо развитым алгоритмическим мышлением чаще пишет логику на фронте и на бэке;

HR:

  • рассматривают большой круг кандидатов: не важно, верстаешь ты на react, пишешь на angular или node.js, а может быть ты программист react-native? Мы рады всем, особенно кто имеет опыт и понимание необходимости использования typescript в проектах;

CTO:

  • имеет большой выбор при составлении команды под новый проект;
  • может ускорить разработку за счет подключения новых разработчиков;
  • не ломает голову, на кого заменить тимлида или сотрудника на проекте, когда тот уходит в отпуск;
  • укладывается в обозначенный на старте проекта срок и трудозатраты в 90% случаев.

Но есть и объективные минусы, куда же без них:

  1. один отличный и еще один неплохой разработчик, которые не захотели переходить на node.js, больше у нас не работают;
  2. отладка кода на Typescript с помощью breakpoint из под VS Code в связке с nodemon полный отстой. Точки остановки то не срабатывают, то срабатывают, но не там. В итоге это приводит к тому, что рано или поздно разработчик сдается и начинает отлаживать код вставками console.log;
  3. Typescript — ваши ожидания, это ваши проблемы. Часто новички попадают впросак, когда, например, объявляют целочисленную переменную в которую получают значение из внешней библиотеки или, скажем, инпута с формы и долго недоумевают, как же так получилось, что у них в переменной оказалось string-значение;
  4. если при компиляции ts в js появляются кольцевые зависимости (например, файл a.ts импортирует b.ts, а b.ts импортирует a.ts), то ошибка возникает на этапе выполнения (значения создаваемых инстансов импортируемых классов undefined), а не на этапе компиляции;
  5. инфраструктура npm иногда (по нашему опыту 1-2 раза в год) преподносит неприятные сюрпризы. Когда обновление версии какой-либо библиотеки подключенной через ^ в зависимой библиотеке (зависимость зависимости) выводит из строя всё приложение. Для минимизации таких проблем создан служебный файл с описанием всего дерева зависимостей package-lock.json. Но в случаях, когда разработка ведется на разных ОС случаются ситуации, когда этот механизм не работает без кропотливого ручного вмешательства.

Холиварные минусы node.js и окружения:

  1. нужна высокая квалификация, чтобы писать расширения на C++. Но так получилось, что за три года у нас ни разу не возникла в этом необходимость. В производительность JS-слоя не упирались;
  2. возможности для реализации ООП и рефлексии гораздо беднее, чем на C#. Но опять таки, функциональное программирование с использованием промежуточных слоев обработки (middleware) — это хорошая альтернатива для очень широкого круга задач;
  3. максимально облегченная VS Code по сравнению с мощной VS;
  4. вместо мощного LINQ встроенные JS-методы по работе с массивами, такие как filter, map, reduce, sort;
  5. в полноценной VS лучше средства профилирования приложения под нагрузкой.

Итого


Я не жалею о том, что принял решение перевести компанию на node.js. Плюсы на наших проектах (а это автоматизация внутренних бизнес процессов любой сложности, тендерные площадки, p2p/b2b/b2p платформы, CRM-системы, социальные WEB-сервисы, MVP любых бизнес идей) на мой взгляд перевешивают минусы. Сроки по проектам не срываются. Набор новых сотрудников активно идет, а у прошедших испытательный срок, глаза горят, а руки рвутся в бой!



При этом конечно же есть сферы, где node.js сейчас не катит. Это GameDev, Data Science, Machine Learning, AI. Но оно и понятно. Если бы был идеальный инструмент для всего, то других бы не осталось.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 84

    0
    А насколько трудным был переход от c# на node.js, учитывая MVC?
      –1
      У фулстек разработчиков трудности перехода были связаны не с node.js, а с привыканием к VS Code. По времени адаптация заняла примерно 2 недели.

      Чистые бэкендеры на C# втягивались в ноду около 2-х месяцев. Но зато сразу после этого могли решать задачи, не связанные с версткой, на фронте.
        +1
        Но зато сразу после этого могли решать задачи, не связанные с версткой, на фронте.
        Зачем бэкендеру решать задачи на фронте?
          +1

          Чтобы развивать свои компетенции не только вглубь, но и вширь. После того как изнутри понимаешь, как используется результат твоего труда, это очень увеличивает качество твоего результата.

            +1

            Ну, например, при изменении контрактов API самому же изменить их поддержку на фронте.

        +1
        сдается мне что Blazor как раз разрабатывался под такие хотелки как ваши (конечно на тот момент его еще не было).

          +2
          Вряд ли блазор ожидает жизненный цикл, отличный от силверлайта и подобных. Слишком нишевый продукт, а у майкрософта с долговременным жизненным циклом таких продуктов реально беда.
            0
            Чой-то? Он позволяет главное — писать броузерные приложения без единой строки JS. Это победа ящетаю.
              +1
              У MS было много очень неплохих продуктов, которые «в мир» так и не вышли, а так и остались чахнуть внутри экосистемы, пока сам MS их не забросил. Такие вещи как typescript — редкое исключение. Может и у блазора получится, но что-то очень сомнительно. Нет к этому таких предпосылок.
          –6
          максимально облегченная VS Code по сравнению с мощной VS
          Что мешает использовать PhpStorm?
            +6
            php в названии :D
              0
              Где связь? Для работы с js/nodejs самая удобная IDE, как по мне.
                0
                я шучу, просто мы с дотнета часто хейтим php
                  0

                  PHPStorm — IDE с уклоном на PHP
                  WebStorm — IDE с уклоном на JS

                    0

                    PHPStorm — это WebStorm (все возможности) плюс PHP

                      +2

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


                      Стоит упомянуть, что WebStorm включает предустановленные плагины, которых изначально нет ни в PHPStorm, ни в PyCharm.
                      И стоит WebStorm в полтора раза дешевле.

                        0

                        Можно пример таких плагинов? Особенно тех, что доступны только в WebStorm и Idea, а в PhpStorm даже поставить нельзя.

                          +1

                          Недоступных нет. Есть предустановленные, которых изначально нет в PHPStorm (их можно установить вручную).


                          Cucumber.js; Dart; EditorConfig; EJS; Handelbars/Mustache; Java Server Pages (JSP) Integration; Karma; LiveEdit; Meteor; PhoneGap/Cordova Plugin.

              +7

              … а почему у вас в заголовке .net Core, а в тексте — про "обычный" .net? Хотя вот как раз у Core сильно меньше проблем с кроссплатформенностью (да и с IDE)?

            • UFO just landed and posted this here
                +1
                Задачи разработки пользовательского интерфейса и бэкенда

                Как ни странно, большая часть задач на фронте и на бэкенде абсолютно одинаковые.
                Мы берем данные (из СУБД или кэша в случае с бэком и из бэкенда или хранилища состояния (redux/mobx/rxJs) в случае с фронтом), выполняем некоторые преобразования и отдаем их потребителю (фронту в случае с бэком или компоненте визуализации в случае с фронтом). Специфичные регулярно возникающие задачи — это проектирование СУБД и интерфейсов — для бэка и верстка — для фронта.

                Как сделать, чтобы крутой бэкендер не залип на несколько дней с верстальной задачей?

                достаточно хорошо декомпозировать задачу (чтобы занимала не больше дня) и прийти на помощь разработчику, если в ожидаемый срок результата нет

                у кого лучше получается, тот и берет как бороться со специализацией?

                постепенно. Сначала давать небольшой объем несвойственных задач, потом контролировать результат и удовлетворенность разработчика от того, что он делает. Если совсем не его, то не давать такой тип задач. Но у нас разве что верстку не все любят, а с остальным таких проблем я не замечаю

                Все это в купе с переходом от типизации C# должно сказаться на процессах обеспечивающих качество

                от типизации C# мы перешли к типизации typescript

                Измеряли ли вы (а потом сравнивали) показатели качества?

                Для нас показатели качества всегда были в приоритете. К нам возвращаются наши Клиенты за разработками нового ПО, для меня это самый главный показатель. Говорят, что им очень нравится наша скорость работы и недельные спринты с понятным результатом

                у вас все разработчики фулл-стеки, а значит дороже

                Фулстеки бывают разные. Джуны, мидлы, сеньоры. З/п зависит от общего уровня

                Кто-то может выполнять задачу, в которой не является специалистом (например, мобильная разработка), а значит будет делать дольше, а значит дороже

                Это так, но мы идем на эти потери (надо сказать, очень небольшие) сознательно. У нас соотношение серверной/WEB/мобильной разработки часто меняется. Поэтому нужны надежные резервы во всех направлениях.

                Код-ревью подразумевает, что все будут либо распыляться на три платформы (фронт, бэк, мобилки)

                Я предпочитаю формулировку «набираться опыта в трех платформах»

                сделали как сделали, а потом тянем деньги на суппорт

                Нет, у нас не такая бизнес модель. У нас есть бесплатная тех-поддержка для наших решений (от полу года до года в зависимости от проекта). Поэтому для нас траты на тех. поддержку — это минус и мы стремимся делать так, чтобы Клиенты возвращались с запросами на новые фичи или системы, а не за исправлением багов.
                • UFO just landed and posted this here
                    0
                    Спасибо Вам за такой содержательный комментарий с интересными вопросами)
                +1

                Стоит указать что Node.js однапоточна и красиво распараллеливать как в .NET не получится.

                  –1
                  Мы обычно распараллеливаем популярные микросервисы с помощью cluster
                  0
                  1.
                  swagger-описание REST API должно автоматически генерироваться из исходного кода
                  Это в корне не верно. OpenApi — пром. стандарт описания сервисов, соответственно пишутся сервисы на сваггере, и из сваггер-описания генерируются заглушки с исходным кодом с готовым транспортом и валидацией. А не наоборот.

                  2.
                  Тимлиды не разруливают споры между бэкендерами и фронтендерами;
                  Во только при чём тут выбор ЯП для бэка? Вообще ни как не связано. Есть огромное количество бэкендов, написанных на ЯП, отличных от js/ts и C#, в которых тимлиды и близко ничего такого не разруливают, да и разруливать нечего ввиду отсутствия споров.

                  3. Из предыдущего
                  чтобы загрузить все ядра сервера придется воспользоваться модулем “Cluster” или внешним управлением процессами.
                  Опять таки решение не имеет ничего общего с задачей. «чтобы загрузить все ядра сервера», нужно оптимизировать CPU-bound вычисления. Cluster к этому ни каким боком

                    +1
                    Это в корне не верно. OpenApi — пром. стандарт описания сервисов, соответственно пишутся сервисы на сваггере, и из сваггер-описания генерируются заглушки с исходным кодом с готовым транспортом и валидацией. А не наоборот.

                    Не работал с таким потоком, работал с указанным автором source -> spec (причём в .NET с этим было ноль проблем).
                    Что, в вашем варианте, происходит при обновлении сервиса? Скажем, я добавляю поле в модель. Я должен добавить его в спецификации и у меня автоматически обновится соответствующая модель в исходном коде?
                      –1
                      Проблема в привязке к стеку проприетарных технологий там, где в этом нет ни малейшей необходимости. Спеку OpenApi проще читать, чем контроллеры asp net, она более выразительна и компактна. Проще перейти с работающего прототипа asp net на тот же nodejs. Проще декларативно прописать валидацию, аутентификацию, композирование требований безопасности и т.д.
                      Я должен добавить его в спецификации и у меня автоматически обновится соответствующая модель в исходном коде?
                      Разумеется так, иначе нафиг оно всё это было бы нужно
                      Не работал с таким потоком
                      хз как это сделано в net (и сделано ли). Но в том яп, который я использую, это всё на достаточно рабочем уровне
                        0
                        Промахнулся с ответом: habr.com/ru/post/506680/#comment_21741526
                          0

                          Ну так и читайте спеку OpenApi! Какая при этом разница что на основе чего было сгенерировано?

                            0
                            Разумеется так, иначе нафиг оно всё это было бы нужно

                            И миграции базы сгенерируются, со всеми "дефолтными" связями?

                        0
                        Могу рассказать, как в моих проектах сделано:

                        Фронт на typescript + react, бэк на java + spring, подход к контракту API — api spec first.

                        По спеке API генерируется клиент API с DTO для фронта и интерфейсы для контроллеров и DTO для бэка.
                        Если в API что-то поменять, то при сборке будут заново сгенерированы клиент, интерфейсы контроллеров и DTO, и на этапе компиляции typescript или java покажут сломанные места.

                        Главный плюс такого подхода: фронтенд-разработчик может менять спеку API так, как нужно ему, не зная java, бэкенд-разработчик может менять спеку не зная typescript. Естественно, этот плюс пропадает, если на фронте и бэке один язык.

                        Главный минус: официальные генераторы кода. Некоторые вещи сложно выразить, например streaming больших файлов. Некоторые вещи, описанные в документации swagger, поддерживаются не всеми генераторами или поддерживаются неправильно. В принципе качество кода этих генераторов ужасно (видел, когда улучшал поддержку некоторых нужных вещей).

                        Если бы на фронте и на бэке был typescript, то я бы использовал подход code first, выделив контракт API в модуль, который бы переиспользовался и на фронте и на бэке, руками написав реализацию этого контракта. По большому счету swagger нужен или в проекте с солянкой технологий или для публичного API.
                          0
                          Спасибо, звучит интересно.
                          Если в API что-то поменять, то при сборке будут заново сгенерированы клиент, интерфейсы контроллеров и DTO

                          Самописного кода у вас там нет? Боюсь, как бы генератор его не потерял.
                          Главный плюс такого подхода: фронтенд-разработчик может менять спеку API так, как нужно ему, не зная java

                          Абстрактно соглашусь, но на практике как часто вам нужно менять модель и не менять сам код АПИ? Грубо говоря, если я добавил новое поле к условному заказу, то мне и обрабатывать его надо, так что бекенд работа всё равно присутствует.
                            0
                            Самописного кода у вас там нет? Боюсь, как бы генератор его не потерял.


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

                            как часто вам нужно менять модель и не менять сам код АПИ? Грубо говоря, если я добавил новое поле к условному заказу, то мне и обрабатывать его надо, так что бекенд работа всё равно присутствует.


                            Я имел в виду, что если используется подход code first, и спека API создается из аннотаций на java-классах бэкенда, то фронтенд-разработчик должен каждый раз на пальцах объяснять бэкенд-разрабочику, что он хочет поменять. В подходе же spec first они оба могут вносить изменения сразу в спеку. Да, изменения в спеке сломают проект и оба разработчика должны будут исправить свою часть проекта.

                            В идеале, конечно, иметь команду из фуллстеков, которые внесут изменения сразу и на фронт и на бэк, но так бывает не всегда. Я видел проект, где фуллстеков не было, причем разработка спеки API шла от фронта. В таком проекте code first со стороны бэка не подходил, возможно есть инструменты для code first со стороны фронта, тут я не в курсе.

                            Вообще, самое главное, по моему мнению, обеспечить типобезопасность для API, для этого нужны статически типизированные языки на фронте и бэке и единый источник правды, из которого потом будут сгенерированы клиенты API / интерфейсы для серверов. А что будет этим источником правды — спека swagger или классы с аннотациями — не так важно и определяется скорее структурой команды и/или проекта.
                              0
                              Кажется мне, что когда API могут менять и фронт и бэк — так себе подход к разработке.
                              Лучше когда кто-то один:
                              — либо фронт. Тогда он делает спеку и бэк уже по ней работает.
                              — либо бэк. Тогда спеку можно генерировать из классов и передавать фронту.
                                +1
                                Как я уже писал, сильно зависит от проекта / команды. Например:

                                1.
                                проект, где фуллстеков не было, причем разработка спеки API шла от фронта
                                В этом проекте бэкенд-разработчик спеку вообще не трогал, можно было бы использовать code first
                                2. Проект, который я пишу сейчас, состоит из нескольких модулей, один занимается обработкой данных и спеку его API я писал от бэкенда, второй модуль — админка (формы / визарды на фронте), спеку его API я писал от фронта. Поэтому я использовал подход spec first, это позволило единообразно собирать оба модуля.

                                Вообще, я время от времени сталкиваюсь с тем, что спека написанная мной от бэкенда не полностью устраивает фронтенд-разработчика, так как я что-то не учел. Не вижу ничего плохого в том, что фронтенд-разработчик предложит правки к спеке, и сам их внесет после обсуждения. Мне кажется, что так будет быстрее, чем построчно объяснять мне, что и где поменять.
                                0
                                Вообще, самое главное, по моему мнению, обеспечить типобезопасность для API, для этого нужны статически типизированные языки на фронте и бэке и единый источник правды, из которого потом будут сгенерированы клиенты API / интерфейсы для серверов. А что будет этим источником правды — спека swagger или классы с аннотациями — не так важно и определяется скорее структурой команды и/или проекта.

                                Абсолютно согласен, в идеале докинуть сверху consumer-driven contracts.
                                Спасибо за обсуждение.
                              –3
                              Да, у меня очень похоже всё на ваш воркфлоу, только у меня го, поэтому ни какие доп. хттп фреймворки типа спринга не нужны. Спеку правят совместно бэкендеры и фронтендеры, ревьюется тимлидом — и это всема удобно я бы сказал. Из спеки генерится всё — модели для сервера и фронта, валидация, транспорт, аутентификация, простой fetch-клиент и т.д.

                              «Главный минус: официальные генераторы кода» — я так понимаю это для всех языков. В го ни кто не пользуется офиц. генератором, есть годная сторонняя либа

                              «Если бы на фронте и на бэке был typescript, то я бы использовал подход code first» — ага, и фротендеру, чтобы править спеку, пришлось бы учить какой нибудь лютый expressjs и прочее node-специфическое г-но, ломая при этом проект бекенда. Человек занят важным делом — пиксили подгоняет, flexbox-ы выравнивает. И тут ему — фигакс! — внезапно надо лезть в серверный код.

                              Во-2, сильно сомневаюсь, что спека из кода генерируется адекватно. Что правильно прописывает валидацию, например. Правильно понимает схемы авторизации. Это всё не тривиально.

                              И вообще притягивать за уши nodejs чтобы всё писать на одном языке — это бред. Нет конкаренси, нет праллелизма, код изуродован асинхронщиной, npm этот дебильный с миллиардам node_modules — чего ради? На рынке труда полно соискателей со знанием js/ts(react) + ЯП бекенда. Ориентироваться на тупых, ниасиливших второй ЯП? если только

                              «swagger нужен или в проекте с солянкой технологий» — ну то есть везде где есть веб. Вот и я говорю — пром. стандарт
                                0
                                фротендеру, чтобы править спеку, пришлось бы учить какой нибудь лютый expressjs и прочее node-специфическое г-но


                                Я вижу контракт API в виде отдельного модуля, который состоит из набора DTO и интерфейсов. В нем не должно быть ничего специфичного для клиента или сервера. DTO описывают, какие данные передаются в API, интерфейсы описывают, какие методы API доступны. Потом интерфейсы реализуются на фронте, чтобы получить клиент и на бэке, чтобы получить контроллер. Таким образом фронтенд-разработчик меняет именно контракт, не трогая реализацию бэкенда, бэкенд-разработчик — контракт, не трогая фронтенда. В этом же модуле можно сделать валидацию, ведь DTO не будут генерироваться через swagger, а будут переиспользоваться.

                                Для общения между модулями на java я использовал такой подход без swagger вообще.

                                сильно сомневаюсь, что спека из кода генерируется адекватно. Что правильно прописывает валидацию, например


                                Теоретически, springfox, который превращает java-классы в спеку, должен работать с JSR 303: Bean Validation, но практически я не видел поддержки валидации в генераторах фронта, хотя библиотеки c валидаторами на аннотациях для фронта я видел. Это основная проблема swagger — очень фрагментарная реализация фишек.

                                притягивать за уши nodejs чтобы всё писать на одном языке — это бред


                                Я думаю, что автор статьи привел хороший пример того, когда это удобно — проект, в котором одинаковые бизнес-правила нужны и на фронте и на бэке.
                                  –3
                                  Я вижу контракт API в виде отдельного модуля, который состоит из набора DTO и интерфейсов
                                  Авторы OpenApi к счастью видят это по другому, поэтому добавили в сваггер много чего ещё, см. обзор фич сваггера. Сразу же возникает куча вопросов — как задать в ваших модулях методы аутентификации, схемы AND|OR авторизации, доп. форматы(base64 string, date-time, bsonobjectid и т.п.), доп. валидацию «через интиерфейс». Я понимаю, что какие-то ответы у вас могут быть, но могу заверить — они точно не понравятся любому, кто не пишет регулярно на java, особенно фронтендерам

                                  Вы предлагаете фронтенд разработчику слегка подучить джаву и ваши гайдланы, которые вам кажутся простыми. Но у него своих гайдлайнов хватает. Потом ему предложат написать ui для бекенда на рубях — ror учить? едва ли это кому-то понравится. Проще изучить OpenApi вместо специфических гайдлайнов и ЯП. Зачем фронтендеру вникать что можно и что нельзя делать в вашем проекте, если есть де факто стандартный язык компактного и декларативного описания сервисов?

                                  В этом же модуле можно сделать валидацию, ведь DTO не будут генерироваться через swagger, а будут переиспользоваться.
                                  Не проще ни разу, это довольно нудный бойлерплейт, особенно если нужен канонический RESTfull и требуется выбирать нужные коды ошибок хттп. На много проще валидацию описать декларативно в сваггере, и пусть себе генерируется.
                                  Для общения между модулями на java я использовал такой подход без swagger вообще.
                                  для такого есть другая универсальная технология — grpc. Используется чуть более чем везде .
                                  Я думаю, что автор статьи привел хороший пример того, когда это удобно.
                                  Да не сказал бы. Автор привёл пример примитивного круд сервиса, коих сотни тысяч на любых языках.

                                  «проект, в котором одинаковые бизнес-правила нужны и на фронте и на бэке» — к сожалению такая формулировка мне не понятна
                                    0
                                    Вы предлагаете фронтенд разработчику слегка подучить джаву и ваши гайдланы

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

                                    Не проще ни разу, это довольно нудный бойлерплейт

                                    Язык описания схемы в swagger более многословен, чем java + lombok, kotlin или typescript, так что в на swagger бойлерплейта будет больше. Плюс swagger менее удобен, когда спеку пора разбить на несколько файлов, не все инструменты экосистемы swagger нормально работают с $ref, ведущими в другой файл.

                                    На много проще валидацию описать декларативно в сваггере, и пусть себе генерируется.

                                    Валидация точно так же декларативно делается аннотациями на полях класса. Только в swagger кастомную аннотацию сложно добавить, а в код на java / typescript — просто.

                                    см. обзор фич сваггера

                                    И исходники генераторов, чтобы понять, какие из них реально поддерживаются.

                                    Давайте я вам проясню мою позицию по swagger:

                                    Идея технологии хорошая, но как и любая другая имеет свою область применения. В случае swagger еще добавляются особенности реализации — не все заявленные фичи поддерживаются для всех языков, поэтому нужно обязательно это иметь в виду, когда принимаешь решение о его использовании. В своих проектах я его использую, в основном spec first, но с удовольствием бы поменял на что-то более удобное и лучше сделанное.
                                      0
                                      Для систем, написанных на разных языках я не вижу альтернативы swagger
                                      Ну вот автор утверждает, что у него всё на ts, и тем не менее из кода генерируется спека сваггера в качестве документации. Получается, даже написав всё на одном языке сваггер всё равно нужен. Вот только, как мы выяснили, такая документация не соответствует апи или не является полной. Поскольку, например, декларативную валидацию из кода на ts едва ли адекватно воспроизведёт тулза, генерирующая спеку сваггера (аналогично другие фичи).
                                      swagger менее удобен, когда спеку пора разбить на несколько файлов, не все инструменты экосистемы swagger нормально работают с $ref, ведущими в другой файл.
                                      ну это значит экосистема совсем никудышная, раз таки мелочи не осиливает
                                        +1
                                        Вот только, как мы выяснили, такая документация не соответствует апи или не является полной. Поскольку, например, декларативную валидацию из кода на ts едва ли адекватно воспроизведёт тулза, генерирующая спеку сваггера

                                        Мне вот интересно. Значит, сделать спеку, которая соответствует коду, тулинг не может… а код, который соответствует спеке — сможет?

                                          0
                                          Значит, сделать спеку, которая соответствует коду, тулинг не может
                                          Не видел таких и сомневаюсь в их существовании — слишком сложная задача в моём представлении. Приведите пример если есть желание.
                                          а код, который соответствует спеке — сможет?
                                          Должен мочь. Иначе экосистема не годится для апи, не адекватна и не нужна — не поддерживает пром. стандарт.
                                            0
                                            Не видел таких и сомневаюсь в их существовании — слишком сложная задача в моём представлении. Приведите пример если есть желание.

                                            "Должен мочь. Иначе экосистема не годится для апи, не адекватна и не нужна — не поддерживает пром. стандарт."


                                            Должен мочь. Иначе экосистема не годится для апи, не адекватна и не нужна — не поддерживает пром. стандарт.

                                            "Не видел таких и сомневаюсь в их существовании — слишком сложная задача в моём представлении. Приведите пример для C# если есть желание."

                                              –3
                                              Трорллинг явно не удался по обоим пунктам — абсолютно не в тему
                                                0

                                                Аргументов, я так понимаю, не будет?

                                                  0
                                                  Перечитайте ваше предыдущее утверждение (если это можно так назвать) и задайте себе тот же вопрос
                                                    0

                                                    Ну так не себе, а вам. В чем, собственно, и состоит смысл упражнения.

                                                      –1
                                                      Смысл в ваших упражнениях появится тогда и только тогда, когда вы их аргументируете прежде, чем требовать аргументации от других
                                                        +1

                                                        Вы же не привели аргументации прежде, чем требовать ее от других? Вот и от других не требуйте.

                                            0
                                            Значит, сделать спеку, которая соответствует коду, тулинг не может… а код, который соответствует спеке — сможет?

                                            Ну, вообще, да. "Баловался" я одно время DSL-ами для спек разного уровня с кодогенерацией, спеки декларативные, код — ООП или ПП вообще. Максимум что получалось — внедрение каких-то незначимых для кода декларативных аннотаций, за синхронизацию которых с императивным кодом отвечает программист.

                                              0

                                              Вот любопытно мне. Как вы из спеки, которая говорит, что "такой-то ресурс возвращает такие-то HTTP-коды", сгенерите соответствующий код? Ну, в смысле, который других кодов не возвращает, а эти возвращает в описанных случаях?

                                                0

                                                Маппинг кодов на исключения в генераторе. Один нормальный код — 2хх, а остальное по исключениям типа ResourceNotFound — 404 и т. п.

                                                  0
                                                  Маппинг кодов на исключения в генераторе.

                                                  Угу, а преобразовывать исключения в коды вы где в коде будете?

                                                    0

                                                    В сгенерированном "водопроводном" контроллере :) типа try {} catch (ResourceNotFound e) { return new Response(404) }

                                                      0

                                                      Угу. А теперь давайте вспомним, что бывают коды, которые не-исключения. Вот 201 и 202, скажем. Которые еще и Location возвращают.


                                                      Кстати, а что будет в try этого метода?

                                                        –1

                                                        Как раз в try что-то вроде return new Response(201, {'Location': ${generateUri(resource)}}).

                                                          0

                                                          Там несколько кодов, откуда генератор знает, какой при каких условиях?


                                                          (и откуда resource берется?)

                                                            0

                                                            Есть некоторые ограничения, в частности упомянутый:


                                                            Один нормальный код — 2хх

                                                            То есть у каждого "роута" один нормальный, успешный код возврата.


                                                            resource возвращается сервисом. Реализовать сервис — ответственность разработчика

                                                              0
                                                              Есть некоторые ограничения

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


                                                              Потому что стандарт позволяет больше одного "нормального" кода.

                                                                0

                                                                Можно бы сделать больше, но в виду того, что к обратной задаче (генерации спеки по коду) даже не понятно было как лучше подступать, то забили, пользовались всё реже, а потом вообще удалили.

                                                                  0
                                                                  потом вообще удалили

                                                                  Вот.

                                                                    0

                                                                    Что вот? Тулинг, который только код по спеке генерировал, нам не подошёл, нам и обратный процесс нужен был. А вот задача генерации кода ("болванок") по спеке вполне решаема. Напомню, что обсуждаем


                                                                    Значит, сделать спеку, которая соответствует коду, тулинг не может… а код, который соответствует спеке — сможет?
                                                                      0
                                                                      А вот задача генерации кода ("болванок") по спеке вполне решаема.

                                                                      В полном объеме? Или "с ограничениями"?


                                                                      Задача генерация спеки из кода тоже решаема — при условии, конечно, что код написан с соблюдением неких правил.

                                                                        0

                                                                        Ограничения, о которых я говорил, были на стадии типа MVP. Все выявленные при "промышленной эксплуатации" недостатки было понятно как решать при одном условии: сгенерированный код руками не меняется. Но только для направления спека->код

                                                                          0

                                                                          Ну так давайте вернемся к примеру с возвращаемыми кодами (и соответствующими им ответами, и это мы еще даже к Content-Type не переходили). Как вы предлагаете решать озвученную проблему?

                                                                            0

                                                                            Не предлагаю, а собирался анализом/маппингом возвращаемого сервисом результата, возможно с использование какого-то ResultStatus контейнера или прослушиванием событий, синхронных.

                                                                            Хочу уточнить, у нас не было цели создать универсальный тулинг для обработки всего, что соответствует swagger спецификации, нам нужно было решать наши задачи интеграции фронта и бэка. Сделали MVP, который позволял фронту вносить изменения в спеку, которые приводили к перегенерации части фронта и бэка. Обратную задачу решить не смогли быстро, оставили как есть, но оказалось непрактичным. Бэк не хотел руками синхронизировать свой код со схемой.

                                                                              0
                                                                              Хочу уточнить, у нас не было цели создать универсальный тулинг для обработки всего, что соответствует swagger спецификации

                                                                              Это значит, что утверждение "тулинг может создать код, который соответствует спеке [в полном объеме спецификации]" вы не проверяли.


                                                                              А я говорю именно об этом.

                                                                                0

                                                                                Полностью нет, но в рамках начальной архитектуры препятствий особых не вижу.

                                                                                  0

                                                                                  А я не вижу препятствий сделать генерацию спеки по коду бэкенда, написанного по определенной архитектуре.


                                                                                  С чего начинали, к тому и вернулись.

                                                                                    0

                                                                                    Так архитектура бэка затачивается под спеку.

                                                                                      +1
                                                                                      Так архитектура бэка затачивается под спеку.

                                                                                      У вас — возможно. А у нас архитектура бэка затачивалась под фреймворк.

                              Only users with full accounts can post comments. Log in, please.