Что такое клиентская логика
Перейти к содержимому

Что такое клиентская логика

  • автор:

Логики веб-приложений и развитие веба

Вся логика приходится на сервер, лишь малая часть на клиент.
Соотношение объема кода: клиент — 5%, сервер — 95%.
Распределение VC: почти все на сервере.
Примеры:
Веб 1,0 сайты.
Сайты на браузерах без js.
FullAjax сайты (например построенные на xAjax) в которых браузер тупо выполняет все скрипты, пришедшие с сервера.

Равномерный тип.

Равномерное распределение логики.
Соотношение объема кода: клиент — 40%, сервер — 60%.
Распределение VC: вид разделен поровну — клиент может дорисовывать некоторые элементы, присланные сервером (слайдеры, попап окна и т.п.), большая часть контроллера на сервере, клиент отвечает за валидацию форм.
Примеры:
Типичные веб 2,0 сайты со всякими виджетами от Prototype, jQuery, MooTools, ExtJS, YUI…

Не придумал название тип.

бОльшая часть приходится на клиент.
Соотношение объема кода: клиент — 70%, сервер — 30%.
Распределение VC: весь вид находится на клиенте (клиент отрисовывает себя исходя из присланных данных, которых много меньше, чем код, который нарисует клиент), большая часть контроллера тоже на клиенте (вся валидация на клиенте). Клиент не может работать без javascript.
Примеры:
GMail (основной), ну и все продукты Google.

Как мне кажется, сейчас веб-приложения переходят с равномерного типа к типу номер 3, преимущества его очевидны: низкая нагрузка на сервер, динамичность на клиенте.
Из не страшных минусов можно выделить загрузку большого объема скриптов, но выход есть, уже есть — все основные js фреймворки можно сгрузить на 1 сервер, как это сделал гугл, и загружать их раз в день )

На горизонте виден уже 4 тип, поданный гуглом, являющийся модификацией 3 типа — все GoogleGears приложения.

Я считаю, что все сайты останутся на 2 этапе (типе) из-за ограниченных возможностей по индексации javascript-генерируемого контента, хотя гугл вроде бы и это учел =)

Как работают веб-приложения

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

1. Чем веб-приложения отличаются от сайтов

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

Сайты содержат различную статику, которая как и HTML-файл не генерируется на лету. Чаще всего это картинки, CSS-файлы, JS-скрипты, но могут быть и любые другие файлы: mp3, mov, csv, pdf.

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

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

2. Какие бывают веб-приложения

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

  1. Backend (бэкенд или серверная часть приложения) работает на удаленном компьютере, который может находиться где угодно. Она может быть написана на разных языках программирования: PHP, Python, Ruby, C# и других. Если создавать приложение используя только серверную часть, то в результате любых переходов между разделами, отправок форм, обновления данных, сервером будет генерироваться новый HTML-файл и страница в браузере будет перезагружаться.
  2. Frontend (фронтенд или клиентская часть приложения) выполняется в браузере пользователя. Эта часть написана на языке программирования Javascript. Приложение может состоять только из клиентской части, если не требуется хранить данные пользователя дольше одной сессии. Это могут быть, например, фоторедакторы или простые игрушки.
  3. Single page application (SPA или одностраничное приложение). Более интересный вариант, когда используются и бэкенд и фронтенд. С помощью их взаимодействия можно создать приложение, которое будет работать совсем без перезагрузок страницы в браузере. Или в упрощенном варианте, когда переходы между разделами вызывают перезагрузки, но любые действия в разделе обходятся без них.

3. Pyhon-фреймворк Django aka бэкенд

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

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

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

Данные приложения хранятся в базе данных (БД). Чаще всего используются реляционные БД. Это когда есть таблицы с заранее заданными колонками и эти таблицы связаны между собой через одну из колонок.

Данные в БД можно создавать, читать, изменять и удалять. Иногда для обозначения этих действий можно встретить аббревиатуру CRUD (Create Read Update Delete). Для запроса к данным в БД используется специальный язык SQL (structured query language).

В Джанго для работы с БД используются модели (model). Они позволяют описывать таблицы и делать запросы на привычном разработчику питоне, что гораздо удобнее. За это удобство приходится платить: такие запросы медленнее и ограничены в возможностях по сравнению с использованием чистого SQL.

Полученные из БД данные подготавливаются во вью к отправке на фронт. Они могут быть подставлены в шаблон (template) и отправлены в виде HTML-файла. Но в случае одностраничного приложения это происходит всего один раз, когда генерируется HTML-страница, на который подключаются все JS-скрипты. В остальных случаях данные сериализуются и отправляются в JSON-формате.

4. Javascript-фреймворки aka фронтенд

Клиентская часть приложения — это скрипты, написанные на языке программирования Javascript (JS) и исполняемые в браузере пользователя. Раньше вся клиентская логика основывалась на использовании библиотеки JQuery, которая позволяет работать с DOM, анимацией на странице и делать AJAX запросы.

DOM (document object model) — это структура HTML-страницы. Работа с DOM — это поиск, добавление, изменение, перемещеие и удаление HTML-тегов.

AJAX (asynchronous javascript and XML) — это общее название для технологий, которые позволяют делать асинхронные (без перезагрузки страницы) запросы к серверу и обмениваться данными. Так как клиентская и серверная части веб-приложения написаны на разных языках программирования, то для обмена информацией необходимо преобразовывать структуры данных (например, списки и словари), в которых она хранится, в JSON-формат.

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

Сериализация — это преобразование списка или словаря в JSON-строку. Для примера:

Десериализация — это обратное преобразование строки в список или словарь.

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

К счастью, на смену JQuery пришли Javascript-фреймворки: Backbone Marionette, Angular, React, Vue и другие. У них разная философия и синтаксис, но все они позволяют с гораздо большим удобством управлять данными на фронтенде, имеют шаблонизаторы и инструменты для создания навигации между страницами.

HTML-шаблон — это «умная» HTML-страница, в которой вместо конкретных значений используются переменные и доступны различные операторы: if, цикл for и другие. Процесс получения HTML-страницы из шаблона, когда подставляются переменные и применяются операторы, называется рендерингом шаблона.

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

5. Как клиент и сервер общаются между собой

Общение клиента с сервером происходит по протоколу HTTP. Основа этого протокола — это запрос от клиента к серверу и ответ сервера клиенту.

Для запросов обычно используют методы GET, если мы хотим получить данные, и POST, если мы хотим изменить данные. Еще в запросе указывается Host (домен сайта), тело запроса (если это POST-запрос) и много дополнительной технической информации.

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

Есть еще один запрос, который делается перед HTTP. Это DNS (domain name system) запроc. Он нужен для получения ip-адреса, к которому привязан запрашиваемый домен. Эта информация сохраняется в браузере и мы больше не тратим на это время.

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

К сожалению, он этого не умеет. Поэтому используется еще одна программа-прослойка — сервер приложений. Например для приложений на питоне, это могут быть uWSGI или Gunicorn. И вот уже они передают запрос в Джанго.

После того как Джанго обработал запрос, он возвращает ответ c HTML-страницей или данными, и код ответа. Если все хорошо, то код ответа — 200; если страница не найдена, то — 404; если произошла ошибка и сервер не смог обработать запрос, то — 500. Это самые часто встречающиеся коды.

6. Кэширование в веб-приложениях

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

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

  • В Джанго пришел запрос на получение данных для графика в отчете. Мы достаем из БД данные, подготавливаем их и кладем в БД с быстрым доступом, например, memcached на 1 час. При следующем запросе мы сразу достанем их из memcached и отправим на фронтенд. Если мы узнаём, что данные перестали быть актуальными, мы их инвалидируем (удаляем из кэша).
  • Для кэширования статических файлов используются CDN (content delivery network) провайдеры. Это серверы, расположенные по всему миру и оптимизированные для раздачи статики. Иногда бывает эффективнее положить картинки, видео, JS-скрипты на CDN вместо своего сервера.
  • Во всех браузерах по умолчанию включено кэширование статических файлов. Благодаря этому, открывая сайт не в первый раз, все загружается заметно быстрее. Минус для разработчика в том, что со включенным кэшем не всегда сразу видны изменения сделанные в коде.

Архитектура веб приложения: компоненты, слои и типы

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

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

Логика довольно проста: когда пользователь вводит URL-адрес в браузере и нажимает «ввод», браузер делает запрос к серверу. Сервер отвечает, а затем показывает требуемую веб-страницу. Все эти компоненты создают архитектуру веб-приложения.

Как работает системная архитектура для веб-приложений?
Все приложения состоят из двух частей — клиентской (front-end) и серверной (back-end).

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

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

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

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

Базовые компоненты архитектуры веб-приложений
Веб-архитектура имеет компоненты пользовательского интерфейса и структурные компоненты. Последние также делятся на клиентские и серверные.

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

Структурные компоненты состоят из клиентской и серверной сторон:
Клиентский компонент разработан с HTML, CSS или JavaScript. Веб-браузеры запускают код и преобразуют его в интерфейс, поэтому нет необходимости в настройке операционной системы.

Что касается серверного компонента, он построен на Java, .Net, Node.JS, Python и других языках программирования. Сервер состоит из двух частей — логики приложения и базы данных. Логика приложения — это центр управления веб-приложением. База данных отвечает за хранение информации (например, ваших учетных данных).

Уровни архитектуры веб-приложений
Существует четыре общих уровня веб-приложений:

  • Уровень представления (PL)
  • Уровень обслуживания данных (DSL)
  • Уровень бизнес-логики (BLL)
  • Уровень доступа к данным (DAL)

Слой бизнес-логики
BLL несет ответственность за надлежащий обмен данными. Этот уровень определяет логику бизнес-операций и правил. Вход на сайт — это пример уровня бизнес-логики.

Уровень службы данных
DSL передает данные, обработанные уровнем бизнес-логики, на уровень представления. Этот уровень гарантирует безопасность данных, изолируя бизнес-логику со стороны клиента.

Уровень доступа к данным
DAL предлагает упрощенный доступ к данным, хранящимся в постоянных хранилищах, таких как двоичные файлы и файлы XML. Уровень доступа к данным также управляет операциями CRUD — создание, чтение, обновление, удаление.

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

  • Одностраничные веб-приложения
  • Многостраничные веб-приложения
  • Архитектура микросервисов
  • Бессерверная архитектура
  • Прогрессивные веб-приложения

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

Известные СПА : Gmail, Facebook, Twitter, Slack.

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

Известные MPA: eBay, Amazon.

Одностраничное приложение или многостраничное приложение ? У многостраничного и одностраничного приложения есть недостатки и преимущества.

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

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

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

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

Известные проекты: Netflix, Uber, Spotify, PayPal.

  • Монолитные и микросервисы
  • Бессерверная архитектура

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

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

Для создания PWA разработчики используют «языки веб-программирования», такие как HTML, CSS и JavaScript. Если приложению требуется доступ к функциям устройств, разработчики используют дополнительные API — NFC API, API геолокации, Bluetooth API и другие.

Известные PWA: Uber, Starbucks, Pinterest.

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

  • Эффективность
  • Гибкость
  • Расширяемость
  • Соблюдение принципа открыто-закрыто
  • Масштабируемость процесса разработки
  • Легко проверить
  • Возможность повторного использования
  • Хорошо структурированный и читаемый код
  • Нижняя граница

HR Блог для IT рекрутера в Телеграм

Хочешь всегда получать новые статьи, бесплатные материалы и полезные HR лайфхаки! Подписывайся на нас в Telegram! С нами подбор ит персонала становится проще 😉

Реализация серверной логики

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

Решение данной задачи не требует динамического отклика на стороне клиента и, соответственно, может быть реализовано в процессе обработки запроса об изменении статуса заказа на стороне сервера. Следовательно, нам нужно отследить, когда заказ будет находиться в состоянии Update с изменением статуса на Paid. В этом случае нам нужно выполнить поиск товара на складах и списать товар. При этом если определенного товара на складе более не остается, нужно удалить запись об этом товаре:

Логика бизнес-сервера на смену статуса заказа

Разобьем данную логику на конкретные шаги:

  1. уточнить состояние (заказ может быть новым или измененным) и статус заказа (проверяем, чтобы заказ имел статус “Оплаченный”);
  2. вычитать и отсортировать все записи о товарах на складах в соответствии со списком товаров в заказе;
  3. перебрать все товары определенного типа на всех складах и:
    • если товара на складах недостаточно — вывести сообщение об ошибке;
    • если конкретного товара на очередном складе достаточно, то списать нужное количество товара и обнулить счетчик списания;
    • если конкретного товара на очередном складе недостаточно, то удалить запись о текущем товаре на данном складе и уменьшить счетчик списания на то количество товара, которое имеется на данном складе.

Реализуем эту логику в ранее созданном бизнес-сервере OrderBS. В процессе работы используйте брейкпоинты, чтобы контролировать значения переменных.

Условия применения логики

Прежде всего, необходимо реализовать проверку необходимых условий для применения описанной логики (шаг 1). Для этого в секции программиста (между комментариями “Start” и “End”) в теле метода OnUpdateOrder проверим ряд условий:

Подключение пространства имен ICSSoft.STORMNET

Note: Для добавленного метода changeOrderStatusPaid следует учитывать два момента: 1. Данный метод у нас помечен модификатором доступа private: он будет недоступен из других классов. Такое объявление является предпочтительным, так как метод реализует внутреннюю логику и нигде больше не будет использоваться. 2. Данный метод мы добавили перед основным методом бизнес-сервера, в специально отведенном скобками разработчика месте. Это нужно для того, чтобы наш метод при перегенерации сохранился.

Прежде всего, для любого метода рекомендуется добавлять комментарии для автодокументации (Documentation comments), в которой минимально указываются:

  1. — описание метода;
    • параметр метода с указанием имени и описания;
  2. — описание возвращаемых значений (если метод возвращает не void).

Кроме того, так как мы будем в дальнейшем часто использовать тип DataObject, мы упростили обращение к нему, добавив требуемое пространство имен с использованием конструкции using, и тем самым избавились от необходимости писать полное имя типа — ICSSoft.STORMNET.DataObject.

Вызовем добавленный метод (changeOrderStatusPaid) из основного (OnUpdateOrder) и вернуть его результат в качестве списка измененных объектов:

Брейкопинт в методе changeOrderStatusPaid

Note: При вычитке данных здесь используется функционал класса FunctionBuilder. Существует другой метод вычитки из БД — Linq-запросы. Выбор конкретного зависит от проекта.

Результатом выполнения данного участка кода является вычитанный из БД список товарных позиций на складах в переменной objs, соответствующих списку товаров в заказе. Проверим список объектов данных в этой переменной, установив брейкпоинт на return ret (см. предыдущий скриншот): мы ожидаем увидеть две записи (1 и 2 ед. товара на 1-м и 2-м складах соответственно).

Содержимое переменной objs

Далее для удобства работы сформируем отсортированные по первичному ключу товаров пары “ключ-значение”, в которые в качестве значений добавим коллекцию вычитанных товарных позиций с соответствующим первичным ключом товара:

Сортированный список товара на разных складах

Note: Для того, чтобы удалить запись из БД с использованием возможностей Flexberry ORM, достаточно присвоить ей статус Deleted и добавить к списку изменяемых объектов.

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

Состояние складов до смены статуса

Ожидаем, что на первом складе совсем не останется товара “Монитор игровой MSI Optix MAG241CP”, а на втором останется только 1 ед. указанного товара. Для проверки выполним два шага:

    Выставим Дату оплаты Заказу 2 перед изменением статуса

Important: Если вы не проставите Дату оплаты, то в дальнейшем без нарушения логики или дополнительных действий (например, манипуляцией этим полем в БД) у вас не получится её выставить.

Состояние складов после смены статуса

  • Переведем Заказ 2 в статус «Оплаченный», сохраним его и посмотрим, какие товары остались на складах:
  • Код работает корректно: со складов списалось 2 единицы товара “Монитор игровой MSI Optix MAG241CP” в указанной нами последовательности.

    Итог

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

    Самостоятельная работа

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

    1. при изменении статуса Заказа на “Оплаченный” создавать на стороне сервера Накладную, к которой был бы уже прикреплен заказ, установлены статус “Новый” и выставлена текущая дата создания.

    Перейти

    • Практическое руководство «Делай как я»
    • Бизнес-серверы и режим отладки
    • Реализация презентационной логики

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

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