Недавно написал небольшую статью на английском про асинхронность в C#: async/await, Task, SynchronizationContext, ConfigureAwait, CPU-bound vs I/O-bound, отмена задач и async-стримы.
Если комфортно читать на английском — тут подробно разобрал, как об этом всём правильно думать и какие ошибки чаще всего встречаю в коде:
👉 ссылка на пост в LinkedIn
Если интересно, могу позже сделать разбор отдельных тем уже на русском, с примерами кода и живыми кейсами.
Если комфортно читать на английском — тут подробно разобрал, как об этом всём правильно думать и какие ошибки чаще всего встречаю в коде:
👉 ссылка на пост в LinkedIn
Если интересно, могу позже сделать разбор отдельных тем уже на русском, с примерами кода и живыми кейсами.
Многие спрашивают, как сейчас получать международные выплаты разработчику, если ты живёшь не в ЕС/США и половина финтеха тебе недоступна.
Я написал небольшой пост в LinkedIn про реальный опыт: какие сервисы работают, где подводные камни и как минимизировать риски при выводе денег.
👉 Читаем здесь
Я написал небольшой пост в LinkedIn про реальный опыт: какие сервисы работают, где подводные камни и как минимизировать риски при выводе денег.
👉 Читаем здесь
Как мы ускорили тяжёлые отчёты в разы и не превратили кэш в ещё одну базу
У одного из проектов у меня был классический больной кейс:
🔴 тяжёлые отчёты, которые каждый раз генерировались с нуля.
🔴 сложные фильтры,
🔴 несколько джоинов,
🔴 агрегации,
🔴 и всё это — по живой базе при каждом просмотре, даже если сами отчеты меняется очень редко.
Пользователи могли запускать один и тот же отчёт по нескольку раз в день, а бэкенд каждый раз честно пересчитывал всё с нуля.
Результат предсказуем: отчёт открывается долго, база страдает, сервер грустит.
Очевидное решение — закешировать результат. Но тут важно не попасть в другую крайность:
Кэш — это не вторая база, в которой мы начинаем хранить «вообще всё».
Я пошёл по пути адресного кеширования именно этого отчёта.
Схема была такая:
Берём модель фильтра (даты, статусы, типы, пользовательские параметры).
Берём тип отчёта / модель отчёта (например, RetentionReport, FunnelReport и т.п.).
На основе этого генерируем детерминированный ключ кэша.
По сути:
Важно:
Фильтр нормализуется (сортировка списков, приведение дат к единому формату),
чтобы два одинаковых запроса не давали разные ключи из-за мелочей.
В ключе нет ничего лишнего: только то, что реально влияет на результат отчёта.
Как это работало
Алгоритм стал простым:
1. Приходит запрос на отчёт.
2. Строим cacheKey из модели отчёта + фильтра.
3. Сначала идём в кэш:
▪️если есть — возвращаем сохранённый результат почти моментально;
▪️если нет — генерируем отчёт как раньше, сохраняем в кэш, отдаём пользователю.
Для пользователя магия:
▪️первый запрос может быть тяжёлым,
▪️все повторные — почти мгновенные.
Где грань: что кешировать, а что нет
Здесь было важно не превратить кэш в помойку:
Не кешируем всё подряд («вдруг понадобится»).
Поставили:
▪️TTL (время жизни) — отчёт живёт в кэше ограниченное время;
▪️ограничения по размеру — чтобы кэш не разрастался бесконтрольно.
Плюс добавили простой механизм инвалидации:
если данные в базе по этому домену сильно поменялись (например, пересчитали что-то массово) — можно целиком сбросить кэш по конкретному типу отчёта.
Что это дало
После включения кеширования:
▪️время генерации повторных отчётов сократилось с десятков секунд до долей секунды;
▪️нагрузка на базу в пиках заметно упала;
▪️пользователи перестали жать F5 и «проверять, может уже посчиталось».
И при этом:
мы не превратили кэш в ещё один источник данных;
логика данных осталась в базе и доменной модели,
кэш — только ускоритель, а не новый слой бизнес-логики.
Плюс у нас были механизмы, когда Redis не работает переключался на IMemoryCache
Вывод
Кэш — мощный инструмент, когда:
▪️ты чётко понимаешь, что именно и зачем кешируешь;
▪️ключ строится детерминированно из входящих параметров;
▪️кэш — это слой оптимизации, а не очередной «хранилище на всякий случай».
У одного из проектов у меня был классический больной кейс:
🔴 тяжёлые отчёты, которые каждый раз генерировались с нуля.
🔴 сложные фильтры,
🔴 несколько джоинов,
🔴 агрегации,
🔴 и всё это — по живой базе при каждом просмотре, даже если сами отчеты меняется очень редко.
Пользователи могли запускать один и тот же отчёт по нескольку раз в день, а бэкенд каждый раз честно пересчитывал всё с нуля.
Результат предсказуем: отчёт открывается долго, база страдает, сервер грустит.
Очевидное решение — закешировать результат. Но тут важно не попасть в другую крайность:
Кэш — это не вторая база, в которой мы начинаем хранить «вообще всё».
Я пошёл по пути адресного кеширования именно этого отчёта.
Схема была такая:
Берём модель фильтра (даты, статусы, типы, пользовательские параметры).
Берём тип отчёта / модель отчёта (например, RetentionReport, FunnelReport и т.п.).
На основе этого генерируем детерминированный ключ кэша.
По сути:
cacheKey = Hash(ReportModel + FilterModel)
Важно:
Фильтр нормализуется (сортировка списков, приведение дат к единому формату),
чтобы два одинаковых запроса не давали разные ключи из-за мелочей.
В ключе нет ничего лишнего: только то, что реально влияет на результат отчёта.
Как это работало
Алгоритм стал простым:
1. Приходит запрос на отчёт.
2. Строим cacheKey из модели отчёта + фильтра.
3. Сначала идём в кэш:
▪️если есть — возвращаем сохранённый результат почти моментально;
▪️если нет — генерируем отчёт как раньше, сохраняем в кэш, отдаём пользователю.
Для пользователя магия:
▪️первый запрос может быть тяжёлым,
▪️все повторные — почти мгновенные.
Где грань: что кешировать, а что нет
Здесь было важно не превратить кэш в помойку:
Не кешируем всё подряд («вдруг понадобится»).
Поставили:
▪️TTL (время жизни) — отчёт живёт в кэше ограниченное время;
▪️ограничения по размеру — чтобы кэш не разрастался бесконтрольно.
Плюс добавили простой механизм инвалидации:
если данные в базе по этому домену сильно поменялись (например, пересчитали что-то массово) — можно целиком сбросить кэш по конкретному типу отчёта.
Что это дало
После включения кеширования:
▪️время генерации повторных отчётов сократилось с десятков секунд до долей секунды;
▪️нагрузка на базу в пиках заметно упала;
▪️пользователи перестали жать F5 и «проверять, может уже посчиталось».
И при этом:
мы не превратили кэш в ещё один источник данных;
логика данных осталась в базе и доменной модели,
кэш — только ускоритель, а не новый слой бизнес-логики.
Плюс у нас были механизмы, когда Redis не работает переключался на IMemoryCache
Вывод
Кэш — мощный инструмент, когда:
▪️ты чётко понимаешь, что именно и зачем кешируешь;
▪️ключ строится детерминированно из входящих параметров;
▪️кэш — это слой оптимизации, а не очередной «хранилище на всякий случай».
👍1👎1
В .NET-проектах мы постоянно перекладываем данные между моделями, DTO и ViewModel’ами — но редко задумываемся, сколько реально стоит эта "маппинговая магия".
В новой статье на Medium я сравнил производительность трёх подходов: Manual Mapping, AutoMapper и Mapperly.
Что внутри:
краткий обзор каждого инструмента и сценариев использования
бенчмарки на 100k объектов (время и память с BenchmarkDotNet)
где AutoMapper действительно проигрывает, а где это не имеет значения
когда есть смысл перейти на Mapperly или остаться на ручном маппинге
Читайте полную версию здесь 👉 ссылка на медиум
В новой статье на Medium я сравнил производительность трёх подходов: Manual Mapping, AutoMapper и Mapperly.
Что внутри:
краткий обзор каждого инструмента и сценариев использования
бенчмарки на 100k объектов (время и память с BenchmarkDotNet)
где AutoMapper действительно проигрывает, а где это не имеет значения
когда есть смысл перейти на Mapperly или остаться на ручном маппинге
Читайте полную версию здесь 👉 ссылка на медиум
Я разработал готовый к использованию шаблон приложения Blazor, построенный в соответствии с принципами чистой архитектуры.
Архитектура строго разделяет домен, приложение и инфраструктуру, исключая репозитории и UnitOfWorks — доступ к данным максимально прозрачен.
Для автоматизации процесса создания и отправки запросов я использовал Refit.
Я создал отдельный проект для миграций, проект для фоновых задач и централизованное логирование через NLog.
Шаблон ориентирован на масштабируемость, тестируемость и долгосрочную поддержку — его можно использовать в качестве основы для реальных коммерческих проектов.
Шаблон разрабатывается как проект с открытым исходным кодом.
Если вам нравится идея чистой, прагматичной архитектуры без лишней «магии», присоединяйтесь к разработке CleanArchitecture.Starter.
Проект открыт для идей, запросов на слияние и обсуждений.
Вы можете работать над покрытием кода тестами или писать документацию для решения.
Даже небольшие улучшения или критика — это вклад.
Звезды, форки, issues и PR — все приветствуется.
Работа над CleanArchitecture.Starter дает реальные преимущества:
🚀 Практический опыт: архитектура для производственных проектов, Blazor + .NET 10, Identity, Kafka, Redis, NLog.
🧠 Развитие как разработчика: работа с чистой архитектурой, тестируемым кодом и сложными интеграциями.
🤝 Сообщество и сотрудничество: общение с опытными разработчиками, обмен знаниями и совместная работа над проверками кода.
⭐️ Портфолио и репутация: запрос на слияние для реального проекта с хорошей архитектурой — это большой плюс для вашего резюме и GitHub.
💡 Влияние на проект: ваши идеи и улучшения остаются в кодовой базе, становясь частью шаблона для других.
Структура решения
Domain:
— Бизнес-модель
— Инварианты
— Полная изоляция
Application:
— Варианты использования
— DTO
— Бизнес-логика
— Без привязки к инфраструктуре
Infrastructure:
— EF Core
— Identity
— Redis
— Kafka
— NLog
Приложение Blazor WASM:
— Пользовательский интерфейс / Конечные точки
— Минимальная логика
Migrations:
— Чистые миграции
— Отсутствие «загрязнения» инфраструктуры
Jobs:
— Фоновые задачи
— Изолировано от API
Тесты:
— Архитектурные тесты (Проверяет отсутствие ненужных зависимостей в слоях. Используется NetArchTest)
— Модульные тесты для приложения, домена и инфраструктуры (XUnit)
— Тесты Playwright для веб-приложений
Для использования шаблона достаточно установить из Nuget:
и создать проект:
Репозиторий проекта: https://github.com/Annaklychev/CleanArchitecture.Template
Канал Telegram для связи по проекту: @CleanArchitecture_Template
Будущие цели (дорожная карта)
— Создание инфраструктуры для Redis и Kafka
— Внедрение механизма идентификации пользователей по Fingerprint
— Настройка HealthChecks
Архитектура строго разделяет домен, приложение и инфраструктуру, исключая репозитории и UnitOfWorks — доступ к данным максимально прозрачен.
Для автоматизации процесса создания и отправки запросов я использовал Refit.
Я создал отдельный проект для миграций, проект для фоновых задач и централизованное логирование через NLog.
Шаблон ориентирован на масштабируемость, тестируемость и долгосрочную поддержку — его можно использовать в качестве основы для реальных коммерческих проектов.
Шаблон разрабатывается как проект с открытым исходным кодом.
Если вам нравится идея чистой, прагматичной архитектуры без лишней «магии», присоединяйтесь к разработке CleanArchitecture.Starter.
Проект открыт для идей, запросов на слияние и обсуждений.
Вы можете работать над покрытием кода тестами или писать документацию для решения.
Даже небольшие улучшения или критика — это вклад.
Звезды, форки, issues и PR — все приветствуется.
Работа над CleanArchitecture.Starter дает реальные преимущества:
🚀 Практический опыт: архитектура для производственных проектов, Blazor + .NET 10, Identity, Kafka, Redis, NLog.
🧠 Развитие как разработчика: работа с чистой архитектурой, тестируемым кодом и сложными интеграциями.
🤝 Сообщество и сотрудничество: общение с опытными разработчиками, обмен знаниями и совместная работа над проверками кода.
⭐️ Портфолио и репутация: запрос на слияние для реального проекта с хорошей архитектурой — это большой плюс для вашего резюме и GitHub.
💡 Влияние на проект: ваши идеи и улучшения остаются в кодовой базе, становясь частью шаблона для других.
Структура решения
Domain:
— Бизнес-модель
— Инварианты
— Полная изоляция
Application:
— Варианты использования
— DTO
— Бизнес-логика
— Без привязки к инфраструктуре
Infrastructure:
— EF Core
— Identity
— Redis
— Kafka
— NLog
Приложение Blazor WASM:
— Пользовательский интерфейс / Конечные точки
— Минимальная логика
Migrations:
— Чистые миграции
— Отсутствие «загрязнения» инфраструктуры
Jobs:
— Фоновые задачи
— Изолировано от API
Тесты:
— Архитектурные тесты (Проверяет отсутствие ненужных зависимостей в слоях. Используется NetArchTest)
— Модульные тесты для приложения, домена и инфраструктуры (XUnit)
— Тесты Playwright для веб-приложений
Для использования шаблона достаточно установить из Nuget:
dotnet new install CleanArchitecture.Starter
и создать проект:
dotnet new cleanarchitecture -n MyApp
Репозиторий проекта: https://github.com/Annaklychev/CleanArchitecture.Template
Канал Telegram для связи по проекту: @CleanArchitecture_Template
Будущие цели (дорожная карта)
— Создание инфраструктуры для Redis и Kafka
— Внедрение механизма идентификации пользователей по Fingerprint
— Настройка HealthChecks
👍1
