Присоединяйтесь к нашему каналу и погрузитесь в мир Backend-разработки Связь: @devmangx РКН: https://clck.ru/3FobxK
99% "медленных API" работают медленно из-за таких причин:
- N+1 запросы
- нет индексов
- чрезмерно многословные эндпоинты (слишком много мелких запросов туда-сюда)
- возвращают больше данных, чем нужно
- криво настроено кэширование
👉 @BackendPortal
9 лучших практик работы с Docker
1. Используй официальные образы
FROM node:14-alpine
FROM node:14.17.0-alpine3.13
FROM build-image AS build
# шаги сборки
FROM production-image
COPY --from=build /app /app
node_modules
npm-debug.log
RUN useradd -m myuser
USER myuser
CMD node index.js
ENV APP_HOME /app
WORKDIR $APP_HOME
FROM debian
RUN apt-get update
RUN apt-get -y install openjdk ssh vim
COPY . /app
CMD ["java", "-jar", "/app/target/app.jar"]
LABEL maintainer="name@example.com"
LABEL version="1.0"
docker scan
WebSockets придумали ещё в 2010 году, может и раньше. Их сразу заточили под то, чтобы заголовки занимали минимум места, и это реально круто.
Вместо фиксированного размера заголовка под длину данных сделали 7-битный индикатор, который показывает, сколько байт нужно для хранения длины.
Если payload ≤ 125 хватает 7 бит
Если payload больше 125 и меньше 65 536 длина указывается в двух следующих байтах
Если payload больше 65 536 длина пишется в восьми следующих байтах
👉 @BackendPortal
Нравится контраст между буферизацией страниц данных в MySQL и Postgres. Подходы кардинально разные.
InnoDB в MySQL делает ставку на больший контроль. С innodb_flush_method='O_DIRECT'
движок обходит большую часть накладных расходов на кэш страниц ОС и управляет всем сам. Объём кэша задаёт innodb_buffer_pool_size
. На выделенных БД-серверах это часто 70%+ всей RAM.
Пул делится на несколько подэкземпляров. Каждый держит двухчастный список страниц LRU: young и old. Новые страницы попадают в old и должны «прожить» без вытеснения заданное время (innodb_old_blocks_time
), прежде чем их пустят в young. Это защищает от thrashing при массовых операциях вроде table scan.
Postgres выбирает более простой внутренний кэш и полагается на кэш страниц ОС для загрузки/выгрузки. Размер задаёт shared_buffers
. Часто ставят около 30% доступной RAM, оставляя место под кэш ОС.
Для вытеснения Postgres использует clock-sweep. У каждой страницы в shared_buffers
есть счётчик: он увеличивается при обращениях (до лимита) и уменьшается при проходе «часов». Когда счётчик падает до 0, страницу можно заменить другой.
Отличный README из репозитория Postgres: ссылка
👉 @BackendPortal
Шпаргалка по командам Redis
🔸Server Commands
- PING
: проверить, жив ли сервер
- FLUSHDB
: удалить все ключи в текущей базе
- FLUSHALL
: удалить все ключи во всех базах
- DBSIZE
: количество ключей в текущей базе
- INFO
: информация о сервере
🔸Basic Key Operations
- SET key value
: сохранить значение по ключу
- GET key
: получить значение ключа
- DEL key [key ...]
: удалить ключ(и)
- EXISTS key
: проверить, существует ли ключ
- EXPIRE key seconds
: установить время жизни ключа (в секундах)
- TTL key
: получить оставшееся время жизни
🔸String Commands
- INCR key
: увеличить числовое значение ключа
- DECR key
: уменьшить числовое значение ключа
- APPEND key value
: добавить строку к значению ключа
- GETRANGE key start end
: получить подстроку
🔸Hash Commands
- HSET key field value
: установить поле в hash
- HGET key field
: получить значение поля
- HGETALL key
: получить все поля и значения
- HDEL key field [field ...]
: удалить поле(я)
- HMSET key field value [field value ...]
: установить несколько полей
🔸List Commands
- LPUSH key value [value ...]
: добавить в начало списка
- RPUSH key value [value ...]
: добавить в конец списка
- LPOP key
: удалить и вернуть первый элемент
- RPOP key
: удалить и вернуть последний элемент
- LRANGE key start stop
: получить элементы по диапазону
🔸Set Commands
- SADD key member [member ...]
: добавить элемент(ы) в set
- SMEMBERS key
: получить все элементы
- SREM key member [member ...]
: удалить элемент(ы)
- SISMEMBER key member
: проверить, есть ли элемент
- SUNION key [key ...]
: объединить set’ы
🔸Sorted Set Commands
- ZADD key score member [score member ...]
: добавить с весом
- ZRANGE key start stop [WITHSCORES]
: получить элементы по индексу
- ZREM key member [member ...]
: удалить элемент(ы)
- ZCARD key
: количество элементов
🔸Persistence & Config
- SAVE
: синхронно сохранить на диск
- BGSAVE
: сохранить на диск в фоне
- CONFIG GET/SET parameter
: получить или задать конфигурацию
🔸Client Connections
- AUTH password
: авторизация с паролем
- SELECT db
: выбрать базу данных (по умолчанию 0)
- QUIT
: закрыть соединение
👉 @BackendPortal
Многие приложения на бэкенде требуют генерации уникальных ID.
На одном сервере это просто, но при масштабировании задача усложняется.
Например, на одном сервере можно использовать автоинкремент или функцию для получения текущего времени.
Но такие стратегии ломаются в распределённой системе, потому что начинают появляться дубликаты.
Вот 4 рабочих подхода:
1. UUID на уровне приложения
UUID — это 128-битное число, которое приложение может генерировать разными способами.
Например, комбинировать время, MAC-адрес сервера или MD5-хэш.
Плюсы:
• серверам не нужно синхронизироваться
• огромное пространство ID и низкая вероятность коллизий
Минусы:
• большой размер (128 бит)
• ID не идут по порядку
2. UUID на уровне базы данных
Многие БД поддерживают автоинкремент.
То есть можно поручить генерацию ID самой базе.
Этот подход также известен как ticket service и использовался, например, Flickr.
Плюсы:
• упрощает код приложения
• ID короткие и последовательные
Минусы:
• лишний запрос к базе ради получения ID
• база становится единой точкой отказа
3. Snowflake ID
Такой способ применяли Twitter и Instagram.
Идея в том, чтобы собирать ID из нескольких полей:
• 41 бит — таймстемп
• 10 бит — worker ID
• 12 бит — порядковый номер
• 1 бит — зарезервирован на будущее
Особенности:
• таймстемп с миллисекундной точностью работает ~70 лет от начальной эпохи
• worker ID выдаётся через сервис координации (например, Zookeeper), с учётом датацентра и сервера
• порядковый номер даёт до 4096 уникальных ID на миллисекунду
Плюсы:
• работает при 1024 машинах одновременно
• масштабируется: каждая машина может генерировать 4096 ID/мс
• ID сортируемы по времени, так как таймстемп в старших битах
4. ULID и NanoID
ULID компактнее и удобнее UUID, плюс их можно сортировать по времени.
Но они не гарантируют глобальную уникальность и могут раскрывать информацию о времени и порядке генерации.
NanoID даёт больше возможностей кастомизации и не показывает коллизий даже при 100 миллионах итераций.
Кроме того, они дружелюбны к URL, что делает их удобным выбором для веб-приложений.
👉 @BackendPortal
Алгоритмы, которые стоит знать перед собеседованиями по System Design
👉 @BackendPortal
🔥 VPN, который реально выручает — отличная находка
Устали от VPN, которые только обещают, но не работают? 😤
Скорость оставляет желать лучшего 📉, соединение постоянно рвется 😵, а нужные сайты отказываются открываться?
Тогда вам точно стоит обратить внимание на ProxyFable! 🚀
💥 Работает очень быстро. Сервис отлично подходит для просмотра YouTube, онлайн-игр и работы — без лагов и зависаний.
🛡 Анонимность на высоком уровне. Никаких логов, всё зашифровано. Можно спокойно пользоваться из любой точки мира.
🌍 Серверы в разных странах — США, Германия, Нидерланды. Всё работает стабильно, без перегрузов.
💸 Выгодная партнерская программа. За приглашение друзей можно получать реальные деньги на карту.
🎁 Сервис дает 2 дня бесплатного доступа — можно протестировать всё без привязки карты и сразу начать приглашать друзей.
💬Поддержка оперативная — отвечают быстро и по делу.
➡️ Протестировать сервис можно тут: @ProxyFable_bot
📡 Новости и обновления: @ProxyFableChannel
Жизненный цикл Pod в Kubernetes
👉 @BackendPortal
Настоящая задача уровня баз данных в сетевом мире.
У маршрутизаторов BPG почти миллион строк в таблицах маршрутизации.
При получении IP пакета нужно найти адрес назначения, отобрать все подходящие сети с применением маски, выбрать маршрут с минимальной стоимостью и определить следующий хоп для пересылки пакета.
Это должно выполняться за доли секунды.
👉 @BackendPortal
BrOk — графический дизайнер интеграционных процессов от AXENIX
🔥 BrOk — это low-code решение для работы с брокерами сообщений (Kafka, RabbitMQ, Artemis, Redis MQ, NATS), СУБД, REST-сервисами и хранилищами данных (ETCD, Redis). Всё управление — через единый GUI, без лишних CLI-команд.
Для кого: разработчики, DevOps и QA, которые настраивают интеграции, тестируют нагрузку или управляют обменом данными.
Что умеет BrOk:
🔸 Подключение к брокерам, БД, REST-API, ETCD и Redis в одном интерфейсе
🔸 Генерация сообщений по шаблонам для тестов
🔸 Low-code сценарии с ретраями и расписанием
🔸 Поддержка Postman и OpenAPI-коллекций
🔸 Инструменты для Kafka: KsqlDB, Connect, Schema Registry
🔥 Главное — BrOk распространяется бесплатно для некоммерческого использования.
Скачайте дистрибутив на официальном сайте и упростите разработку и тестирование сложных информационных систем уже сегодня!
Реклама. ООО «Акстим Тех». ИНН 6950259732
Говорят, что микросервисы вредны для планеты. 🌟
В статье рассматриваются стратегии как на уровне проектирования, так и на уровне эксплуатации, которые помогают сделать микросервисы более энергоэффективными.
Снижение высокого энергопотребления в микросервисах - тык
👉 @BackendPortal
В программировании система это набор правил, процессов и триггеров, которые помогают управлять рабочим процессом.
В этой статье Prankur Pandey разбирает ключевые принципы системного дизайна, которые можно применять и в коде, и в жизни.
В примерах используются джоб-борды и гейминг, а также рассматриваются проблемы роста, кэширование и CDN, API, теорема CAP и многое другое
👉 @BackendPortal
👨💻Ищем программистов, DevOps, QA. Релокейт, удалёнка, платим много!
Специально для Вас, собираем лучшие вакансии, только с прямыми контактами в Telegram!
👩💻 DevOps 👣 Go
👩💻 Java 👩💻 Python
🖼️ PHP 👩💻 Node.js
👩💻 C# 🤖 ML & DS
🔎 QA 🖥 SQL
👩💻 UX/UI 👩💻 Frontend
👩💻 Mobile 👩💻 Analyst
💼 1C
Подпишись чтобы не упустить свой шанс получить лучший оффер!
Какие паттерны проектирования реально нужны
Знать все паттерны не обязательно.
Но есть базовые, которые каждый разработчик должен освоить по мере роста, чтобы писать более качественный софт.
🔴 Junior Developer
Фокус на основе: создание объектов, чистые алгоритмы, понятные потоки событий.
1. Singleton — один экземпляр, глобальный доступ. Использовать осторожно, избегать скрытого состояния
2. Factory Method — делегирование создания объектов, скрытие деталей
3. Strategy — замена алгоритмов без if/else
4. Template Method — фиксированный алгоритм с настраиваемыми шагами
5. Observer — подписка на события без жесткой связки
Эти паттерны помогают писать более чистый и тестируемый код с самого начала.
🔴Mid-Level Developer
Следующий уровень —> умение управлять сложностью и добавлять гибкость.
1. Builder — пошаговое создание сложных объектов
2. Facade — единая точка входа в запутанную подсистему
3. Adapter — подгонка сторонних API под твой код
4. Proxy — прокси-объект для контроля доступа, ленивой загрузки или удаленного вызова
5. Decorator — добавление обязанностей во время выполнения без наследования
6. Command — обертывание действий в объекты (очередь, undo, логирование)
7. State — представление режимов как классов вместо флагов
Эти паттерны учат упрощать подсистемы, расширять поведение без наследования и моделировать процессы безопаснее.
🔴Senior Developer
Задача —> развивать системы безопасно и оптимизировать их под изменения и производительность.
1. Composite — одинаковое обращение к дереву «часть-целое» (меню, правила, цены)
2. Visitor — добавление операций к сложным структурам без редактирования классов
3. Bridge — разделение абстракции и реализации, независимое развитие
4. Flyweight — разделение внутреннего состояния для экономии памяти при множестве похожих объектов
5. Interpreter — небольшие DSL, разбор и выполнение простых грамматик
На этом уровне важно не количество паттернов, а умение выбирать.
Самое сложное это понять, когда их не стоит применять.
- Паттерны — это инструменты, а не правила.
- Неверный паттерн в неверное время создаст лишнюю сложность.
- Верный — сделает систему гибкой и готовой к будущим изменениям.
👉 @BackendPortal
Коллекционирую инструменты, которые помогают в разработке ПО: сервисы, утилиты, базы данных, языки программирования и многое другое. Регулярно делаю на них мини-обзоры. В основном стараюсь находить новое, но бывает и всякое недооцененное старое. Без спама, без роботов и без рандомных репозиториев: только уникальный и отборный контент! ✨
Подробнее
Retry-паттерн может как спасти систему, так и угробить её. Это простой способ добавить устойчивости, но большинство используют его неправильно .
Главное правило это не пытаться ретраить бесконечно. Повторы стоят ресурсов: если их мало, то ты сдаёшься на временных сбоях вроде сетевого глюка, если слишком много — перегружаешь систему и скрываешь настоящие ошибки. Обычно трёх повторов достаточно, дальше речь уже скорее идёт о реальной проблеме, а не о временном сбое.
Не менее важно время между повторами. Без задержек все запросы будут сыпаться одновременно и добьют систему. Экспоненциальный бэкофф, где паузы растут от секунды к двум, четырём и дальше, даёт сервису шанс восстановиться и не превращает ретраи в отдельную проблему.
Ретраить нужно только временные ошибки. Например, таймауты или перегрузку сервера. Но если сервер возвращает битый запрос или запрет доступа, повторять нет смысла, это баг данных или дизайна. Представь автомат, если он пишет «временно не работает», можно попробовать позже. Если он требует другую купюру, повторная попытка ничего не изменит.
И ещё одна защита — circuit breaker. Если сервис реально лежит, ретраи могут устроить каскад отказов. Circuit breaker следит за количеством ошибок, отключает запросы, если порог превышен, и снова включает их, когда система оживает.
Если всё сделать правильно, повторы делают систему надёжной. Если неправильно, то это, как подливать бензин в огонь.
👉 @BackendPortal
Stack Overflow не просто вытеснили ИИ
Он сам вырастил себе замену.
Было время, когда программирование выглядело так:
уперся в стену — открыл пять вкладок Stack Overflow и лихорадочно ищешь тот самый ответ с зелёной галочкой 2012 года.
До ChatGPT, Claude или DeepSeek было только одно место, где можно было:
• найти потерянную точку с запятой
• расшифровать загадочную ошибку
• скопировать рабочий сниппет кода (и надеяться, что взлетит)
Каждый разработчик хотя бы раз шептал себе: «Да благословит бог этого анонима», когда находил решение ровно под свою проблему.
Сегодня мы просто спрашиваем у ИИ — и получаем готовый ответ.
Но прикол в том, что:
Все эти ИИ учились именно на данных Stack Overflow.
Тот регекс, который тебе написал ChatGPT? Он вырос из Stack Overflow.
Советы по отладке от Claude? Питаются миллионами ответов SO.
Подсказки кода от DeepSeek? Это знания Stack Overflow в новом обличии.
Stack Overflow создал самую полную базу знаний по программированию, а потом эта база научила ИИ, как заменить её.
Классический момент «я тебя вырастил».
Конец одной эпохи. Начало другой.
А у тебя какой самый запоминающийся случай, когда Stack Overflow спас твою задницу? 😁
👉 @BackendPortal
Мы и так используем базы данных, так почему бы не заменить ими кэши? Можно ли вообще полностью отказаться от кэширующих сервисов в пользу БД?
В посте автор делится мыслями и разбирает, как постепенно идёт движение в эту сторону.
Читать здесь: https://avi.im/blag/2025/db-cache/
👉 @BackendPortal
Как можно использовать Redis?
Redis — это не только кэш
👉 @BackendPortal
Нашёл список того, что обычно входит в зону ответственности бэкенд-разработчика:
∟📂 Серверное программирование (Python, Java, PHP, NodeJs, Go...)
∟📂 Базы данных (SQL, NoSQL)
∟📂 API и веб-сервисы
∟📂 Безопасность и аутентификация
∟📂 Кэширование и оптимизация производительности
∟📂 Масштабирование и балансировка нагрузки
∟📂 Деплой и DevOps
∟📂 Структуры данных и алгоритмы
∟📂 Файловое и объектное хранилище (S3)
∟📂 Обработка ошибок и логирование
∟📂 Тестирование
∟📂 Фоновые задачи и планировщики
∟📂 Непрерывная интеграция и доставка
∟📂 Контейнеризация и оркестрация (Docker, Kubernetes)
∟📂 Очереди сообщений и потоковая обработка (RabbitMQ, Kafka)
👉 @BackendPortal
Видеостриминговый стартап из Сингапура провёл техническое собеседование в необычном формате
Кандидат на позицию Backend и Infra инженера за 4 часа вместе с командой собрал прототип аналитического движка. В работе использовали Go, PostgreSQL и Grafana, а для фронтенда выбрали Claude.
Исходный код проекта открыт: https://github.com/mrinalxdev/go-grafana
👉 @BackendPortal
Задержки это смерть от тысячи мелких порезов.
Так что чините. Начните с базы: один плохой запрос легко добавляет полсекунды, а на тысячах запросов это превращается в катастрофу. Не используйте SELECT *
, добавляйте индексы, убивайте N+1 и всегда проверяйте запросы через EXPLAIN
.
Дальше посмотрите на сетевые прыжки. Каждый переход через цепочку сервисов добавляет задержку, поэтому мелкие сервисы иногда лучше объединить. Хорошая идея — использовать BFF.
Кэш тоже обязателен. Если данные не меняются, нет смысла тянуть их заново. Сессии, каталоги, конфиги — держите их горячими. Протухший кэш плохо, но отсутствие кэша почти всегда хуже.
Не отправляйте десятки мелких запросов последовательно. Объединяйте их в батчи или гоните параллельно, так результат приходит быстрее.
И не таскайте лишние данные. Убирайте ненужные поля, сжимайте ответы, делайте пагинацию и оптимизируйте картинки.
Скорость это не дополнительная фича, а фундамент нормального UX.
👉 @BackendPortal
Вышел Kubernetes 1.34
Разработчики сообщили о релизе новой версии Kubernetes. Те, кто хочет протестировать обновления прямо сейчас, могут воспользоваться обновлённым playground Kubernetes the Hard Way: ссылка
👉 @BackendPortal
Как один баг в Google Cloud уронил весь Интернет
12 июня Google Cloud упал, и вместе с ним перестали работать Spotify, Fitbit, Gmail, Google Drive, Vertex AI и десятки других сервисов. Причиной оказался всего один null pointer баг в Service Control — сервисе, через который проходит почти каждый запрос к Google Cloud API.
Все запросы проходят через Service Control, он как вышибала, решающий, есть ли у тебя доступ. Если он падает, то рушится всё.
В конце мая Google добавил туда новый код для проверки квот, где не было обработки ошибок на пустые поля. На тестах этот код не активировался, потому что требовалась специфичная политика. Не было ни feature flag, ни постепенного раската — просто мёртвый код, ждавший своего часа.
12 июня утром в базы попала политика с пустыми полями. Service Control попытался её обработать, наткнулся на null pointer и упал.
Spanner почти мгновенно разнёс некорректные данные по всему миру, и каждая инстанция, которая к ним обратилась, падала следом. Двух минут хватило, чтобы Google Cloud лег глобально.
Начались таймауты, 503 от перегруженных систем и 401, когда пустые политики интерпретировались как отсутствие прав.
Пользователи Spotify массово получали 401, Fitbit выдавал разные ошибки в зависимости от региона, сервисы не могли пройти аутентификацию к своим бэкендам.
У Google был kill switch — красная кнопка, чтобы отключить проблемный код. Её нажали через сорок минут, и большая часть регионов восстановилась. Но us-central1 оставался недоступен ещё три часа. Когда тысячи инстансов Service Control перезапустились одновременно, они одновременно пошли в базу и устроили эффект стада, который уронил даже сам фикс.
Хуже всего то, что статус-дэшборд Google тоже крутился на Google Cloud. Когда облако упало, он умер вместе с ним, и мониторинг ослеп. Команды поддержки почти час работали вслепую.
Инцидент случился потому, что не было feature flag на новом коде, не было проверки на null в критическом пути, данные с багом мгновенно разлетелись по всему миру, восстановление не имело задержек и мониторинг был привязан к той же инфраструктуре, которую он должен был отслеживать.
После этого Google заморозил изменения в Service Control, начал переделывать систему так, чтобы компоненты могли падать независимо, добавляет задержки в репликацию, чтобы отлавливать некорректные данные, и строит отдельный мониторинг, который будет жить вне основной системы.
В итоге вся история показала, что один пропущенный if может уронить одну из самых сложных платформ в мире. Не атака и не катастрофа, а просто null pointer.
В распределённых системах локальный сбой превращается в глобальную аварию быстрее, чем человек успевает отреагировать. Инфраструктура, которая даёт масштаб, так же усиливает и ошибки.
Любой запрос к API всегда на расстоянии одного непроверенного значения от фатала. Это и есть реальность современной облачной архитектуры.
Полный отчет о происшествии: ссылка
👉 @BackendPortal
YAML известен тем, что он не друг никому и враг почти каждому. Попробуй это, чтобы понять, друг он тебе или враг.
Тест, с которым можно проверить, насколько хорошо ты знаешь YAML-файлы, а насколько нет :)
👉 @BackendPortal
Интересный кейс поведенческого таргетинга с динтаблицами
Система поведенческого таргетинга — это сотни тысяч событий в секунду, требование обработки в режиме exactly-once, жёсткие ограничения по времени отклика, компромисс между скоростью и экономией ресурсов.
Разработчик YTsaurus рассказал, как построить такую систему на основе динамических таблиц. А также:
🔸 почему таблицы в памяти иногда тормозят из-за аллокатора
🔸 зачем внедрять xdelta
🔸 как именно устроены агрегатные колонки
🔸 как добиться, чтобы миллисекунды отклика в 99,9 перцентиле стали реальностью
Если нужно построить систему, способную обрабатывать миллиарды событий в день, — описанный подход давно работает в продакшне и позволяет решать реальные задачи.
Читать здесь: ссылка
👉 @BackendPortal
Что происходит, когда ты вводишь URL в браузер?
В процессе участвуют сам браузер, ОС компьютера, провайдер, сервер, где лежит сайт, и сервисы на этом сервере.
1. Вводишь адрес
Например: https://somewebsite.com/page
https://
— схема, говорит браузеру подключаться через TLS.somewebsite.com
— домен, который указывает на IP сервера./page
— путь к нужному ресурсу.
2. Поиск IP по домену
Браузер должен понять, к какому серверу идти. Для этого делается DNS-запрос. Сначала проверяются кэши (браузера, ОС, провайдера). Если IP нет, DNS идёт по цепочке: от корневых серверов до нужного уровня домена.
3. Установка TCP-соединения
Через TCP пакеты проходят по маршруту: браузер → роутер → провайдер → интернет-узлы → сервер с нужным IP. Чтобы ускорить процесс, сайты часто используют CDN — они кэшируют контент ближе к пользователю.
4. Отправка HTTP-запроса
После соединения браузер отправляет HTTP(s) запрос: стартовая строка (например, GET /page HTTP/1.1), заголовки, тело. По этим данным сервер понимает, что именно нужно клиенту.
5. Обработка запроса сервером
Сервер получает запрос, смотрит на заголовки, путь и тело. Для GET /page сервер находит ресурс, формирует ответ и возвращает его с HTTP-статусом (например, 200 OK).
6. Рендеринг ответа
Браузер получает ответ и заголовки, например Content-Type: text/html. Это говорит, что в теле HTML. Дальше браузер парсит HTML, подтягивает CSS, JS, картинки и строит финальную страницу.
👉 @BackendPortal
Вкратце современный стек разработки ПО
👉 @BackendPortal
Чем отличаются друг от друга архитектурные паттерны MVC, MVP, MVVM, MVVM-C и VIPER?
👉 @BackendPortal