Сначала фронт, а потом бэк (когда-нибудь)

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


    Разработка сложного функционала требует тонкой координации усилий коллектива инженеров.


    И одним из важнейших моментов является вопрос распараллеливания задач.


    Возможно ли избавить фронтовиков от необходимости ждать реализацию бэка? Есть ли способ распараллелить разработку отдельных фрагментов UI?


    Тему распараллеливания задач в веб-разработке мы и рассмотрим в этой статье.



    Проблема


    Итак, давайте для начала обозначим проблему. Представьте, что у вас есть матерый продукт (интернет сервис), в котором собрано довольно много разных микросервисов. Каждый микросервис в вашей системе — это своего рода мини-приложение интегрированное в общую архитектуру и решающее какую-то конкретную проблему пользователя сервиса. Представьте, что сегодня утром (в последний день спринта) к вам обратился Product Owner по имени Василий и обявил: "В следующем спринте мы начинаем пилить Импорт Данных, который сделает пользователей сервиса еще счастливее. Он позволит пользователю в сервис залить сразу стопиццот дофигаллиардов позиций из дремучей 1С!".


    Представьте что вы менеджер или тимлид и слушаете все эти восторженные описания счастливых пользователей не с позиции бизнеса. Вы оцениваете сколько трудозатрат все это потребует. Как хороший менеджер вы прикладываете все усилия, чтобы уменьшить аппетиты Василия на скоуп задач для MVP (здесь и далее, Minimum Viable Product). При этом два главных требования для MVP — способность системы импорта выдержать большую нагрузку и работа в фоне, выкинуть нельзя.


    Вы понимаете, что традиционным подходом, когда все данные обрабатываются в пределах одного запроса пользователя, обойтись не удастся. Тут придется городить огород всяких фоновых воркеров. Придется завязываться на Event Bus, думать о том как работает балансировщик нагрузки, распределенная БД и т.п. В общем все прелести микросервисной архитектуры. В итоге вы делаете вывод, что разработка бэкенда под эту фичу затянется, в гадалки не ходи.


    Автоматом встает вопрос: "A что будут делать фронтовики все это время пока нет никакого API?".


    Кроме того, выясняется, что данные-то надо импортировать не сразу. Нужно сначала провалидировать их и дать пользователю поправить все найденные ошибки. Получается хитрый воркфлоу и на фронтенде тоже. А запилить фичу надо, как водится, "вчера". Стало быть и фронтовиков надо как-то так скоординировать, чтобы они не толкались в одной репе, не порождали конфликтов и спокойно пилили каждый свой кусок (см. КДПВ в начале статьи).


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


    Agile пытается нам помочь



    Гибкие методологии дают мудрый совет. "Начните со скейта и покажите пользователю. Вдруг ему понравится. Если понравилось, продолжайте в том же духе, прикручивайте новые фишки."
    Но что, если пользователю сразу нужен как минимум мотоцикл, причем уже через две-три недели? Что если для начала работы над фасадом мотоцикла нужно хотя бы определиться с габаритами мотора и размерами ходовой части?


    Как сделать так, чтобы реализация фасада не откладывалась до тех пор, пока не появится определенность с остальными слоями приложения?



    В нашей ситуации лучше применить другой подход. Лучше сразу начать делать фасад (фронт), чтобы убедиться в корректности изначального представления об MVP. С одной стороны, подсунуть Product Owner'у Василию декоративный фасад, за которым ничего нет, кажется читерством, надувательством. С другой стороны, мы очень быстро получаем таким образом фидбек именно о той части функционала, c которой в первую очередь столкнется пользователь. У вас может быть неимоверно крутая архитектура, но если удобства использования нет, то какульками забросают все приложение целиком, не разбираясь в деталях. Поэтому мне кажется более важным выдать максимально функциональный UI как можно быстре, вместо того, чтобы синхронизировать прогресс фронтовой части с бэкендом. Нет смысла выдавать на пробу недоделанный UI и бэк, функционал которых не удовлетворяет главным требованиям. В то же время выдача 80% требуемого функционала UI, но без работающего бэка, вполне может оказаться профитной.


    Немного технических деталей


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


    Пользователь должен иметь возможность выгрузить в сервис файл данных большого объема. Содержимое этого файла должно соответствовать определенному формату (например, CSV). В файле должна быть определенная структура данных и есть обязательные поля, которые не должны быть пустыми. Иными словам, после выгрузки в бэкенде нужно будет данные провалидировать. Валидация может длиться значительное время. Держать коннект к бэкенду открытым нельзя (отвалится по таймауту). Поэтому мы должны быстро принять файл и запустить фоновую обработку. По окончанию валидации мы должны оповестить пользователя, что он может приступить к редактированию данных. Пользователь должен исправить ошибки, обнаруженные при валидации.


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


    Самый эффективный способ оповещения — WebSocket'ы. С фронта через Websocket с определенным периодом будут отправляться запросы на получения текущего статуса фоновой обработки данных. Для фоновой обработки данных нам понадобятся фоновые обработчики, распределенная очередь команд, Event Bus и т.д.


    Dataflow видится следующим (для справки):


    1. Через файловый API браузера просим пользователя выбрать нужный файл с диска.
    2. Через AJAX отправляем файл в бэкенд.
    3. Ожидаем окончания валидации и распарсивания файла с данными (опрашиваем статус фоновой операции через Websocket).
    4. По окончании валидации грузим подготовленные к импорту данные и рендерим их в таблице на странице импорта.
    5. Пользователь редактирует данные, исправляет ошибки. По нажатию на кнопку внизу страницы отправляем исправленные данные в бэкенд.
    6. Опять на клиентской стороне запускаем периодический опрос статуса фоновой операции.
    7. До окончания текущего импорта у пользователя не должно быть возможности запустить новый импорт (даже в соседнем окне браузера или на соседнем компьютере).

    План разработки


    Мокап UI vs. Прототип UI



    Давайте сразу обозначим разницу между Wireframe, Mockup, Prototype.


    На рисунке выше изображен Wireframe. Это просто рисунок (в цифре или на бумаге — не суть важно). С другими двумя понятиями сложнее.


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


    Прототип же создается с помощью тех же инструментов, что и будущий интерфейс пользователя (например, React). Код прототипа размещается в общем репозитарии приложения. Он не будет заменен, как это происходит с мокапом. Сначала его используют для проверки концепции (Proof of Concept, PoC). Потом, если он пройдет проверку, его начнут развивать, постепенно превращая в полноценный интерфейс пользователя.


    Теперь ближе к делу...


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


    Как составление алгоритма начинается с блок-схемы, так и создание прототипа начинаем с минималистичного Wireframe'а (см. рисунок выше). На этом Wireframe мы делим будущий функционал на крупные блоки. Главный принцип тут — фокусировка ответственности. Не следует разделять один кусок функциональности на разные блоки. Мухи идут в один блок, а котлеты в другой.


    Далее нужно как можно быстрее создать заготовку страницы (пустышку), настроить Routing и разместить в меню ссылку на эту страницу. Затем нужно создать заготовки базовых компонентов (по одному на каждый блок в Wireframe прототипа). И закаммитать этот своеобразный фреймворк в ветку разработки новой фичи.


    Получаем такую иерархию веток в git:


    master ---------------------- >
      └ feature/import-dev ------ >

    Ветка "import-dev" будет играть роль development бранча для всей фичи. У этой ветки желательно закрепить одного ответственного человека (мэйнтейнера), который мержит атомарные изменения от всех параллельно работающих над фичей коллег. Также желательно не делать прямых каммитов в эту ветку, чтобы уменьшить шанс конфликтов и неожиданных изменений при мерже в эту ветку атомарных пулл реквестов.


    Т.к. у нас на этот момент уже созданы компоненты для основных блоков на странице, то можно уже сразу создавать отдельные ветки под каждый блок UI. Финальная иерархия может выглядеть так:


    master ----------------------- >
      └ feature/import-dev ------- >
        ├ feature/import-head ---- >
        ├ feature/import-filter -- >
        ├ feature/import-table --- >
        ├ feature/import-pager --- >
        └ feature/import-footer -- >

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


    Подходом, описанным выше, мы обеспечиваем безконфликтную работу нескольких разработчиков UI. У каждого фрагмента UI свой каталог в иерархии проекта. В каталоге фрагмента есть основной компонент, его набор стилей и свой набор дочерних компонентов. Также у каждого фрагмента может быть свой менеджер состояния (MobX, Redux, VueX сторы). Возможно, компоненты фрагмента используют какие-то глобальные стили. Однако изменять глобальные стили при разработке фрагмента новой страницы запрещено. Изменять дефолтное поведение и стиль общего атома дизайна также не стоит.


    Примечание: под "атомом дизайна" подразумевается элемент из набора стандартных компонентов нашего сервиса — см. Atomic Design; в нашем случае предполагается, что система Атомарного Дизайна уже реализована.


    Итак, мы физически отделили фронтовиков друг от друга. Теперь каждый из них может работать спокойно, не боясь конфликтов при мерже. Также каждый может в любой момент создать пулл реквест из своей ветки в feature/import-dev. Уже сейчас можно спокойно набрасывать статичесий контент и даже формировать интерактив в пределах одного хранилища состояния.


    Но как нам обеспечить возможность взаимодействия фрагментов UI между собой?


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


    Для создания этого сервиса нам понадобится еще одна ветка в git:


    master --------------------------- >
      └ feature/import-dev ----------- >
        ├ feature/import-js-service -- >
        ├ feature/import-head -------- >
        ├ feature/import-filter ------ >
        ├ feature/import-table ------- >
        ├ feature/import-pager ------- >
        └ feature/import-footer ------ >

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


    $/> git checkout -b feature/import-service
    $/> git commit .
    $/> git push origin HEAD
    $/> git checkout feature/import-dev
    $/> git merge feature/import-service

    Кому-то это покажется напряжным, но профит минимизации конфликтов весомее. К тому же пока вы эксклюзивный владелец ветки, можете без опасений делать push -f без риска повредить чью-то локальную историю каммитов.


    Фэйковые данные


    Итак, на предыдущем этапе мы сделали заготовку интеграционного JS-сервиса (importService), сделали заготовки фрагментов UI. Но без данных наш прототип работать не будет. Ничего не рисуется кроме статических декораций.


    Теперь нам надо определиться с примерной моделью данных и создать эти данные в виде JSON или JS файлов (выбор в пользу того или другого зависит от настройки импортов в вашем проекте; настроен ли json-loader). Наш importService, а также его тесты (о них будем думать попозже) импортируют из этих файлов данные, необходимые для имитации ответов от реального бэкенда (он пока еще не реализован). Куда положить эти данные не суть важно. Главное, чтобы их можно было легко импортировать в сам importService и тесты в нашем микросервисе.


    Формат данных, конвенцию именования полей желательно обговорить с разработчиками бэка сразу. Можно, например, договориться об использовании формата, соответствующего спецификации OpenAPI Specification. Какой бы спецификации формата данных ни следовал бэк, фэйковые данные мы создаем в точном соответствии формату данных в бэке.


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


    Контракты


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


    Для описания контрактов (спецификации API) можно использовать специализированные инструменты. Напр., OpenAPI / Swagger. По-хорошему, при описании API с таким инструментом нет нужды в присутствии всех разработчиков. Этим может заниматься один разработчик (редактор спецификации). Результатом коллективного обсуждения нового API должны были стать некие артефакты вроде MFU (Meeting Follow Up), по которым редактор спецификации и конструирует справочник для будущего API.


    По окончании создания драфта спецификации не должно потребоваться много времени, чтобы проверить корректность. Каждый участник коллективного обсуждения сможет независимо от других сделать беглый осмотр для проверки, что его мнение было учтено. Если что-то покажется некорректным, то можно будет уточнить через редактора спецификации (обычные рабочие коммуникации). Если все удовлетворены спецификацией, то ее можно опубликовать и использовать как документацию на сервис.


    Юнит-Тестирование


    Примечание: Лично для меня ценность юнит-тестов довольная низка. Тут я согласен с Дэвидом Хансоном (David Heinemeier Hansson @ RailsConf). "Юнит-тесты — это отличный способ убедиться, что ваша программа ожидаемым образом делает д… мо." Но я допускаю особые кейсы, когда юнит-тесты приносят много пользы.


    Теперь, когда мы определились с фэйковыми данными, можно приступать к тестированию базового функционала. Для тестирования фронтовых компонентов можно использовать такие инструменты как karma, jest, mocha, chai, jasmine. Обычно рядом с тестируемым ресурсом JS кладется одноименный файл с постфиксом "spec" или "test":


    importService
      ├ importService.js
      └ importService.test.js

    Конкретное значение постфикса зависит от настроек сборщика пакетов JS в вашем проекте.


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


    К примеру, хорошо покрывать юнит-тестами разного рода хэлперы (helper), через которые между JS компонентами и сервисами расшариваются куски логики или неких алгоритмов. Также этими тестами можно покрыть поведение в компонентах и сторах MobX, Redux, VueX в ответ на изменение данных пользователем.


    Интеграционное и E2E-тестирование


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


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


    E2E-тесты (End-to-End) работают на ещё более высоком уровне. Они проверяют, что поведение UI корректно. Например, проверка, что после отправки файла с данными в сервис, пользователю показывается крутилка, сигнализирующая о длящемся асинхронном процессе. Или проверка, что визуализация стандартных компонентов сервиса соответствует гайдам от дизайнеров.


    Этот вид тестов работает при помощи некоторого фреймворка автоматизации UI. Например, это может быть Selenium. Такие тесты вместе с Selenium WebDriver запускаются в некотором браузере (обычно Chrome с "headless mode"). Работают они долго, но снижают нагрузку на специалистов QA, делая за них смоук тест.


    Написание этих видов тестов довольно трудоемко. Чем раньше мы начнем их писать, тем лучше. Не смотря на то, что унас нет еще полноценного бэка, мы уже можем начинать описывать интеграционные тесты. У нас уже есть спецификация.


    С описанием E2E тестов препятствий еще меньше. Мы уже набросали стандартные компоненты из библиотеки атомов дизайна. Реализовали специфичные куски UI. Сделали некоторый интерактив поверх фэйковых данных и API в importService. Ничто не мешает начать автоматизацию UI как минимум для базовых кейсов.


    Написанием этих тестов можно опять же озадачить отдельных разработчиков, если имеются не озадаченные люди. И также для описания тестов можно завести отдельную ветку (как описано выше). В ветки для тестов нужно будет периодически мержить обновления из ветки "feature/import-dev".


    Общая последовательность мержей будет такой:


    1. Например, девелопер из ветки "feature/import-filter" создал ПР. Этот ПР проревьюили и мэйнтейнер ветки "feature/import-dev" вливает этот ПР.
    2. Мэйнтейнер обявляет, что влито обновление.
    3. Девелопер в ветке "feature/import-tests-e2e" затягивает крайние изменения мержем из "-dev ветки.

    CI и автоматизация тестирования


    Фронтовые тесты реализуются с помощью инструментов, работающих через CLI. В package.json прописываются команды для запуска разных видов тестов. Эти команды используются не только девелоперами в локальной среде. Они нужны еще и для запуска тестов в среде CI (Continuous Integration).


    Если сейчас мы запустим билд в CI и ошибок не обнаружится, то в тестовую среду будет доставлен наш долгожданный прототип (80% функционала на фронте при не реализованном еще бэке). Мы сможем показать Василию приблизительное поведение будущего микросервиса. Васлилий попинает этот прототип и, возможно, сделает кое-какие замечания (возможно даже серьезные). На данном этапе вносить коррективы не дорого. В нашем случае бэк требует серьезных архитектурных изменений, поэтому работа по нему может идти медленнее, чем по фронту. Пока бэк не финализирован, внесение изменений в план его разработки не приведет к катастрофическим последствиям. При необходимости что-то поменять на этом этапе мы попросим внести коррективы в спецификацию API (в сваггере). После этого повторяются шаги, описанные выше. Фронтовики по-прежнему не зависят от бэкендеров. Отдельные специалисты фронтенда не зависят друг от друга.


    Бэкенда. Контроллеры-заглушки


    Отправной точкой разработки API в бэке является утвержденная спецификация API (OpenAPI / Swagger). При наличии спецификации работу в бэке также станет легче распараллеливать. Анализ спецификации должен навести разработчиков на мысль об основных элементах архитектуры. Какие общие компоненты / сервисы нужно создать, прежде чем приступать к реализации отдельных вызовов API. И тут опять же можно применить подход как с заготовками для UI.


    Мы можем начать сверху, т.е. с наружнего слоя нашего бэка (с контроллеров). На этой стадии мы начинаем с раутинга, заготовок контроллеров и фэйковых данных. Слой сервисов (BL) и доступа к данным (DAL) мы пока не делаем. Просто переносим данные из JS в бэкенд и программируем контроллеры так, чтобы они реализовывали ожидаемые ответы для базовых кейсов, выдавая куски из фэйковых данных.


    По завершению этой стадии фронтовики должны получить работающий бэкенд на статичных тестовых данных. Причем именно тех данных, на которых фронтовики пишут интеграционные тесты. По идее, в этот момент не должно составить большого труда переключить JS шлюз (importService) на использование заготовок контроллеров в бэке.


    Ответная часть для запросов через Websocket концептуально не отличается от Web API контроллеров. Эту "ответку" также делаем на тестовых данных и подключаем importService к этой заготовке.


    В конечном итоге весь JS должен быть переведен на работу с реальным сервером.


    Бэкенд. Финализация контроллеров. Заглушки в DAO


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


    Примечание: мы по-прежнему не зависим от того, реализована ли схема данных в БД.


    Бэкенд. Финализация DAO. Реальная БД


    После того, как схема данных реализована в БД, мы можем перенести в нее тестовые данные из предыдущих этапов и переключить наш зачаточный DAL на работу с реальным сервером БД. Т.к. мы в БД переносим изначальные данные, создававшиеся для фронта, все тесты должны остаться актуальными. Если какой-то из тестов упадет, значит что-то пошло не так и нужно разбираться.


    Примечание: вообще с очень большой вероятностью работы со схемой данных в БД для новой фичи будет немного; возможно изменения в БД будут сделаны одновременно с реализацией сервисов в BL.


    По окончанию этой стадии мы получаем полноценный микросервис, альфа-версию. Эту версию уже можно показать внутренним пользователям (Product Owner'у, продуктологу или еще кому-то) для оценки как MVP.


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


    Заключение


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


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


    Надеюсь, что вы нашли данную статью полезной.


    Спасибо за внимание!

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

    More
    Ads

    Comments 69

      +4
      Дочитал до
      Также у каждого фрагмента может быть свой менеджер состояния (MobX, Redux, VueX сторы).

      Задумался. Загрустил.

      Нет, конечно срочно ваять MVP таким манером, наверное, можно, но…
        0
        Вы про отдельные сторы у каждого фрагмента?
        0
        Спасибо, мы примерно так и разрабатываем. А расскажите подробней об import-service.js какие функции вы бы туда вынесли?
          0
          Первое, что само собой напрашивается, общение с бэком + возможно кэширование для редкоменяющихся данных.
          Можно внутри сервиса реализовать опрос хаба Websocket в бэке.
          Возможно, было бы уместно реализовать через него паттерн «Publisher — Subscriber» для оповещения фронтовых компонентах о событиях в бэке. Возможно, паблишера событий выгоднее будет реализовать отдельно от данного сервиса и импортировать в него как стороннюю сущность. Сервис будет чем-то вроде хаба / шлюза для интеграции с бэком.

          Таким путем данный сервис может дорасти до статуса Service Worker (кэширующая прокся между браузером и бэком). Это если в будущем понадобится нарастить мышечную массу в сервисе.

          Хочется повыносить в сервис такой весь инфраструктурный код, вроде клиента Websocket.
          +11
          С фронта через Websocket с определенным периодом будут отправляться запросы на получения текущего статуса фоновоой обработки данных

          Извините, а зачем через сокеты запрашивать что-то с фронта, если бэк сам может послать уведомление об окончании обработки?

            –12
            Бэк не может по своему усмотрению посылать что-либо на удаленный компьютер пользователя.
            То что выглядит как посылка от сервера на ваш компьютер скорее всего реализуется одним из двух способов:
            1. По принципу перманентного коннекта по TCP/IP, когда пайп удерживается непрерывной пересылкой проверяющих пакетов туда-сюда-обратно (вернее не удерживается, а проверяется не оборвалось ли подключение).
            2. Принимающая сторона постоянно спрашивает, есть ли для нее новое письмецо.

            Так или иначе, принимающая сторона выспрашивает у сервера новые сообщения.
            Какой способ эффективнее и надежнее, надо профилировать и проверять.
            Я предпочитаю явное поведение и более простую реализацию (в смысле контроля над поведением). Но в случае с websocket особой разницы нет.

            Вы либо спрашиваете сами с достаточно коротким периодом (скажем в 3 секунды), либо этот опрос делает за вас криво написанная либа для первого SignalR (с какой частотой она опрос сервера делает не выяснял).
              +11
              Извините, но по-моему вы в корне не правы. Если установлено правильное WebSocket-соединение, то именно бэк может сам засылать сообщения на клиента. Если вы в браузере откроете DevTools и посмотрите вкладку Network, то увидите среди прочих типов соединений WS, и если такие соединения есть, у них будет код ответа 101. Этот код устанавливается именно при переключении на постоянное соединение. Читайте здесь: developer.mozilla.org/ru/docs/Web/HTTP/Status/101

              Есть подозрение, что у вас бэк на php написан или типа того, который из коробки не держит постоянные соединения и не может вам обеспечить такой функционал. Но библиотеки типа socket.io умеют понимать, когда нормально сокетное соединение не поддерживаются и переходят в режим постоянных запросов-ответов (ping-pong). Вероятно у вас как раз так и работает. Но это не значит, что сервер не умеет отправлять принудительно сообщения в браузер.
                +2
                Упомянули SignalR, так что, скорее всего, на бэке — .NET. а то, что постоянно шлются запросы говорит о проблеме в настройке веб сервера.
                  0
                  а то, что постоянно шлются запросы говорит о проблеме в настройке веб сервера.

                  Таки да. Когда я проксирую через nginx, тоже приходится дополнительно конфиг дописывать, что-то типа такого:
                      location /api/ {
                          
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "upgrade";
                          proxy_read_timeout 36000;
                  
                          rewrite ^/api(.+) $1 break;
                          proxy_pass http://localhost:4000;
                  
                          proxy_set_header X-Real-IP $remote_addr ;
                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                  
                      }
                  –5
                  1. Статья не про SignalR. Детали реализации общения через WebSocket для целей статьи несущественны.
                  2. Подробно в подкапотных деталях работы SignalR и Websocket в целом копатья не доводилось. Согласно диаграммам в этом пособии и представлениям о сетях коммуникаций видится следующее:
                    а) Клиентская либа (SignalR напр.) инициирует Persistent Connection. Как это работает? Оба конца должны быть уверены, что коннекшен еще жив. Проверить это можно отправкой короткой датаграммы с неким периодом. Об этом я и писал.
                    б) Пока коннекшен жив мы можем слать данные туда-сюда и уже не важно чья инициатива начать отправку (событие в сервере или браузер клиента).

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


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


                  Для MVP было решено не переусложнять решение (для первого опыта с SignalR лучше попроще было сделать). SignalR Hub в итоге работает подобно WebAPI Controller, но транзакции значительно эффективнее. Переделать такое решение на нормальную событийную коммуникацию не очень сложно.

                    +6
                    Детали реализации общения через WebSocket для целей статьи несущественны.

                    Но тогда вообще лучше про WS ничего не говорить. А то получается «Возьмем кое-что и не будем использовать его как надо».

                    Оба конца должны быть уверены, что коннекшен еще жив.

                    В том-то и дело, что в случае использования WS оба уверены. На обеих сторонах есть события on[connect,disconnect] и т.п. И если где-то отвалилось соединение, вторая сторона обязательно об этом узнает.

                    то надо всех клиентов как-то идентифицировать

                    Это уже вопрос реализации. В любом случае, на это есть средства. Когда вы на сервере принимаете новый WS-коннект, вы можете идентифицировать пользователя (как именно идентификация будет выполняться — это уже вопрос конечного проекта). А когда хотите отправить кому-то персонально сообщение, ищете уже в общем пуле соединений те, которые персонализированы по нужному пользователю.

                    Но у меня делается иначе: у меня для разных сущностей есть разные подписки, например «Подписываюсь на сообщения, созданные для меня или в группах, где я участвую». И когда создается сущность, соответствующая этим критериям, я получаю в браузер уведомление.
                      –1
                      В том-то и дело, что в случае использования WS оба уверены. На обеих сторонах есть события on[connect,disconnect] и т.п

                      Вы меня недопонял, кмк. На низком уровне как реализован Persistent Connection? Как две программы на двух разных компьютерах определяют, что коннекшен ещё жив?
                      Все мои комментарии были о том, как технически это может быть реализовано.
                      WS — это же не UDP.
                      Мне кажется интересным разобраться в тех. деталях коннекшена через WS и конкретно SignalR.


                      И кстати вам должно быть известно, что SignalR перед установкой коннекта пробует несколько протоколов (в зависимости от поддержки на стороне сервера). Оттого может зависеть, какой подход выгоднее.

                        0
                        Вы меня недопонял, кмк. На низком уровне как реализован Persistent Connection? Как две программы на двух разных компьютерах определяют, что коннекшен ещё жив?

                        Я не уверен, правильно ли понял вопрос, но обычно это реализуют браузеры
                        https://html.spec.whatwg.org/multipage/web-sockets.html#ping-and-pong-frames

                        +1
                        Да, я в таких случаях просто создаю «комнаты» типа «user_114» и «role_admin». И когда пользователь заходит с нескольких устройств, он получит сообщение на все из них. Если же соединения нет, то отправляю сообщения через пуш уведомления (группы те же)

                        Кроме того у каждого вебсокетного соединения есть свой уникальный id, что лучше подойдет для вышеописанного случая: нет необходимости сообщать всем, на какой стадии идет парсинг или импорт.
                          0

                          С комнатой — это годный вариант. Учту на будущее.


                          Так-то я согласен что long polling не лучшее решение.

                    +2
                    Так или иначе, принимающая сторона выспрашивает у сервера новые сообщения.

                    В случае websocket нет, сервер сам может отослать сообщение клиенту, так как это обычное tcp/ip соединение.
                    Просто зачем тогда вам вообще websocket? преимущество его именно в двусторонней связи. Вы таким методом можете с успехом использовать обычный http.
                    Но в случае с websocket особой разницы нет.

                    Как раз есть, в вашем случае вы подключили websocket и используете его как http.
                    либо этот опрос делает за вас криво написанная либа для первого SignalR

                    Либа здесь не причем, те сообщения, которые вы можете видеть в логе, обычная реализация протокола браузером, и это как раз проверяющие пакеты туда-сюда-обратно. Даже первый SignalR использует доступный способ передачи в данном браузере, и если WebSocket реализован, будет использовать его, и вся имплементация лежит на браузере. Никакого опроса на новые сообщения в SignalR при использовании WebSocket я не видел.
                +2

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


                1. Вы начинаете показывать альфа версию, когда уже готов бэк, если предположения фронта были не правильные или требуются переделки, то работа по бэку была в стол, почему бы не показывать фронт когда он на тестовых данных? и собирая фидбэк вносить корректировки.
                2. У вас есть спецификация (я не о формате ответов от апи) по фронту и e2e тесты, смысл держать спеку, ведь e2e тесты покажут насколько фронт соответствует тому что ожидается, я бы предложил делать e2e тесты раньше фиксируя ожидаемое поведение и фиксируя спеку, прошел e2e соответствуешь спеке.
                3. Ну и самое забавное, вы предлагаете разбивать задачи на большое число веток (ничего против веток не имею и git с ними справится) но ваш профит в том что придется решать меньше конфликтов, но теперь представьте пока вы фичу делаете в мастер влили другую фитчу(другая команда) и возникли конфликты (это нормально), вы делаете rebase или merge для import ветки разрешаете их, потом другие разработчики вливают к себе import ветку и тд, а теперь другая ситуация, вы собрали ветку import влили все ветки фронта и бэка и возникли конфликты при вливание вашей мега большой ветки в мастер (или дев) и теперь все участники должны быть чтобы по решать конфликты (причем двух больших веток что вливали).
                  Я бы предложил отдельные ветки разработчиков с частями фитчи import, сразу вливать в мастер (или дев, она же не релизит это).
                4. Прочитал про количество ролей что ожидается использовать: фронтовики, бэкендеры, редактор спецификации, мейнтенеры ветки фичи, уверены что последние 2 нужны? может я еще кого то забыл.

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


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

                  0
                  1. Последние две роли — эпизодические. Мэйнейнер сам может быть девелопером части функционала (ему же не надо постоянно сидеть без дела и ждать чужих ПР). То же и с редактором спецификаций. Просто дано конкретное имя для временной функции.
                  2. Не уверен, что смогу ответить на все, т.к. без примера детали проблемы трудно понять.
                    3.1 Если мы пилим новую фичу, то с очень маленькой вероятностью будет конфликт (физически под новую фичу отводится новый каталог). Конфликт возможен, если будет запрет на глубину вложенности субкомпонентов реакт и потребуется создать каталог для субкомпонента на одном уровне с компонентами от других команд. Эта проблема может быть решена именованием субкомпонента (например, вместо PageFooter назвать компонент ImportPageFooter). Просто избежать коллизии имен.

                  потом другие разработчики вливают к себе import ветку и тд

                  Не понятно каких конкретно вы имеете в виду разработчиков… Другой команды или моей же.
                  В любом случае, import-dev ребейзится или мержится в мастер по завершению работы над MVP. После аппрува ПР и вливания в мастер все временные ветки моей команды могут быть спокойно удалены. Конфликты с другими командами перед мержем в мастер пофикшены и ПР принят. Дальше начинается обычная поддержка (багофиксы и тп). Разработчики из моей команды просто переключаются на мастер.


                  Я бы предложил отдельные ветки разработчиков с частями фитчи import, сразу вливать в мастер (или дев, она же не релизит это).

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


                  1. e2e на самом деле у нас и пишутся в параллели, но идет это дело долго и поэтому все равно они будут готовы позже готовности прототипа UI.


                  2. Именно так и описано как вы написали. Читайте в разделе про CI последний абзац.


                    0
                    e2e на самом деле у нас и пишутся в параллели, но идет это дело долго и поэтому все равно они будут готовы позже готовности прототипа UI.

                    получается e2e остается для того чтобы обнаруживать регресс?


                    Не понятно каких конкретно вы имеете в виду разработчиков

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


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


                    master ----------------------- >
                      └ feature/import-dev ------- >
                        ├ feature/import-head ---- >
                        ├ feature/import-filter -- >
                        ├ feature/import-table --- >
                        ├ feature/import-pager --- >
                        └ feature/import-footer -- >

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

                    Так у вас же спринты, вы в начале спринта делаете ветку спринт№123 и она становится dev веткой, а когда спринт заканчивается вся ветка вливается в мастер и релиз (ну так по обычному git flow).


                    Если вы делаете отдельные фитч ветки вроде import и тд и потом по мере готовности вливаете в master и делаете сразу релиз, то у вас получается github flow и зачем вам использовать спринты? делайте релизы по мере готовности без привязки к спринту.


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

                      0
                      разработчикам работающим над импортом придется учитывать и влитые эти задачи и при merge или rebase могут возникнуть конфликты, далее разработчики что не влили в ветку импорта, придется делать тоже самое (мержить или ребейзить свои правки) на актуальные данные.

                      Ветка import-dev в мастер будет сердиться только когда есть уверенность в ее стабильности (проверена qa и дана резолюция, что критических багов нет). К этому моменту все атомарные ветки девелоперов уже должны быть влиты в dev. Как я писал ранее. Дальше будет ребейз или мерж двойной. Почти наверняка будет конфликт в yarn.lock + быть может в package.json. других конфликтов с чужими мержами в мастер не должно возникнуть, если не правили общие компоненты.

                        0

                        вы увидели что ветка import отстает от master
                        в master есть что то очень важное
                        ветка import прошла qa и что касается фичи все ок, но есть конфликты с мастером и надо убедится что не будет регресов после внивания в мастер, так получилось что общие компоненты правили.
                        теперь когда изменения из master будут вливаться возник конфликт, как определить кому их разрешать? у вас в ветке знания размазаны по различным ролям (куча людей) и та ветка что влита была тоже знания размазаны по куче людей, как убедится что разрешение конфлитка ничего не сломало?

                          0

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

                        +1
                        Так у вас же спринты, вы в начале спринта делаете ветку спринт№123 и она становится dev веткой, а когда спринт заканчивается вся ветка вливается в мастер и релиз (ну так по обычному git flow).

                        Гит флоу и расписание спринта разных команд — это совсем разные темы. Кроме того гит флоу — это рекомендация и всегда подлежит адаптации под конкретную команду. Не всем заходит гит флоу в полной реализации.

                          0

                          так вы пытаетесь выдавить скорость из разработки, паралеля все что можно, но при этом все равно привязаны к сроку спринта в 7 или 14 или сколько там у вас он идет и фидбэк будет собран не раньше его окончания, а если фидбэк будет собран раньше то зачем спринты? и релизы вне спринта.


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


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

                            +2
                            так вы пытаетесь выдавить скорость из разработки, паралеля все что можно, но при этом все равно привязаны к сроку спринта в 7 или 14 или сколько там у вас он идет и фидбэк будет собран не раньше его окончания, а если фидбэк будет собран раньше то зачем спринты? и релизы вне спринта.

                            На тестовую среду можно доставлять из ветки dev. Показать прототип PO или ещё какому-нибудь внутреннему пользователю можно значительно раньше, не синхронизируясь с циклами разработки других команд.

                              +1
                              я вижу разбиение задач и их планирование у вас как в waterfall, но при этом аргументируете это гибкой разработкой и скоростью

                              Гибкие методологии разве требуют избавиться от планирования?
                              Спринт планируется. Базовая спека от PO насыщается деталями аналитика в команде или самими разработчиками. В пределах спринта (по скраму если) всеми силами нужно придерживаться плана на спринт. Без планирования в аджайл получаем канбан, в котором команда просто отрабатывает прилетающие в реалтайме карточки на доске (конвейер).

                                0
                                Гибкие методологии разве требуют избавиться от планирования?

                                я этого не говорил.


                                Помните в самом начале написал мысль о том что "9 женщин не смогут родить ребенка за 1 месяц" если переводить мысль более подробно, то вы пытаетесь большое число сотрудников (ролей) задействовать в разработке ОДНОЙ фичи и пытаетесь в рамках этой фитчи ее оптимизировать чтобы стартовать скажем так подзадачи параллельно. При этом большое число ролей работающих над одной фитчей должны между собой комуницировать и шарить знания, что тоже как бы затратно.


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


                                За счет меньшей команды у них будет меньше комуникаций между ролями и больше времени на работу, пусть они там в 2 или 3 сделают фитчу чуть позже, чем как вы указали в 6 или больше, хотя разница во времени будет небольшой, при этом другие 2 — 3 человека смогут взять и делать другую фитчу для бизнеса паралельно и к концу спринта (плюс например неделя вы получите уже две фичи вместо одной, неделя это цифра с потолка).


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


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

                                  +1

                                  я вас понимаю… главная цель в контексте статьи — при фиксированном составе команды максимально ускорить разработку конкретной фичи при минимизации рисков больших переделок.


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

                            0
                            точно также и про процесс разработки, чем он проще и легче, тем больше времени на разработку, это лишь мое имхо возможно я ошибаюсь.

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

                          0

                          E2e тесты покажут, что код им соответствует, а не что код соответствует ожиданиям. Без человекочитаемой спецификации, заапрувленной ПО или стейкхолдером, откуда брать ожидания? Как контролировать отсутствие ошибок в тестах? Откуда вообще могут появиться баги?

                            0

                            E2E отражают ожидания(требования).


                            если меняются ожидания то правят и E2E, после правки E2E проект может перестать им соответствовать.


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


                            Как контролировать отсутствие ошибок в тестах?

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


                            Откуда вообще могут появиться баги?

                            Философский вопрос, хоть откуда если коротко.

                              0

                              Как вы проверите, что е2е отражают ожидания? Запуском системы в эксплуатацию и вердиктом "это не то, что мы хотели"? Использованием стейкхолдеров в качестве QA, потому что у QA нет никаких спецификаций, на соответствие которым проверять систему.

                          0
                          Больше 8 лет был чисто бэк-разработчиком. Но последние 2+ года fullstack (с php перешел на js + node-js). Постоянно много идей и всяких черновиков проектов. Практически всегда начинал с бэка (база данных, API и т.п.). Всегда считал, что если бэк не позволит правильно хранить и обрабатывать информацию, то и на фронте ничего не получится. Но в последнее время все больше и больше прихожу к тому, что если делать конечный продукт для пользователей, который они должны открыть в браузере и выполнять какие-то свои задачи, то без правильной реализации фронта все будет бессмысленно. По этой причине не могу начать реализацию очередной своей идеи. Она весьма нестандартная и я все никак не могу окончательно придумать как же будут выглядеть интерфейсы для нее. А без интерфейсов ничего в итоге работать не будет.

                          Поэтому лично я категорически согласен с содержимым статьи. + в статью и карму.
                            0
                            Точно такая же проблема и у меня сейчас) Тоже пришел к тому, что сначала хорошо бы понимать как всё будет выглядеть. В итоге не знаю что делать, то ли долбиться в эту стену, то ли изучить web-дизайн.
                              0
                              Дизайн я вряд ли осилю…
                                0
                                Когда-то «заставили стать временным» дизайнером, пока не найдут нового. Думал справлюсь за пару недель, посмотрю видосы на ютубе и на этом всё. Ага. Затянуло на полтора года, так понравилось.

                                Как оказалось дизайн в контексте web-интерфейсов — это не столько про «иллюстративно-художественную» часть, сколько про решение проблем бизнеса с помощью интерфейсов (один из переводов слова дизайн — проектирование).

                                Могу сказать с позиции фронтенд разработчика, что это одни из самых ценных приобретённых знаний. Уровень «страданий и стресса» при взаимодействии с постановщиками задач и с бекендом сократился на порядки.

                                Основы дизайна (ещё раз, не художественного, а именно как проектирование), развивают чуйку и умение задавать правильные точные вопросы. С достаточно большим процентом точности можно предсказать куда будет расти «простоя задача». Т.е. что в задаче есть «вода» и неопределённые требования (поэтому на продумывание можно забить и сделать «в лоб»). А что есть ограничения на всё время жизни проекта (как правило скрытые, которые нужно «найти» между строк среди потока мыслей постановщика задачи), соответственно, нужно продумывать более надёжную фронт-архитектуру.

                                Очень мило иногда получалось, когда фулл-стек-синьоры делали какой-то параллельный проект. А после сдачи ко мне подходит начальник IT и говорит сделай по нормальному..) и можешь сам ставить задачи этим разрабам. И ещё куча подобных кейсов.

                                Также заметил минусы обладания основами дизайна:
                                — 80% дизайнеров теперь кажутся просто «операторами фотошопа»;
                                — 99% digital-маркетологов теперь кажутся кхм… «плохими людьми»;
                                — меньше пишешь кода, больше разговариваешь, часто зовут на митинги с топ-ами при обсуждении их новых чудо-идей;
                                — могут переманить в «менеджеры»;
                                — дольше делаешь вёрстку (и в целом фронт) чем раньше;
                                  0
                                  Ого, классный опыт, спасибо что поделились!
                                  А есть советы относительно того, как всё таки наработать хотя бы начальные навыки создания интерфейсов. Именно в механическом смысле. Я вот в голове могу представить, но когда начинаю делать, сразу тупняк жесткий наступает))
                                    +1
                                    Это не тупняк. Просто проектирование интерфейса — это тоже отдельная работа, которая требует времени. Сходу продумать не получится. Т.е., имхо, ноги ростут от недооценивания другого вида работ… как бек недооценивает фронт (та что там формочку набросать), так и фронт недооценивает дизайн (та что там формочку нарисовать).

                                    Что касается советов для появления «автоматических/механических» навыков, то сходу вспоминается следующие:
                                    • Каждый раз проектируя конкретный экран/интерфейс, необходимо задавать себе вопрос — “Что сейчас важно для пользователя?”, “Что я хочу показать пользователю в данный момент?”, чтобы понимать как спроектировать и распределить внимание в конкретном случае.
                                    • При проработке пользовательского сценария — научиться/натренироваться мыслить экранами для достижения цели пользователем. Я обычно на большом листе бумаги делаю наброски всех экранов всех возможных состояний конкретного сценария. Это кстати помогает более точно и быстро сформировать иерархию компонентов и их названия (в случае react-а допустим)
                                    • Не заставлять думать или много читать пользователя. Он должен сфокусироваться на том что хочет (цель), а не на том что он делает (автоматизм, интерфейс должен “исчезнуть” Идеальный интерфейс — это не кнопка «сделать всё хорошо», идеальный интерфейс — это его отсутствие, когда уже всё хорошо.)).
                                    • Не вводить в заблуждение пользователя — каждая ссылка или кнопка должна чётко отвечать на вопрос что произойдёт дальше, без всяких двусмысленностей. Не «Ок/Отмена», а «Да, сохранить / не сохранять». Но это больше тема интерфейсных текстов — отдельный пласт знаний. Гуглить Максим Ильяхов.
                                    • Фокус внимания пользователя ограничен (например, 10% внимания на ваш интерфейс, 90% на управление авто). Поэтому чем меньше плотность информации на еденицу площади — тем лучше.
                                    • Каждый блок может содержать не более 5-7-9 элементов (правило 7 ± 2). Если элементов больше — нужно их разгруппировать.
                                    • На одном экране — один акцент (читай, одна главная cta-кнопка, например.). Если акцентов больше — то само понятие акцента исчезает.
                                    • Правило внутреннего и внешнего. Это, например, есть несколько списков на одном экране. Так вот, расстояние между ul должно быть больше расстояния между li.
                                    • Если уже есть какой-то интерфейс и нужно его как-то улучшить — то в первую очередь лучше думать что убрать, а не чего бы ещё добавить.
                                    • Ссылка — это объект (существительное), а кнопка — это действие (глагол, «отправить», «выйти», «заказать» т.д.)

                                    Понятное дело, это не догмы.
                                0

                                А зачем изучать дизайн, чтоб понимать как будет выглядеть. Сделайте на каком-нибудь UI фреймворке, чтобі совсем страшно не выглядело и определитесь с тем, что должно быть на странице/экране/форме, чтобы было удобно пользоваться функционально. А красиво уже потом как-нибудь, если будут ресурсы.

                              +3
                              Какой-то оверинжиниринг на пустом месте.
                              Почему, для создания прототипа одной страницы нужно 5 фронтендеров?
                                +1
                                Два варианта. Либо надо за пару суток, либо — … ну, такие фронтэндеры в конторе.
                                  0

                                  Табличку и 2 кнопки для прототипа 5 фронтеров за 2 суток? Фигасе…

                                  +1

                                  Ну можно было подобрать пример и посолиднее. Просто увеличилось бы количество воды.

                                    +1

                                    Чтобы сделать прототип в, например, 3 раза быстрее чем может сделать один

                                      0

                                      Увеличением числа разработчиков быстрее прототип не сделать. Стандартная же история про 9 женщин рожающих ребенка за один месяц.


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

                                        +1

                                        Я ж не говорю, что в 5 раз быстрее 5 разработчиков сделают чем один, но раза в 3 можно сделать. И не прототип сделать в 3 раза быстре, а MVP — готовое к продакшену решение в рамках существующей системы, предположительно строго следующей в том числе принципам модульной архитектуры.

                                    +2
                                    Если есть понимание что надо делать — понятна схема данных, UX нарисован — то накидать таблички и прикрутить API на беке — дел на полчаса. Бек может оставить где-то заглушки себе, и в параллельно пилить.

                                    Если еще и GraphQL — то куча всего переиспользуется, и особо думать даже не надо как API строить. Плюс есть еще BaaS-сервисы — через UI накидал схему данных, оно тебе API, и вообще бекендеров не надо.

                                    Если что надо делать непонятно — то и фронт, и бек пока рано писать. Потому что фигня получится под переделку. В этом случае надо прототипировать на картинках, уточнять требования, придумывать схему данных.

                                    Ситуации когда фронту нечем заняться пока бек что-то там делает, могут вызваны следующими причинами:
                                    1. бек натащил паттернов, навернул лишнего, и теперь банальное «сделать API для таблички» — это уже для них сложно. Типичная сейчас история, кстати.
                                    2. архитекторы, аналитики и UXD — перегружены разгребанием текучки, и не планируют наперёд
                                    3. вообще нет планирования заранее и наперёд, едем в стиле злого аджайла — «ща чего-нибудь нафигачим, а дальше всё пять раз перепишем».

                                    Короче лечить надо причину, а не следствие.

                                      0
                                      Через файловый API браузера просим пользователя выбрать нужный файл с диска.


                                      Если для экспорта из 1с то это уже хреновая реализация

                                      Через AJAX отправляем файл в бэкенд.


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

                                      Ожидаем окончания валидации и распарсивания файла с данными (опрашиваем статус фоновой операции через Websocket).


                                      Long Polling через сокеты? А зачем? Прочитал переписку выше и все еще не понял.

                                      По окончании валидации грузим подготовленные к импорту данные и рендерим их в таблице на странице импорта.


                                      Стопиццот дофигаллиардов позиций, ага))

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

                                      И делаем выгрузку из 1с полностью неконсистентной.

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

                                      Вы еще пропустили пункт разрешения коллизий.
                                      А может быть импорт должен быть одной транзакцией?
                                      В каком виде вы собираетесь показывать результаты и что с ними потом можно сделать?

                                        0
                                        Стопиццот дофигаллиардов позиций, ага))

                                        Пейджер естественно.

                                          0

                                          А чо ждать тогда? Первые 30 строк в файлике распарсил и отправил ответ на фронт.

                                            0

                                            Про csv это к примеру было. Для простоты. Может быть и другой формат (xlsx напр.)

                                              0

                                              Ну + полчаса, из-за того что библиотека парсящая xls сделана по мудацки

                                                0

                                                я про то, что частями слать не получится… или есть либа для парсинга XLSX в JS?

                                                  +1
                                                    0

                                                    Спасибо! Надо будет поизучать на досуге.

                                                    0

                                                    Еще раз, мы говорим про минимальную херь для презентации. Слать реактивно кусками многопоточно в очередь в извращенных форматах можно доделывать потом до посинения. Ия не вижу разницы долбить csv файл или xlsx на куски. Единственное csv каждый кусок можно читать отдельно наверно. И я думаю что ли бы для js есть какие угодно либы)) это самый популярный язык проганья.

                                                      0

                                                      Как я понимаю, речь не о минимальной хере для презентации, а о минимальной хере для продакшена. Не о прототипе, а о MVP.

                                              0
                                              А вы сами хоть раз пытались работать с таблицей на 1000 строк разбитую по 20 строк на страничку? То еще адище я вам скажу
                                                0

                                                Конечно, имел опыт.
                                                К пейджеру обязательно поисковик и фильтр по некоторым полям. Это стандартные вещи UX.

                                                  0
                                                  И в случае 100500 позиций как правило это все еще неюзабельно
                                                    0

                                                    "Критикуешь — предлагай альтернативу"

                                                      0
                                                      Идеального рецепта пока не нашел. В некоторых случаях хорошо работают сворачиваемые по группам строки таблицы, в других отображение всех результатов за раз, в третьих разбиения таблицы на несколько с вкладками. Но отображение 10+ страниц это последнее, что стоит делать
                                                        0

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

                                              0

                                              Ээээ… Задача стоит делать две кнопки "импорт csv" И "подтвердить импорт" и табличку на фронте, которая рендерит csv с возможностью редактировать строки? Помню такую херь до обеда делал на прод. Если задача сделать минимально работающий прототип, то реально можно в одну будку сделать за день и бек и фронт. Потом уж прикручивать вебсокеты, css, ActiveMq. Вебсокеты вы как то не так юзаете, кстати… Про дизайн не понял, если проект не с нуля, то компоненты(кнопка, таблица) уже должны существовать с дизайном, нет?..

                                                0
                                                По моему мнению надо сначала приготовить и проверить бэк. Все это распределение, что раньше напоминает анекдот:
                                                Закончилась посадка на суперлайнер ИЛ-2086. В салон выходит стюардесса:
                                                Дамы и господа, для того, чтобы помочь вам скоротать время полета,
                                                на борту нашего лайнера имеются библиотека, кинозал, три бара,
                                                ресторан, бассейн и два теннисных корта. А теперь я попрошу вас
                                                пристегнуть ремни безопасности, потому что сейчас вместе со всей этой
                                                финей мы попытаемся взлететь!
                                                  +1
                                                  На мой взгляд, написано сложно о простом. Проблема высосана из пальца.
                                                    0

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

                                                    –1
                                                    Для упрощения взаимодействия фронта и бэка (в т.ч. для распараллеливания работы) можно генерировать по заданной спецификации и клиентский код для фронта, и код (контроллеры) на бэкеде. Мне в этом очень помогает Unchase OpenAPI (Swagger) Connected Service.
                                                    Как ей пользоваться можно прочитать здесь.

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