Обновить
4

Пользователь

2
Подписчики
Отправить сообщение

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

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

Один агрегат - одна транзакция пошло от Вернона.

Прямого запрета нет, но важно понимать;

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

  • В свою очередь это ведёт к проблемам при росте конкуретности и масштабировании системы.

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

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

Например попытка через ДДД реализовать SSO, MFA и тд может увенчаться успехом, но смысла в этом мало. Есть уже устоявшиеся модели, их реализации и способы работы с ними, и моделировать тот же процесс через ДДД это изобретать велосипед.

Коротко говоря - ДДД это инструмент под задачу, а не швейцарский нож.

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

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

Следуя моей метафоре, у Вас есть бумажка инстанс агрегата типа "Приглашение". Вы предъявляете ее оператору выполняющему "Сценарий использования приглашения". Если ему надо выполнить много шагов в рамках данного сценария, он вероятнее всего, возьмет бланк из стопки, в котором будет отслеживать прогресс. Этот бланк будет инстансом агрегата "Запрос на обработку приглашения" или вроде того. И там будет отмечать галочками пройденные промежуточные этапы по мере их выполнения. Так же он может отмечать их прямо в приглашении если оно спроектировано соответствующим образом. В конечном итоге оператор возьмёт пустой бланк формата "Учётная запись" и заполнил его. Далее в своем чеклисте отметит, что этот этап пройден. Потом отметит в приглашении что оно уже было использовано и поставит подпись. Потом отметит это в своем чеклисте. И так пока процесс не завершится. Чек-лист нужен для того, чтобы если этот оператор выйдет из строя, другой оператор мог подхватить в том месте, где закончил предыдущий. А можно и без него, тогда процесс начнется заново. Все зависит от того, как это работает сейчас. Конечно можно предложить улучшения и указать менеджерам на пробелы в их проуессах, но лучше начать автоматизацию с того, как это работает сейчас, а потом итеративно улучшать.

Вы скажете - а что если 1 оператор начал заполнять бумажку, но не закончил и заболел. Люди как-то могут разобраться, позвонить, уточнить. Машина нет. Но в целом процесс должен быть такой - недописана или дописана без подписи оператора - считать, что ее вообще нет. Это и есть аналог транзакционности. Поставил подпись = готово к комммиту. Положил в картотеку = закоммитил.

Оператор это и есть ваш код. Бумажка это агрегат. Код работает с одним агрегатом за раз. Взял один, изменил, положил на стол. Взял другой, изменил, положил на стол.

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

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

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

В итоге у вас 2 бумажки - приглашение и копия карточки вашей учётной записи.

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

Принципы построения гибкой и устойчивой архитектуры достаточно просты:

  • В системе должны быть выделены логические модули. Модуль это единый набор функциональности. Например, модуль управления профилем пользователя. Или модуль управления способами входа пользователя (mfa, sso, пароли). Модули между собой не общаются , друг от друга не зависят. У каждого модуля свое изолированное пространство БД.

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

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

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

20 лет прошло с момента диссертации Роя Филдинга, а разработчики до сих пор не могут отличить REST от HTTP RPC.

REST это про гипермедиа, HATEOAS, возможность задать поведение клиента с сервера. URL в REST это адрес ресурса и ключ кеширования, он вообще не обязан иметь какой-то четкой структуры. GET без body нужен, чтобы передавать все параметры запроса в строке запроса и иметь возможность закешировать ответ на любом из прокси, чтобы не читать body. PUT, PATCH, DELETE и тд являются вариациями POST (того же эффекта можно добиться используя POST и комбинации заголовков для манипуляции кешом на клиенте); именно поэтому в HTML 5 до сих пор есть только формы GET & POST.

А вы рассказываете про RPC поверх HTTP. И там хоть делайте хоть PATCH /orders хоть POST /update-order, скорее всего вы не будете использовать заголовки для кешироания в своих микросервисах и дадите клиенту вашего сервиса (разработчику) самому решать, что кешировать, а что нет. И уж тем более у вас не будет поддержки HATEOAS.

Рецепт такой - вы пишете веб интерфейс используя ASP.NET Core или NextJS (конечный потребитель это человек через браузер) - вы уже используете REST. Вы пишите микросервисы (конечный потребитель разработчики, которые интегрируют ваш АПИ в свои приложения) - вы используете RPC.

Согласен. Я писал об этом в данном комментарии к другой статье. https://habr.com/ru/articles/926214/comments/#comment_28553346

Я бы сказал так - BFF и API Gateway это логические единицы. Физически это может быть даже один контейнер, в котором будет приложение, которое по путям /app, /checkout, /admin или по заголовку host будет маршрутизировать запросы на соответствующий модуль BFF или Gateway. А логическое ядро (команды, запросы, оркестраторы, бэкграунд обработчики) будет так же запущено в рамках того же приложения. Получаем модульный монолит. Такая архитектура знакома многим, кто разрабатывал приложения до появления тренда на микросервисы - тот же классический WordPress яркий пример такой архитектуры. Там модули вообще можно было подключать как плагины. И все на одном сервере.

Если модули не спутаны между собой, то всегда можно взять какую-то особо нагруженный модуль и при явной необходимости вынести его в отдельный инстанс/инстансы и масштабировать его независимо. То же самое и с базой данных. Может вообще оказаться, что инстанс приложения тянет и его масштабировать не надо, а БД не тянет. Для высокой нагрузки - соответствующее решение с соответствующим SLA. Для низкой нагрузки - простое решение. Например, модуль поиска видео и просмотра видео очень нагруженный и может быть потребоваться десятки инстансов БД, с репликацией и ТД чтобы выдержать нагрузку и обеспечить высокую доступность и отказоустойчивость. Эти сервера могут даже географически быть раскинуты, для обеспечения SLA. А вот модуль пользовательских плейлистов кратно менее нагружен и куда проще (т.к. там скоуп не весь сервис или категория видео, а конкретный пользователь). С таким на десятки тысяч активных пользователей и один относительно небольшой инстанс справится, да и требования к доступности и устойчивости у него наверняка поменьше. Ну не получилось добавить в свой плейлист видео, неприятно, но не конец света. На крайний случай можно на клиенте записать, что была попытка добавить и потом в бэкграунде обновить данные на сервере.

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

UPD: А так как объективно число пользователей интернета ограничено, как и их время в сутках, то не может быть так, чтобы у всех приложений был трафик как у Озон, Вайлдберриз, Амазон, Гугл и тд. Т.е. чаще всего приложениям пользуются пару тысяч человек в общем случае одновременно, и тут объективно чаще всего монолита хватит. Даже чуть вскрою карты, я работаю в крупнейшем провайдере IP телефонии в Серверной Америке. У нас трафик на самых нагруженных сервисах, о которых мне известно, оценивается тысячами RPM, максимум десятками тысяч в самое пиковое время. И микросервисная архитектура используется во многом для независимого деплоя модулей, недели для масштабирования. Хотя там такая связность, что даже и эта возможность почти никогда не работает. Буквально многие сервисы могли быть сделаны монолитами и ничего бы в худшую сторону не изменилось.

Все куда проще. Есть клиент, например, клиентский веб портал. Вам нужно получить информацию из системы в разрезе данного конкретного пользователя. Нужно пройти аутентификацию и при отображении интерфейса делать срез / представление для данного пользователя. Бэкенд же генерик. Он позволяет мутировать данные и получать данные. Он не беспокоиться, а кто их запросил, а есть ли у него права. Он концентрируется на бизнес логике (данные и их мутации). Работу по созданию проекции для пользователя, агрегации данных и ТД выполняет BBF. Такой же BFF может быть и для приложения портал авторов, которое взаимодействует с тем же бэкендом, но уже в другой проекции, другими правилами авторизации и тд. И третий BFF будет у приложения бэк офиса, где например модерируется контент. Например в веб приложении могут использоваться к http-only cookie для хранения полезной инфы, в том числе авторизации; а мобильные приложения могут использовать более удобный для них jwt.

Чем отличается от API Gateway? Только степенью узконаправленности: API Gateway широкое направлен и чаще всего обслуживает публичный АПИ. Это выражается во всем: от широты спектра возможностей авторизации: oAuth2, API ключи и до очень обобщенного контракта API, чтобы можно было сделать буквально любого клиента или любую интеграцию.

Мой рецепт такой - если клиент "ваш" нативный - делайте БФФ, в перспективе не будет ошибкой 100%. Если вам нужно предоставит точки интеграции "внешним" сервисам в рамках вашей компании или других компаний - делайте API Gateway и проектируйте публичный API. При этом в обоих случаях ядро системы будь то монолит или микросервисы предоставляют только операции с бизнес логикой + оркестраторы или менеджеры процессов, не парятся за авторизацию и доступны только в приватной сети. За авторизацию и пользовательский контекст отвечают BFF и API Gateway.

В реакт нет механизма запуска рендера без изменения состояния или пропсов.

ASP.NET MVC — это супермощная вещь, но она имеет ряд недостатков по сравнению с альтернативами.

  • Интерактив на странице реализовывать куда менее удобно, чем, например, в Next.js. Да, у вас доступна вся мощь ванильного JavaScript, HTMX, Bootstrap — и добавить на страницу карусели, модальные окна или простые формы вполне реально. Этого достаточно для части задач, например при рендеринге простых сайтов. Однако создать полноценный интерактивный мультифункциональный портал для клиента — вроде банковского — будет значительно сложнее (хотя и возможно: как-то же раньше справлялись, хотя современные фреймворки как раз и пришли на смену этому «как-то»).

  • Выбор UI-китов крайне ограничен, и большинство из них постепенно устаревают. В современном мире UI-киты чаще всего выпускаются под конкретные фреймворки. Да, бывают версии с «чистым» HTML, но их немного — и у них возникают серьёзные вопросы по объёму «копипасты»: чтобы создать модальное окно или форму с валидацией, приходится вставлять в 2–3 раза больше кода, чем в случае с компонентами.

  • С другой стороны, это же и плюс: работа на самом низком уровне с HTML, CSS и ванильным JavaScript заставляет искать максимально простые и прямолинейные решения там, где это возможно.

Если же рассмотреть Blazor, он тоже оказывается довольно компромиссным решением:

  • Да, интерактивность реализовывать проще, и удобно переиспользовать код между сервером и клиентом. Однако всё это присутствует и в Next.js — причём в куда более продвинутом виде и нативно, на «родных» веб-технологиях, а не через WebAssembly и JS Interop.

  • А сам JS Interop — это головная боль. Да, можно использовать JS-библиотеки, но там, где TypeScript подсвечивает синтаксис и проверяет типобезопасность на этапе компиляции, в Blazor все проверки смещаются в рантайм (включая тесты).

  • В целом экосистема Blazor развита слабо: под React сотни UI-китов и библиотек, а в Blazor основную работу ведут энтузиасты.

Поэтому каждый раз, когда я думаю запускать новый SaaS-пилот и пишу бэкенд на .NET, руки тянутся «ускорить» разработку, взяв ASP.NET Core или Blazor. Но, трезво взглянув на реальность, я понимаю: гораздо быстрее написать весь фронт на Next.js — там уже есть нужный UI-кит, удобные компоненты, сборка через Vite, минимизация, оптимизация, сжатие изображений — всё настроено «из коробки». Так что на ASP.NET MVC я уже смотрю с лёгкой ностальгией, хотя и пишу API на .NET.

P.S. При этом Next.js и вся Node.js-экосистема, конечно, дико кривые. Не далее как сегодня я узнал, что в Node.js HTTP-сервере жёстко задан таймаут запроса — 5 минут. Его можно переопределить, но только глобально: установка таймаута для отдельных запросов не работает. А багу этому уже за 100 лет в обед, и никаких движений по её исправлению. Ладно, это ещё можно обойти. Но Next.js не позволяет переопределять этот таймаут через свои настройки — только патчингом исходников. Ишью на GitHub по этому поводу висит уже несколько лет. И таких «подводных камней», на которых можно зависнуть на целый день, хватает — стоит лишь выйти за рамки простого рендеринга. Но всё равно, как ни странно, получается быстрее, чем на ванильном стеке, за счёт универсальности серверного и клиентского рендеринга.

Статья - "Тотэнхэм". И вроде не наврал нигде, но вроде все описано очень поверхностно. Ничего нового для себя не открыл.

Я как-то вступил в достаточно жесткое противостояние с менеджментом на тему Agile и Scrum. Менеджмент требовал полного соответствия комитменту, чтобы велосити был не толькл стабильным, так ещё и рос каждые несколько спиртов, т.к. якобы наша компетенция растет. Причем когда мы говорили, что задача на несколько спринтов и ее хрен нарежешь, заставляли нарезать, и очевидно резали почти наугад и само собой в 90% случаев не попадали, потому что то, что думали будем делать долго, делали быстро, а то, что думали сделаем быстро - делали долго. И рапорты каждый раз по одному месту. И никак этого не избежать, т.к. делали уникальный продукт (аля продвинутый зум со встроенным OBS на webrtc). И менеджмент сам не понимал - результаты на дистанции несколько месяцев феноменальные, а отчёты непонятные. Их прямо ломало от этого, они не могли четко "планировать". Хотя какое тут планирование, неизвестность за каждым углом, сегодня план один, завтра другой и это и есть Agile в своей крайней степени.

Я же объяснял, что менеджменту надо сфокусироваться на продукте, ценности и на оценке ситуации, а не на показателях отчётов по спринту в Jira. Задача будет сделана тогда, когда будет сделана. На планинге скоуп и риски оцениваются приблизительно, на основе того, что команда знает в момент оценки о задаче. Далее команда даёт ежедневный статус апдейт, какое состояние, переоценивает риски. Менеджер должен внимательно слушать, анализировать и если нужно менять вектор, забивая на цели спринта и коммитменты, если появляется что-то более приоритетное или оказывается, что риски были взвешены неправильно и надо срочно менять тактику. О том, почему они были взвешены неправильно и можно ли было что-то изменить обсудите уже на ретро, но выкореживаться, чтобы получить "хороший" отчёт вредит вообще всем.

В этом плане объективно самым гибким методом является scrumban - нет никаких спринтов, команда работает по обстоятельствам. При этом присутствует цикличность ритуалов вроде рефаймерта, демо, ретро, иногда даже хай левел планинга. Но для этого нужно, чтобы либо команда была самоорганизована и сама драйвила продукт (0.1% команд на такое способен), либо чтобы был толковый менеджер, а не "менеджер по методичке".

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

Главная фича ORM это ченж трекинг и маппинг. И особенно удобно это в CQRS на стороне Command. Достаете из репозитория агрегат (который может быть сущностью вроде Order с вложенным OrderItems), за счёт маппинга агрегат собирается автоматически по довольно простым правилам маппинга описанным декларативно; далее мутируете агрегат, соблюдая инварианты, и далее вызываете "Сохранить". Все, ОРМ сам высчитает diff, постарается его применить к конкретной базе которая используется на проекте, применит оптимистичную конкуренцию и тд.

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

Если я использую тактические паттерны DDD на проекте, выделяю явно слой домена, агрегаты и ТД, то у меня всегда используется ОРМ, потому что это очень удобно.

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

Да нет никакой субординации на собеседовании, это ваш конкретный загон.

Нантмающая сторона по своим субъективным критериям за выделенное время пытается понять, подходит ли ей специалист. Специалист пытается понять, подходит ли ему данная компания по своим субъективным критериям. Дальше либо матч есть либо нет. Кому-то подходит модель "начальник-подчиненный", кому-то более неформальный подход, а кто-то не смотрит ни на что кроме суммы офера.

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

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

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

Более того, в работе я лично предпочитаю модели без субординации, а с зонами ответственности и компетенции. Условно я всегда должен иметь возможность дать обратную связь своему линейному менеджеру или иметь возможность высказать свое мнение о его решении. Но его решение остаётся его решением и ему за него и отвечать. Ровно как и я как тех лид могу придти с решением к команде, она может дать обратную связь. В конечном итоге мы придем к варианту, что есть несколько сопоставимых опций из которых надо выбрать и финальное решение за мной, т.к. это моя ответственность. Т.е. в такой схеме минимизируется "я начальник ты дурак". Все прозрачно, какие опции рассматриваются, какие у них плюсы и минусы и какое финальное решение и почему.

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

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

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

В этом и суть менеджера, найти и почувствовать лучшую комбинацию.

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

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

А знаете как справляется такой спец-единоличник? Он не закрывает ноутбук в 6 вечера, он сидит после работы, иногда до ночи, иногда на выходных. И именно так он и стал спецом. И когда в команде вроде кто-то взял задачу, спец морально настроился максимум чуть поссаппортить, а в итоге все скатывается в то, что ему приходится опять сидеть тащить, потому что релиз уже на носу, а сделано 10% максимум - теряется доверие.

И вот тут тоже проблема лида и менеджмента. Им такой спец удобен. Они не хотят менять тех середнячков, которых набрали, на спецов покруче. Не хотят планировать лучше. У них есть такой спец, так что все будет в шоколаде (да и по деньгам он получает на 15-20% больше всего по сравнению с нормесами). И это бесконечный цикл, который может разорвать только сам спец, но осадок все равно останется, либо нормальный менеджер.

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

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

1
23 ...

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность