Выбираем лучший бэкенд-фреймворк 2021 года

Dependency Injection. Adopt

Мы рекомендуем использовать паттерн DI и вместе с ним IoC-контейнеры. Кажется, что DI не сильно распространен во фронтенд-разработке за пределами Angular, но у нас в компании этот паттерн получил широкий охват, в том числе на проектах с React, где мы используем собственный фреймворк Tramvai.js, который построен на DI.

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

Нужно понимать, что DI повышает порог входа в проект. В некоторых случаях, особенно если у вас множество простых и уникальных задач, когда меньше возможности что-либо переиспользовать, смысл использовать DI уменьшается.

Наш коллега Сергей Нестеров сделал доклад Dependency injection в React-приложении — советуем посмотреть.

Требования к сотрудникам

Бэкэндеры тесно взаимодействуют с фронтенд-разработчиками (frontend developer) и другими специалистами, работающими над проектом, поэтому любая допущенная ошибка приведёт к остановке работы всей команды. Такая особенность профессии заставляет предъявлять к её представителям самые строгие требования.

Главные требования:

  • умение работать с различными языками программирования (например, PHP, C++, Java, Python и другие);
  • знание API, веб-фреймворка и основ фронтенда;
  • понимание правил безопасности в сети и умение применять их на практике;
  • наличие навыков создания запросов к базам данных;
  • владение принципами функционирования HTTP;
  • обладание навыком создания и оценивания технического задания;
  • знание перевода основных терминов (с английского языка).

Какая зарплата у backend-разработчика?

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

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

  • Стажер даже в самом начале карьеры получает в среднем 30-40 тыс. руб.;
  • Младший разработчик переходит на зарплату около 60 тыс. руб.;
  • Миддлам платят около 100 тыс. руб.;
  • Старший специалист получает 150-180 тыс. руб.;
  • Тимлид может рассчитывать на зарплаты свыше 200 тыс. руб.

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

Крупные корпорации и зарубежные компании предлагают зарплату от 300 до 500 тыс. в пересчете на рубли.

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

Менеджер зависимостей

Долго писать не буду, а сразу скажу, что это без сомнений Glide. Если вы работали с gradle или maven, то вам наверняка знакома парадигма объявлений зависимостей в неком файле с последующим их задействованием по необходимости. Так вот Glide — это хомячий Gradle, с решением конфликтов и прочими плюшками.

Кстати, если у вас возникнут проблемы при тестировании, когда go test лезет в папку vendor, жадно тестируя каждую либу, то проблема решается элементарно:

Этот параметр исключает папку vendor из тестирования. В сам репозиторий достаточно положить glide.yaml и glide.lock файлы.

Мобильной разработке это все никак не поможет, но просто, чтобы вы знали)

Причины перехода на Go

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

Не хочу сказать ничего плохого о Spring и Java в целом, это удивительный инструмент под серьезные задачи, как огромный толстопузый испанский галеон. А мы искали что-то больше похожее на легковесный пиратский клипер.

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

У Go такой проблемы нет. От слова совсем. Иногда даже через слишком: сидишь, ищешь идеальное решение, а StackOverflow тебе на это отвечает: ‘Ну да, просто циклом for, а ты чего ждал?’

Со временем к этому привыкаешь и перестаешь гуглить всякие мелочи по пустякам, а начинаешь включать голову и просто писать код.

Как стать frontend-разработчиком?

Чему учиться?

Программисты со стажем немного лукавят, когда говорят о низком пороге входа в профессию frontend-разработчика. Под этим обычно подразумевается легкость изучения базовых технологий, связанных с версткой (HTML и CSS), и начальных навыков оживления веб-страниц с помощью плагинов и библиотек. Но в 2021 году это лишь малая часть того, что должен знать и уметь фронтендер.

«В 2017 году я устроился на свою первую работу, зная лишь HTML, CSS, немного JavaScript и JQuery, — рассказывает Алексей Видякин. — Сегодня, в 2021 году, требования очень выросли, поскольку выросла конкуренция. Базовыми знаниями верстки уже никого не удивишь».

Вот примерный список требований к джуниор-специалисту в 2021 году:

Знать HTML и CSS. Под этим подразумеваются навыки кроссбраузерной и адаптивной верстки, знание популярных CSS-фреймворков, препроцессоров и HTML-шаблонизаторов.

Знать JavaScript, в частности стандарт Ecmascript 6 — спецификацию 2015 года, принесшую языку новые элементы синтаксиса и новый уровень производительности.

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

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

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

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

Базово знать один из современных фреймворков: React, Angular или Vue.js

С их помощью разработчик может минимизировать количество обращений к DOM (Document Object Model — объектная модель документа) и организовать мгновенный обмен данными с сервером через API. Вместо того чтобы по каждому клику перезагружать страницу целиком, фреймворк управляет состоянием ее отдельных компонентов, обеспечивая пользователю мгновенный отклик приложения. Подразумевается, что если человек сумел освоить один из них, то сможет достаточно быстро разобраться с другим, если возникнет необходимость. Есть довольно много вакансий, где требуется какой-то конкретный фреймворк.

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

Где начать работать?

Существует три основных варианта трудоустройства: фриланс, студия веб-разработки и работа на стороне заказчика.

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

«На позиции trainee (стажера) я выполнял ту работу, за которую не хотели браться более опытные сотрудники, — вспоминает Алексей. — В основном это были правки от заказчика, то есть дополнения на сайте, которые нужно просто внести по определенному шаблону, ничего не поломав при этом. Дополнительная ценность такой работы в том, что ты начинаешь понимать, как устроены реальные проекты именно в вашей студии».

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

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

Траектории того, как приходят во frontend, разные. Читайте историю Марка Соболева, который служил в полиции, а теперь разрабатывает образовательные сервисы.

Что должен знать и уметь специалист back-end?

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

Hard skills или рабочие навыки, которые непосредственно нужны для решения тех или иных задач. Сюда можно отнести:

  • знание языков программирования – PHP, Python, Ruby, Java, Go и/или другие;
  • умение работать с базами данных, знание системы управления базами данных MySQL;
  • умение применять фреймворки и паттерны программирования Ruby on Rails, Yii, Django и/или другие;
  • знание алгоритмов работы.

Soft skills или неспециализированные навыки, которые необходимы для успешного прохождения собеседований и устройства на работу, быстрого вливания в коллектив и карьерного роста:

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

Как строится работа над проектом

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

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

Руководитель сервиса отвечает за эти задачи — он объясняет всем, что сейчас важно делать и почему. Обычно бэкендер отвечает за конкретный кусочек продукта, с которым надо делать что-то разумное

Например, ускорять его», — говорит Алексей Шаграев из Яндекс.Поиска.

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

«Могу рассказать на примере команды Яндекс.Практикума. Я как заказчик для бэкенда говорю: «Нам нужно, чтобы платформа умела делать то-то и то-то. Например, чтобы я вводила код в окошко, а платформа мне что-то отвечала». Дальше мы садимся с бэкендом и обсуждаем задачу: что нужно сделать, как это реализовать, какие есть ограничения. Мы можем выбирать решение вместе, потому что я и моя команда понимаем в разработке. Иначе у бэкендеров была бы дополнительная задача — объяснить нам как заказчикам особенности каждого решения», — рассказывает София Техажева.

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

Если проект начинают с нуля, то шаги для бэкендера будут такими:

  • Выбрать язык. Python, C++, Java, PHP — это основные языки, на которых пишут бэкенд. Так, на Java написано большинство банковских систем — этот язык используют в Райффайзенбанке и Сбербанке. На PHP создана сеть Badoo и часть сервиса ВКонтакте. Чаще всего это исторически обусловленные решения. Когда в компании уже пять лет пишут на Java, сложно переводить огромный массив кода на другой язык.
  • Выбрать инструменты. Например, базу данных или фреймворк. Набор этих инструментов плюс языки, которые бэкендер выбирает, чтобы строить свой двигатель, все вместе называются стек технологий. Стеки бывают разные, в зависимости от задач и традиций компании.
  • Написать код. Здесь бэкендер пользуется инструментами для создания версий, тестирования и хранения данных.

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

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

«В больших компаниях бэкендер растет так: вначале он действует как механик — что-то чинит, вставляет новые части. А обучение и опыт приводят к тому, что он начинает проектировать новые структуры самостоятельно», — объясняет София Техажева.

Например, вы отправляете запрос, а ответа нет или выпадает бессмысленный текст — значит, с сервисом что-то не так. Когда пользователь видит ошибку, он редко может определить, где она случилась — во фронтенде или в бэкенде. Но если происходит сбой на сервере, то система выдает соответствующие сообщения (Error 503 Backend fetch failed и другие).

Суть деятельности backend-разработчика

Как известно, любые интернет-сервисы, сайты и мобильные приложения включают в себя две составляющие: backend и frontend:

  • Первая часть – выполняемый на серверной стороне код, откуда загружается сайт или веб-сервис. Допустим, вы написали в поисковой строке запрос. Он попадает на сервер. Который ищет информацию в БД и отдает ответ на запрос. Как раз-таки процесс поиска и создания ответа из БД – это и есть backend-часть ПС.
  • Вторая часть – исполняемый в браузере код. Иными словами, фронтенд-разработчик отвечает за отображение сайта на разных устройствах (ПК, ноутбук, планшет, смартфон и т.п.), работоспособность форм и кнопок.

Как стать back end разработчиком? Что нужно знать и уметь?

Несмотря на то, что backend предполагает работу только с серверной частью, начать путь в профессию лучше с изучения основ HTML и CSS. Далее – осваивать серверные языки программирования, работу с базами данных.

Посмотрите интересное видео на тему, чем отличается backend разработчик от frontend и какие знания необходимы для работы в этой сфере:

Список того, что нужно знать и уметь бэканд разработчику, может включать:

  1. Языки программирования, например, PHP, Ruby, Python, Java, JavaScript / Node.js. PHP – самый популярный язык серверного программирования.

  2. Технологию AJAX, позволяющую обновлять данные в браузере без перезагрузки страницы.

  3. Базы данных MySQL, PostgreSQL, MongoDB и другие.

  4. Принципы работы UNIX-систем.

  5. Принципы ООП (объектно-ориентированное программирование).

  6. Фреймворки – наборы скриптов, ускоряющих разработку веб-сайтов.

  7. Работу с системами контроля версий Git.

Что такое backend?

Бэкенд (англ. back-end) — это программно-аппаратная часть сервиса. Это набор средств, с помощью которых происходит реализация логики веб-сайта. Это то, что скрыто от наших глаз, т. е. происходит вне компьютера и браузера.

Как только вы введёте запрос на странице поисковика и нажмёте клавишу «Ввод», frontend закончится и начнётся backend. Ваш запрос отправится на сервер Яндекса или Google, т. е. по месту расположения алгоритмов поиска. Именно там и происходит вся «магия». Но вот, на мониторе появляются данные, которые вы запрашивали, — это происходит возвращение во frontend.

Также можно сказать, что backend — это процесс объединения пользователя с сервером.

Что касается backend-разработчика, то он использует любые инструменты, которые доступны на его сервере. На практике программисты применяют любой из универсальных языков: PHP, Ruby, Python, Java. Кроме того, при backend-разработке задействуются СУБД (MySQL, PostgreSQL, SQLite, MongoDB и пр.).

В зависимости от особенностей продукта меняются и обязанности backend-разработчика.

Бэкенд-разработчик — «боец невидимого фронта»

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

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

Основные инструменты бэкенд-разработчика — серверные языки программирования. В целом в работе специалист использует разные инструменты:

  • языки PHP, Python, Ruby, Java, C#, Node.js (программная платформа);
  • дополнительно к Node.js полезно изучить Express (библиотека для взаимодействия платформы Node.js с сервером) и Mongo DB (базу данных для получения и хранения информации);
  • в качестве дополнительных средств применяются фреймворки Laravel, Symfony, CodeIgniter, Django, Flask, Ruby on Rails, Spring, Express.
  • для хранения данных используются MySQL, PostgreSQL, SQLite. 

Язык программирования JavaScript (JS)

Скриптовый язык, который отвечает за динамику интернет-страницы и пользовательскую логику. JavaScript “вдыхает жизнь” в сверстанный сайт, реализуя его динамическое поведение. На сегодняшний день, помимо данной функции, JS широко используют в создании серверных приложений, игровых и мобильных приложений, а также для разработки сценариев для организации автоматизированного тестирования и прочего.

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

Bootstrap 4

Это пользующаяся популярностью платформа, которая способствует разработке адаптивных веб-приложений. Bootstrap используют для построения сайтов и интерфейсов администраторских панелей. Среди плюсов данной платформы:

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

С изучением Bootstrap версии 4 поможет одноименный видео курс на ITVDN.

API

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

Цели для нового API сформировались из ежедневных трудностей в реализации новых продуктовых и дизайнерских идей. Нам были нужны:

  1. Слабая связанность компонентов системы, чтобы бэкенд и фронтенд можно было развивать параллельно.
  2. Высокая масштабируемость, чтобы новый API не мешал наращивать функциональность.
  3. Стабильность и согласованность.

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

Наиболее распространены разного рода REST API. В последние годы к ним добавляют описательные модели через инструменты типа swagger, но нужно понимать, что это тот же REST. И, по сути, его главный плюс и в то же время минус — это правила, которые носят исключительно описательный характер. То есть никто не запрещает создателю такого API отклоняться от постулатов REST при реализации отдельных частей.

Другим распространённым решением является GraphQL. Он тоже не идеален, но в отличие от REST, GraphQL API — это не просто описательная модель, а настоящие правила.

Выше я говорил про систему, которая должна была согласовывать работу фронтенда и бэкенда. Прослойка (interlayer) — это именно тот промежуточный уровень. Рассмотрев возможные варианты работы с сервером, мы остановились на GraphQL в качестве API для фронтенда. Но, так как бэкенд написан на C++, то реализация GraphQL-сервера оказалась нетривиальной задачей. Не буду здесь описывать все возникшие сложности и ухищрения, на которые мы шли, чтобы их преодолеть, реального результата это не принесло. Посмотрели на проблему с другой стороны и решили, что простота — залог успеха. Поэтому остановились на проверенных решениях: отдельный Node.js сервер с Express.js и Apollo Server.

Далее нужно было решить, как обращаться к API бэкенда. Сначала смотрели в сторону поднятия REST API, потом пробовали использовать аддоны на C++ для Node.js. В итоге поняли, что это всё нам не подходит, и после подробного анализа для бэкенда выбрали API на базе gRPC-сервисов.

Собрав воедино полученный опыт использования C++, TypeScript, GraphQL и gRPC, мы получили архитектуру приложения, позволяющую гибко развивать бэкенд и фронтенд, продолжая при этом создавать единый программный продукт.

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

Общая схема работы после изменения архитектуры

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

Предыстория

BILLmanager появился как раз в те времена, когда не было жёсткого разделения по направлениям. Он имел согласованную архитектуру, умел управлять поведением пользователя и его даже можно было расширять плагинами. Шло время, команда развивала продукт, и вроде всё было хорошо, но стали наблюдаться странные явления. К примеру, когда программист занимался бизнес-логикой, он начинал плохо верстать формы, делал их неудобными и сложными для восприятия. Или добавление, казалось бы, простой функциональности отнимало несколько недель: архитектурно модули были жёстко связаны, поэтому при изменении одного приходилось корректировать другой.

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

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

Full-stack-разработчик

По данным «Мой круг» full-stack и back-end разработчики в 2018 году получают до 140 тысяч рублей. Front-end – до 125 тысяч.

Может ли один и тот же человек заниматься и фронтенд, и бэкенд разработкой? Если он разбирается и в том, и в другом – почему нет: такой разработчик называется Full Stack.

Он умеет решать любые задачи по разработке веб-сервисов и сайтов: знает серверные языки, JavaScript, HTML и CSS, может оптимизировать сайт под поисковики и превратить сделанный в Photoshop макет дизайна в рабочий интерфейсный код (вы же помните, что интерфейс сайта рисует дизайнер, но на самом сайте всё будет описано кодом?).

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

Это, кстати, очень важно: многие front-end и back-end разработчики просто не понимают друг друга и ставят невыполнимые задачи, а full-stack специалист как раз может стать связующим звеном

Учиться сразу на fullstack нет смысла: обычно программист сперва осваивает одну часть работы – frontend или backend – и уже потом добавляет к багажу знаний навыки по смежному направлению.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector