Отличные ресурсы по системному дизайну
http://github.com/ashishps1/awesome-system-design-resources
Что внутри:
1. Базовые концепции — теорема CAP, консистентное хеширование, масштабируемость, доступность
2. Основы сетей — DNS, балансировка нагрузки, модель OSI, WebSockets
3. Глубокое погружение в базы данных — шардирование, репликация, SQL vs NoSQL
4. Разборы реальных систем — Twitter, Netflix, Uber, Google Drive
5. Включены диаграммы и примеры реализации кода
👉 @KodBlog
http://github.com/ashishps1/awesome-system-design-resources
Что внутри:
1. Базовые концепции — теорема CAP, консистентное хеширование, масштабируемость, доступность
2. Основы сетей — DNS, балансировка нагрузки, модель OSI, WebSockets
3. Глубокое погружение в базы данных — шардирование, репликация, SQL vs NoSQL
4. Разборы реальных систем — Twitter, Netflix, Uber, Google Drive
5. Включены диаграммы и примеры реализации кода
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🍌2
Большинство .NET-разработчиков используют
Но очень немногие делают его поведением по умолчанию — и тем самым теряют реальную производительность.
Вот полная картина query tracking в EF Core, которую редко объясняют в одном месте.
Три метода. Одна цель: не трекать то, что вы не собираетесь менять.
AsNoTracking()
Самый быстрый вариант. Полностью обходит change tracker.
Идеален для любых read-only запросов — списки, результаты поиска, отчёты, фоновые задачи.
Есть один нюанс: если одна и та же сущность появляется через разные join’ы, EF Core создаст два разных C#-объекта в памяти вместо одного.
Это может незаметно ломать поведение.
AsNoTrackingWithIdentityResolution()
Даёт тот же плюс отсутствия трекинга, но решает проблему дубликатов объектов.
EF Core по-прежнему не использует change tracker, но поддерживает identity map, благодаря чему одна и та же сущность всегда соответствует одному и тому же C#-объекту.
Немного медленнее, чем чистый
UseQueryTrackingBehavior()
Перестаньте писать
Задайте это глобально в
Теперь все запросы по умолчанию — без трекинга.
А там, где нужно изменение данных, явно используйте
Используйте NoTracking, когда:
→ read-only API endpoint’ы
→ пагинация и списки
→ отчёты и экспорт данных
→ чтение данных в фоновых задачах
→ страницы с результатами поиска
Оставляйте tracking, когда:
→ загружаете сущность для последующего обновления
→ после запроса вызывается
→ мутируете сложные графы объектов
→ работаете с disconnected-сценариями сущностей
👉 @KodBlog
AsNoTracking() время от времени — когда вспоминают.Но очень немногие делают его поведением по умолчанию — и тем самым теряют реальную производительность.
Вот полная картина query tracking в EF Core, которую редко объясняют в одном месте.
Три метода. Одна цель: не трекать то, что вы не собираетесь менять.
AsNoTracking()
Самый быстрый вариант. Полностью обходит change tracker.
Идеален для любых read-only запросов — списки, результаты поиска, отчёты, фоновые задачи.
Есть один нюанс: если одна и та же сущность появляется через разные join’ы, EF Core создаст два разных C#-объекта в памяти вместо одного.
Это может незаметно ломать поведение.
AsNoTrackingWithIdentityResolution()
Даёт тот же плюс отсутствия трекинга, но решает проблему дубликатов объектов.
EF Core по-прежнему не использует change tracker, но поддерживает identity map, благодаря чему одна и та же сущность всегда соответствует одному и тому же C#-объекту.
Немного медленнее, чем чистый
AsNoTracking, но правильный выбор для сложных запросов с Include.UseQueryTrackingBehavior()
Перестаньте писать
AsNoTracking в каждом запросе.Задайте это глобально в
DbContext при старте приложения.Теперь все запросы по умолчанию — без трекинга.
А там, где нужно изменение данных, явно используйте
AsTracking().Используйте NoTracking, когда:
→ read-only API endpoint’ы
→ пагинация и списки
→ отчёты и экспорт данных
→ чтение данных в фоновых задачах
→ страницы с результатами поиска
Оставляйте tracking, когда:
→ загружаете сущность для последующего обновления
→ после запроса вызывается
SaveChanges→ мутируете сложные графы объектов
→ работаете с disconnected-сценариями сущностей
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥3😁1
DelegatingHandlers позволяют добавлять поведение до или после выполнения запросов через HttpClient.
Это как middleware, но для исходящих (outgoing) запросов.
Отличный кейс использования этой возможности?
Добавление заголовков аутентификации к запросу.👇
👉 @KodBlog
Это как middleware, но для исходящих (outgoing) запросов.
Отличный кейс использования этой возможности?
Добавление заголовков аутентификации к запросу.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5👀2
Однажды я заплатил $40 за .NET-приложение с самописным ORM.
В нём были SQL-инъекции, сломанные транзакции и ноль тестов.
Вот 6 ORM-репозиториев, которые действительно стоят внимания:
1. Entity Framework Core - https://github.com/dotnet/efcore
2. Dapper - https://github.com/DapperLib/Dapper
3. NHibernate - https://github.com/nhibernate/nhibernate-core
4. linq2db - https://github.com/linq2db/linq2db
5. Marten - https://github.com/JasperFx/marten
6. SqlKata - https://github.com/sqlkata/querybuilder
👉 @KodBlog
В нём были SQL-инъекции, сломанные транзакции и ноль тестов.
Вот 6 ORM-репозиториев, которые действительно стоят внимания:
1. Entity Framework Core - https://github.com/dotnet/efcore
2. Dapper - https://github.com/DapperLib/Dapper
3. NHibernate - https://github.com/nhibernate/nhibernate-core
4. linq2db - https://github.com/linq2db/linq2db
5. Marten - https://github.com/JasperFx/marten
6. SqlKata - https://github.com/sqlkata/querybuilder
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4😁2👍1
Этот динамический парсинг JSON уже не раз меня подводил — я обычно кастую к
В JSON.NET (
Не первый раз на это натыкаюсь, и, скорее всего, не последний😂
👉 @KodBlog
string через as string, где это возможно.В JSON.NET (
JObject / JValue) значения ведут себя как dynamic при цепочках обращений, но при этом не кастуются, потому что по сути это Expando-подобные объекты.Не первый раз на это натыкаюсь, и, скорее всего, не последний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🤩1🍾1
Модули в модульном монолите могут взаимодействовать только через свой публичный API.
Но как это обеспечить на уровне архитектуры?
С помощью тестирования архитектуры.
Вот пример кода с тестом, который проверяет направление зависимостей между модулями.
👉 @KodBlog
Но как это обеспечить на уровне архитектуры?
С помощью тестирования архитектуры.
Вот пример кода с тестом, который проверяет направление зависимостей между модулями.
[Fact]
public void TicketingModule_ShouldNotHaveDependencyOn_AnyOtherModule()
{
string[] otherModules = [
UsersNamespace,
EventsNamespace,
AttendanceNamespace
];
string[] integrationEventsModules = [
UsersIntegrationEventsNamespace,
EventsIntegrationEventsNamespace,
AttendanceIntegrationEventsNamespace
];
List<Assembly> ticketingAssemblies =
[
typeof(Order).Assembly,
Modules.Ticketing.Application.AssemblyReference.Assembly,
Modules.Ticketing.Presentation.AssemblyReference.Assembly,
typeof(TicketingModule).Assembly
];
Types.InAssemblies(ticketingAssemblies)
.That()
.DoNotHaveDependencyOnAny(integrationEventsModules)
.Should()
.NotHaveDependencyOnAny(otherModules)
.GetResult()
.ShouldBeSuccessful();
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2🍾1
Кто-то собрал все задачи с LeetCode, сгруппированные по компаниям, и выложил это в публичный репозиторий на GitHub. Более 300 компаний: Google, Meta, Amazon, Jane Street, Citadel, Stripe, Snowflake — практически каждый крупный работодатель в техе, у каждого своя папка.
Внутри папок всё отсортировано по тому, насколько недавно задача встречалась — за последние 30 дней, 60 дней, 90 дней и за всё время.
Это означает, что если у тебя интервью в Google в пятницу, ты можешь открыть папку Google и увидеть, какие задачи они задавали в этом месяце.
Не самая приятная правда, которая здесь вскрывается: компании месяцами переиспользуют одни и те же задачи. Интервью — это не непредсказуемое испытание, а набор паттернов. А паттерны можно изучать.
Большинство кандидатов решают 500 случайных задач на LeetCode и надеются на лучшее. Те, кто знают про этот репозиторий, тратят то же время на 30 задач, которые их целевая компания задавала за последние 30 дней.
Те же усилия. Совершенно другие шансы.
Технические интервью больше вознаграждают подготовку, чем способности — сильнее, чем кто-либо в найме готов это признать. Этот репозиторий просто делает это очевидным.
Лично я этим не пользовался, делюсь на случай, если кому-то пригодится.
P.S. Мы всё ещё решаем LeetCode в 2026?
👉 @KodBlog
Внутри папок всё отсортировано по тому, насколько недавно задача встречалась — за последние 30 дней, 60 дней, 90 дней и за всё время.
Это означает, что если у тебя интервью в Google в пятницу, ты можешь открыть папку Google и увидеть, какие задачи они задавали в этом месяце.
Не самая приятная правда, которая здесь вскрывается: компании месяцами переиспользуют одни и те же задачи. Интервью — это не непредсказуемое испытание, а набор паттернов. А паттерны можно изучать.
Большинство кандидатов решают 500 случайных задач на LeetCode и надеются на лучшее. Те, кто знают про этот репозиторий, тратят то же время на 30 задач, которые их целевая компания задавала за последние 30 дней.
Те же усилия. Совершенно другие шансы.
Технические интервью больше вознаграждают подготовку, чем способности — сильнее, чем кто-либо в найме готов это признать. Этот репозиторий просто делает это очевидным.
Лично я этим не пользовался, делюсь на случай, если кому-то пригодится.
P.S. Мы всё ещё решаем LeetCode в 2026?
Please open Telegram to view this post
VIEW IN TELEGRAM
😁3🤔1
Пользовательские SQL-запросы в миграциях EF? Это возможно.
Пользовательские SQL-команды полезны, когда мы не можем выразить что-то через Fluent API EF.
Я использовал их для переноса данных из одного столбца в другой или для создания сложных индексов.
- Создайте пустую миграцию
- Напишите свои SQL-запросы
👉 @KodBlog
Пользовательские SQL-команды полезны, когда мы не можем выразить что-то через Fluent API EF.
Я использовал их для переноса данных из одного столбца в другой или для создания сложных индексов.
- Создайте пустую миграцию
- Напишите свои SQL-запросы
public partial class Update_Products : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("<YOUR CUSTOM SQL HERE>");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// You are also responsible for reverting any changes.
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3👍2
Младший разработчик использует EF Core напрямую в контроллере.
Средний разработчик использует BaseRepository, IUnitOfWork, IOrderRepository, IOrderDataAccess, IOrderQueryBuilder.
Старший разработчик использует EF Core без репозитория.
Когда ваше приложение маленькое, использование паттерна Репозиторий кажется простым.
То, что начинается как простая операция CRUD с 4 методами, быстро превращается в большой класс с запросами для чтения и записи в базу данных для всех возможных случаев.
По мере роста вашего домена вы сталкиваетесь со следующими проблемами:
- Вы создаете репозиторий для каждой новой сущности
- Куда относятся методы, работающие с несколькими сущностями?
- Вы создаете репозитории с 10-20 методами
Я часто слышу следующие распространенные оправдания для использования репозиториев:
𝟭. "Мы можем позже сменить базу данных."
В 99% случаев вам не нужно будет менять базу данных.
- Переход от одной SQL базы данных к другой? EF Core справится с этим.
- Использование хранимых процедур, триггеров? Тогда их нужно переписать на уровне базы данных.
- Переход от реляционной базы данных к документной? Вам нужно будет изменить модель данных, запросы и шаблоны доступа. Вы не сможете просто заменить реализации репозиториев.
𝟮. "Это облегчает тестирование."
Некоторые утверждают, что мокирование репозитория проще, чем мокирование DbContext.
Но это скрывает большую проблему: вы тестируете абстракцию абстракции.
Мокирование репозиториев часто приводит к хрупким тестам, которые не отражают реальное поведение запросов.
Вместо этого лучше использовать реальный EF Core с базой данных в памяти или, что еще лучше, писать интеграционные тесты.
𝟯. "Это обеспечивает разделение ответственностей."
Репозитории часто используются для того, чтобы держать бизнес-слой отдельно от EF Core. Но на практике это разделение создает больше путаницы, чем ясности.
По мере того как ваши фичи растут, вам приходится:
- Внедрять несколько репозиториев в ваши сервисы
- Или переносить логику, работающую с несколькими сущностями, в жирный репозиторий
Вместо чистого разделения вы получаете больше косвенных ссылок, больше шаблонного кода и трудный для понимания код.
DbContext в EF Core уже реализует паттерны Репозиторий и Unit of Work, как указано в официальном описании кода DbContext.
Я использую EF Core напрямую в кейсах использования и сервисах моего приложения. Это прагматичный подход, который приносит мне больше преимуществ, чем недостатков.
👉 @KodBlog
Средний разработчик использует BaseRepository, IUnitOfWork, IOrderRepository, IOrderDataAccess, IOrderQueryBuilder.
Старший разработчик использует EF Core без репозитория.
Когда ваше приложение маленькое, использование паттерна Репозиторий кажется простым.
То, что начинается как простая операция CRUD с 4 методами, быстро превращается в большой класс с запросами для чтения и записи в базу данных для всех возможных случаев.
По мере роста вашего домена вы сталкиваетесь со следующими проблемами:
- Вы создаете репозиторий для каждой новой сущности
- Куда относятся методы, работающие с несколькими сущностями?
- Вы создаете репозитории с 10-20 методами
Я часто слышу следующие распространенные оправдания для использования репозиториев:
𝟭. "Мы можем позже сменить базу данных."
В 99% случаев вам не нужно будет менять базу данных.
- Переход от одной SQL базы данных к другой? EF Core справится с этим.
- Использование хранимых процедур, триггеров? Тогда их нужно переписать на уровне базы данных.
- Переход от реляционной базы данных к документной? Вам нужно будет изменить модель данных, запросы и шаблоны доступа. Вы не сможете просто заменить реализации репозиториев.
𝟮. "Это облегчает тестирование."
Некоторые утверждают, что мокирование репозитория проще, чем мокирование DbContext.
Но это скрывает большую проблему: вы тестируете абстракцию абстракции.
Мокирование репозиториев часто приводит к хрупким тестам, которые не отражают реальное поведение запросов.
Вместо этого лучше использовать реальный EF Core с базой данных в памяти или, что еще лучше, писать интеграционные тесты.
𝟯. "Это обеспечивает разделение ответственностей."
Репозитории часто используются для того, чтобы держать бизнес-слой отдельно от EF Core. Но на практике это разделение создает больше путаницы, чем ясности.
По мере того как ваши фичи растут, вам приходится:
- Внедрять несколько репозиториев в ваши сервисы
- Или переносить логику, работающую с несколькими сущностями, в жирный репозиторий
Вместо чистого разделения вы получаете больше косвенных ссылок, больше шаблонного кода и трудный для понимания код.
DbContext в EF Core уже реализует паттерны Репозиторий и Unit of Work, как указано в официальном описании кода DbContext.
Я использую EF Core напрямую в кейсах использования и сервисах моего приложения. Это прагматичный подход, который приносит мне больше преимуществ, чем недостатков.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11💯6👎2❤1👏1
𝟭𝟱 инструментов и библиотек .NET, которые использует сообщество
Я задал в r/dotnet один вопрос: какие библиотеки .NET вы реально используете, о которых никто не говорит?
Тред собрал сотни ответов. Я отфильтровал те, у которых 10+ апвоутов.
CSharpier набрал 62 голоса. Refit — на втором месте. Дальше — набор инструментов с 30–31 голосами, о большинстве из которых разработчики либо не знают, либо почти не упоминают в туториалах.
Несколько моментов, которые бросились в глаза:
Сообщество стало скептически относиться к зависимостям. FluentAssertions, IdentityServer и MediatR — к ним уже нет прежнего доверия. Самый залайканный комментарий (не про библиотеку) был: «Batteries included, stay away from dependencies» (используй встроенные возможности и избегай лишних зависимостей). Этот контекст важен при чтении списка.
LINQPad — это не NuGet-пакет, но он постоянно всплывал в обсуждении. Команды строят на нём целые вспомогательные воркфлоу.
Vogen меня удивил. Type-safe value objects, генерируемые на этапе компиляции без runtime-оверхеда. Я видел баги в продакшене из-за путаницы с orderId/customerId — это как раз решает такие проблемы.
👉 @KodBlog
Я задал в r/dotnet один вопрос: какие библиотеки .NET вы реально используете, о которых никто не говорит?
Тред собрал сотни ответов. Я отфильтровал те, у которых 10+ апвоутов.
CSharpier набрал 62 голоса. Refit — на втором месте. Дальше — набор инструментов с 30–31 голосами, о большинстве из которых разработчики либо не знают, либо почти не упоминают в туториалах.
Несколько моментов, которые бросились в глаза:
Сообщество стало скептически относиться к зависимостям. FluentAssertions, IdentityServer и MediatR — к ним уже нет прежнего доверия. Самый залайканный комментарий (не про библиотеку) был: «Batteries included, stay away from dependencies» (используй встроенные возможности и избегай лишних зависимостей). Этот контекст важен при чтении списка.
LINQPad — это не NuGet-пакет, но он постоянно всплывал в обсуждении. Команды строят на нём целые вспомогательные воркфлоу.
Vogen меня удивил. Type-safe value objects, генерируемые на этапе компиляции без runtime-оверхеда. Я видел баги в продакшене из-за путаницы с orderId/customerId — это как раз решает такие проблемы.
Please open Telegram to view this post
VIEW IN TELEGRAM
😁1
Как версионировать Minimal API?
Нужно определить
Более удобный подход — создать
👉 @KodBlog
Нужно определить
ApiVersionSet и применить его к эндпоинтам.Более удобный подход — создать
route group, назначить ему версию API, а затем использовать этот route group для маппинга отдельных эндпоинтов.Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👀1
Нужно быстро написать небольшой скрипт или прототип на C#?
JetBrains Rider 2026.1 теперь поддерживает file-based C# программы, которые можно запускать без
Доступны:
- полноценный автокомплит (code completion)
- отладка (debugging)
- run-маркеры для top-level statements и
👉 @KodBlog
JetBrains Rider 2026.1 теперь поддерживает file-based C# программы, которые можно запускать без
.csproj.Доступны:
- полноценный автокомплит (code completion)
- отладка (debugging)
- run-маркеры для top-level statements и
#! (shebang)Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👀2
Устанвока:
/plugin marketplace add dotnet/skillsОдна команда — и твой coding-агент получает .NET-суперсилы
Работает в Copilot CLI, VScode, и VS 2026.
https://github.com/dotnet/skills/tree/main/plugins/dotnet-upgrade
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3😁1🥴1
Нужно предотвратить race conditions в базе данных?
Когда несколько запросов могут обновлять одни и те же строки, требуется блокировка.
Типичный пример: фоновые воркеры обрабатывают одну и ту же таблицу.
Забудьте про тяжёлые уровни изоляции. Вместо этого используйте блокировки на уровне строк:
- PostgreSQL:
- SQL Server:
Это можно использовать с «сырым» SQL в EF Core.
Я использовал
Вот как это применить в .NET: https://milanjovanovic.tech/blog/a-clever-way-to-implement-pessimistic-locking-in-ef-core
Важно отметить, что такая блокировка живёт в течение всей транзакции.
👉 @KodBlog
Когда несколько запросов могут обновлять одни и те же строки, требуется блокировка.
Типичный пример: фоновые воркеры обрабатывают одну и ту же таблицу.
Забудьте про тяжёлые уровни изоляции. Вместо этого используйте блокировки на уровне строк:
- PostgreSQL:
FOR UPDATE / FOR UPDATE SKIP LOCKED- SQL Server:
WITH (UPDLOCK)Это можно использовать с «сырым» SQL в EF Core.
Я использовал
SKIP LOCKED, чтобы масштабировать свой Outbox-процессор до более чем 2 млрд сообщений в день.Вот как это применить в .NET: https://milanjovanovic.tech/blog/a-clever-way-to-implement-pessimistic-locking-in-ef-core
Важно отметить, что такая блокировка живёт в течение всей транзакции.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👀2
Автор масштабировал свой outbox-процессор до более чем 2B+ сообщений в день.
Вот 4 вещи, которые он сделал:
- Добавил покрывающий индекс → index-only scan’ы
- Внедрил батчинг при публикации сообщений
- Масштабировал через параллельные процессоры
- Использовал
Реализовать паттерн Outbox — просто.
А вот масштабировать его — уже настоящая задача.
Полный разбор здесь
👉 @KodBlog
Вот 4 вещи, которые он сделал:
- Добавил покрывающий индекс → index-only scan’ы
- Внедрил батчинг при публикации сообщений
- Масштабировал через параллельные процессоры
- Использовал
FOR UPDATE SKIP LOCKED для обработки конкуренцииРеализовать паттерн Outbox — просто.
А вот масштабировать его — уже настоящая задача.
Полный разбор здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👏3
Одна из самых распространенных ошибок, которые я вижу при подключении к существующим проектам:
разработчики помещают всю бизнес-логику внутрь сервисных классов.
Что это дает?
Запутанный клубок из бизнес-правил, запросов к базе данных и логики отправки email, запихнутых в один метод.
Результат:
- Новая фича ломает 3 вещи, которые ты даже не трогал.
- Тесты, где нужно 15 моков, чтобы проверить одно бизнес-правило.
- Мигрень каждый раз, когда клиент говорит: «Просто небольшое изменение…»
Корневая причина? Структура кода.
Но есть 3 простых паттерна из DDD, которые это исправляют.
Кратко, что они дают:
- Один паттерн позволяет тестировать сложные бизнес-правила без единого мока — буквально просто
- Один изолирует логику фильтрации запросов, чтобы переиспользовать ее в разных обработчиках без копирования LINQ-выражений
- Один отделяет сайд-эффекты (email, фоновые задачи, синхронизация с CRM), так что добавление нового сайд-эффекта занимает минуты, а не рефакторинг, который забивает контекст AI-агента за 5 минут
Я использовал все три на легаси-кодовой базе возрастом 7 лет.
Полные примеры кода в статье:
https://thearchitecturelab.com/post/3-ddd-patterns
👉 @KodBlog
разработчики помещают всю бизнес-логику внутрь сервисных классов.
Что это дает?
Запутанный клубок из бизнес-правил, запросов к базе данных и логики отправки email, запихнутых в один метод.
Результат:
- Новая фича ломает 3 вещи, которые ты даже не трогал.
- Тесты, где нужно 15 моков, чтобы проверить одно бизнес-правило.
- Мигрень каждый раз, когда клиент говорит: «Просто небольшое изменение…»
Корневая причина? Структура кода.
Но есть 3 простых паттерна из DDD, которые это исправляют.
Кратко, что они дают:
- Один паттерн позволяет тестировать сложные бизнес-правила без единого мока — буквально просто
new Invoice() и проверка результата- Один изолирует логику фильтрации запросов, чтобы переиспользовать ее в разных обработчиках без копирования LINQ-выражений
- Один отделяет сайд-эффекты (email, фоновые задачи, синхронизация с CRM), так что добавление нового сайд-эффекта занимает минуты, а не рефакторинг, который забивает контекст AI-агента за 5 минут
Я использовал все три на легаси-кодовой базе возрастом 7 лет.
Полные примеры кода в статье:
https://thearchitecturelab.com/post/3-ddd-patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾4❤2
Clean Architecture & Vertical Slices
Clean Architecture дает четкие границы.
Vertical Slice Architecture дает четкие фичи.
Вот как я это организую:
1) Domain — ядро бизнес-логики
Никакого EF Core. Никакого HTTP. Никаких внешних зависимостей. Только чистые бизнес-правила.
Паттерн Result, базовые сущности и доменные модели — все без привязки к фреймворкам.
2) Application — здесь раскрывается VSA
Вместо папки "Services" с 20 файлами — каждый use case имеет свою собственную папку.
Каждый slice = Command/Query → Validator → Handler.
Хочешь понять “Create Todo”? Открываешь одну папку — и все. Без поиска по слоям.
3) Infrastructure — слой реализации
Базы данных, EF Core, кэширование, identity — все детали реализации живут здесь.
Слой Application определяет, что ему нужно (интерфейсы).
Infrastructure предоставляет реализацию. Чистая инверсия зависимостей.
4) Api — тонкий слой, без бизнес-логики
Этот слой принимает запросы и возвращает ответы. И все.
Минимальные API с endpoint-классами.
Каждый endpoint просто валидирует запрос, передает его в handler и возвращает результат.
5) Tests — защита архитектуры
Юнит-тесты повторяют структуру feature-папок.
Архитектурные тесты гарантируют, что зависимости всегда направлены внутрь —
Domain не ссылается на Infrastructure, Application не ссылается на Api.
👉 @KodBlog
Clean Architecture дает четкие границы.
Vertical Slice Architecture дает четкие фичи.
Вот как я это организую:
1) Domain — ядро бизнес-логики
Никакого EF Core. Никакого HTTP. Никаких внешних зависимостей. Только чистые бизнес-правила.
📁 Domain
├── 📁 Common
│ ├── AuditableEntity.cs
│ ├── BaseEntity.cs
│ ├── Error.cs
│ ├── PagedResult.cs
│ └── Result.cs
└── 📁 Entities
├── ApplicationUser.cs
└── TodoItem.cs
Паттерн Result, базовые сущности и доменные модели — все без привязки к фреймворкам.
2) Application — здесь раскрывается VSA
Вместо папки "Services" с 20 файлами — каждый use case имеет свою собственную папку.
📁 Application
├── 📁 Abstractions
│ ├── 📁 Data
│ │ └── IAppDbContext.cs
│ ├── 📁 Identity
│ │ ├── ICurrentUser.cs
│ │ └── ITokenService.cs
│ └── 📁 Messaging
│ ├── ICommand.cs
│ ├── ICommandHandler.cs
│ ├── IQuery.cs
│ └── IQueryHandler.cs
├── 📁 Features
│ ├── 📁 Identity
│ │ ├── 📁 Login
│ │ ├── 📁 Register
│ │ └── 📁 RefreshToken
│ └── 📁 Todos
│ ├── 📁 Create
│ │ ├── CreateTodoCommand.cs
│ │ ├── CreateTodoCommandHandler.cs
│ │ └── CreateTodoValidator.cs
└── DependencyInjection.cs
Каждый slice = Command/Query → Validator → Handler.
Хочешь понять “Create Todo”? Открываешь одну папку — и все. Без поиска по слоям.
3) Infrastructure — слой реализации
Базы данных, EF Core, кэширование, identity — все детали реализации живут здесь.
📁 Infrastructure
├── 📁 Persistence
│ ├── 📁 Configurations
│ │ ├── ApplicationUserConfiguration.cs
│ │ └── TodoItemConfiguration.cs
│ ├── 📁 Migrations
│ ├── AppDbContext.cs
│ └── AppDbSeeder.cs
├── 📁 Identity
├── 📁 Caching
└── DependencyInjection.cs
Слой Application определяет, что ему нужно (интерфейсы).
Infrastructure предоставляет реализацию. Чистая инверсия зависимостей.
4) Api — тонкий слой, без бизнес-логики
Этот слой принимает запросы и возвращает ответы. И все.
📁 Api
├── 📁 Endpoints
│ ├── TodoEndpoints.cs
├── 📁 Extensions
│ ├── GlobalExceptionHandler.cs
│ ├── ResultExtensions.cs
│ └── ValidationFilter.cs
└── Program.cs
Минимальные API с endpoint-классами.
Каждый endpoint просто валидирует запрос, передает его в handler и возвращает результат.
5) Tests — защита архитектуры
📁 tests
├── 📁 Application.UnitTests
│ └── 📁 Features/Todos
│ ├── CreateTodoCommandHandlerTests.cs
└── 📁 Architecture.Tests
└── ArchitectureTests.cs
Юнит-тесты повторяют структуру feature-папок.
Архитектурные тесты гарантируют, что зависимости всегда направлены внутрь —
Domain не ссылается на Infrastructure, Application не ссылается на Api.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤4👀2
C# модификатор
Компилятор всегда создаёт временную переменную, если требуется преобразование аргумента, есть неявное преобразование типа аргумента, или если аргумент — это значение, а не переменная.
Пример
👉 @KodBlog
in позволяет компилятору создать временную переменную для аргумента и передать на неё ссылку только для чтения.Компилятор всегда создаёт временную переменную, если требуется преобразование аргумента, есть неявное преобразование типа аргумента, или если аргумент — это значение, а не переменная.
Пример
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8👀2
Пример итерации по словарю с деконструкцией
#dotnet #csharp
👉 @KodBlog
KeyValuePair с осмысленными именами переменных вместо использования Key/Value, что делает код более читаемым.#dotnet #csharp
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11🤔4
Вашему .NET-приложению не обязательно быть медленным в 2026 году.
Кэширование — один из самых простых способов повысить производительность, и
- Очень быстрый
- Простой в использовании
- Не требует внешних зависимостей
Но есть компромиссы:
- Данные теряются при перезапуске
- Ограничено памятью одного сервера
Нужно более надёжное или масштабируемое решение?
Вот гайд по вариантам распределённого кэширования в .NET:
P.S. Не забывайте, что кэширование не решает корневую проблему производительности. Поэтому обязательно сначала проанализируйте и устраните её.
👉 @KodBlog
Кэширование — один из самых простых способов повысить производительность, и
IMemoryCache — самый простой вариант, с которого можно начать:- Очень быстрый
- Простой в использовании
- Не требует внешних зависимостей
Но есть компромиссы:
- Данные теряются при перезапуске
- Ограничено памятью одного сервера
Нужно более надёжное или масштабируемое решение?
Вот гайд по вариантам распределённого кэширования в .NET:
P.S. Не забывайте, что кэширование не решает корневую проблему производительности. Поэтому обязательно сначала проанализируйте и устраните её.
Please open Telegram to view this post
VIEW IN TELEGRAM
🐳2🍾2❤1👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Docker за 2 минуты
Большинство разработчиков используют Docker каждый день, не понимая, что происходит под капотом. Вот всё, что нужно знать.
У Docker есть 3 основных компонента:
1. Docker Client: место, где ты вводишь команды, которые через API общаются с Docker daemon.
2. Docker Host: здесь работает daemon, который делает всю основную работу (сборка образов, запуск контейнеров и управление ресурсами)
3. Docker Registry: хранилище Docker-образов. Docker Hub — публичный, но компании часто поднимают приватные registry.
Вот что происходит, когда ты запускаешь
• Docker подтягивает образ из registry (если его нет локально)
• Docker создаёт новый контейнер из этого образа
• Docker выделяет контейнеру файловую систему с возможностью записи (read-write)
• Docker создаёт сетевой интерфейс для подключения контейнера
• Docker запускает контейнер
Вот и всё.
Client, host и registry могут находиться на разных машинах. Именно поэтому Docker хорошо масштабируется.
Понимание этой архитектуры сильно упрощает дебаг контейнеров — ты точно знаешь, где искать проблему, если что-то ломается.
👉 @KodBlog
Большинство разработчиков используют Docker каждый день, не понимая, что происходит под капотом. Вот всё, что нужно знать.
У Docker есть 3 основных компонента:
1. Docker Client: место, где ты вводишь команды, которые через API общаются с Docker daemon.
2. Docker Host: здесь работает daemon, который делает всю основную работу (сборка образов, запуск контейнеров и управление ресурсами)
3. Docker Registry: хранилище Docker-образов. Docker Hub — публичный, но компании часто поднимают приватные registry.
Вот что происходит, когда ты запускаешь
docker run:• Docker подтягивает образ из registry (если его нет локально)
• Docker создаёт новый контейнер из этого образа
• Docker выделяет контейнеру файловую систему с возможностью записи (read-write)
• Docker создаёт сетевой интерфейс для подключения контейнера
• Docker запускает контейнер
Вот и всё.
Client, host и registry могут находиться на разных машинах. Именно поэтому Docker хорошо масштабируется.
Понимание этой архитектуры сильно упрощает дебаг контейнеров — ты точно знаешь, где искать проблему, если что-то ломается.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🕊2