MediatR против простых сервисных классов в 2026: должна ли смена лицензии вас сдвинуть?
Для нового кода простые сервисные классы - лучший выбор по умолчанию. Смена лицензии MediatR в июле 2025 важна, только если вы выше порога Community в 5 млн долларов или отвергаете copyleft RPL-1.5. Сохраняйте MediatR, когда pipeline behaviors несут реальную нагрузку.
Краткая версия: для нового кода на .NET 11 в 2026 году по умолчанию выбирайте простые сервисные классы и обращайтесь к MediatR только тогда, когда вы действительно опираетесь на его pipeline behaviors. Смена лицензии в июле 2025 - реальная причина для переоценки, но это не та пропасть, какой её изобразили некоторые. Если ваша компания имеет годовой валовой доход менее 5 000 000 долларов, вы можете и дальше бесплатно использовать новейшую версию MediatR в рамках редакции Community, так что вопрос лицензирования вынуждает к решению только более крупные команды или тех, кто не хочет принимать copyleft RPL-1.5 в бесплатной лицензии с открытым исходным кодом. Технический вопрос (нужен ли вам медиатор вообще?) - именно тот, что должен на самом деле определять выбор, и для большинства случаев диспетчеризации запросов вы можете убрать именно косвенность, а не библиотеку.
Версии, упоминаемые по всему тексту: MediatR 12.5.0 - последний выпуск под чистой лицензией Apache 2.0; MediatR 13.0 (выпущен 2025-07-02) и последующая линейка 14.x поставляются под двойной моделью Reciprocal Public License 1.5 / коммерческая лицензия от Lucky Penny Software. Код нацелен на <TargetFramework>net11.0</TargetFramework> с SDK .NET 11 и C# 14.
Что на самом деле изменилось в июле 2025
MediatR и AutoMapper годами поддерживались Джимми Богардом под разрешительной лицензией с открытым исходным кодом. 2025-07-02 он передал обе библиотеки новой компании, Lucky Penny Software, и сменил модель лицензирования. Механизмы, важные для вашего решения:
- Старые версии остаются бесплатными навсегда. MediatR 12.x и более ранние остаются под Apache 2.0 (MIT для некоторых более старых артефактов) и не перелицензируются задним числом. Вы можете зафиксировать
12.5.0и никогда не платить. Загвоздка в том, что для этой линейки вы не получаете обновлений безопасности, исправлений или поддержки. - Новые версии имеют двойную лицензию. MediatR 13.0 и более поздние предлагаются под Reciprocal Public License 1.5 (RPL-1.5) для использования с открытым исходным кодом или под платной коммерческой лицензией.
- Существует бесплатная редакция Community. Компании и частные лица с годовым валовым доходом менее 5 000 000 USD (и не получившие более 10 000 000 внешнего капитала), некоммерческие организации с тем же бюджетом, образовательное использование и непроизводственные среды - все они квалифицируются для бесплатного использования новейшей версии MediatR в рамках уровня Community коммерческого соглашения.
- Платные уровни зависят от размера команды. Standard (1-10 разработчиков), Professional (11-50) и Enterprise (без ограничений), оплата ежемесячно или ежегодно, при этом учитываются только разработчики с “программным доступом”, которые пишут или компилируют код, вызывающий MediatR.
- Проверка лицензии только записывает в журнал. Истёкший или отсутствующий ключ выдаёт предупреждения в журнале, а не сбой во время выполнения. Без блокировки функций, без снижения производительности, без сервера лицензий и без сетевого вызова.
Итак, практическая развилка такова. Небольшая мастерская с доходом ниже 5 млн? Вы можете бесплатно остаться на текущей версии MediatR, и единственное трение - это предупреждение в журнале, пока вы не зарегистрируете ключ Community. Крупнее или хорошо профинансированы? Вы либо платите, либо принимаете взаимные обязательства RPL-1.5, либо уходите. Эта третья группа - именно те, кому стоит читать сравнение “против простых сервисных классов”.
Матрица возможностей с первого взгляда
| Аспект | MediatR 13+ | Простые сервисные классы |
|---|---|---|
| Диспетчеризация запросов | Косвенность через ISender.Send | Прямой вызов метода внедрённого интерфейса |
| Сквозная функциональность | IPipelineBehavior<,> как первоклассный механизм | Декораторы (Scrutor) или явные вызовы |
| Переход к определению из вызывающего кода | Попадает в Send, а не в handler | Попадает в реализацию |
| Безопасность связывания на этапе компиляции | Разрешение во время выполнения, может упасть при первом вызове | Внедрение через конструктор, падает при запуске |
| Уведомления / fan-out | Встроенная публикация INotification | Список handler-ов, написанный вручную |
| Стоимость запуска | Сканирование сборок для регистрации handler-ов | Никакой сверх обычной регистрации DI |
| Накладные расходы на вызов | Аллокация wrapper + поиск в словаре + виртуальная диспетчеризация | Почти ноль, JIT может девиртуализировать |
| Native AOT / trimming | Требует осторожности, регистрация на основе рефлексии | Чисто |
| Лицензия (выше 5 млн дохода) | Коммерческая покупка или RPL-1.5 | Никакой, это ваш собственный код |
| Новый код в 2026 | Только если behaviors несут реальную нагрузку | Выбор по умолчанию |
Строка, которая решает большинство споров, - “сквозная функциональность”. Почти всё остальное в этой таблице говорит в пользу простых сервисных классов. Подлинная и труднозаменимая ценность MediatR - это конвейер: единое место, чтобы обернуть каждый запрос валидацией, журналированием, транзакциями и кешированием. Если вы не используете этот конвейер, вы платите за косвенность и лицензию ради приукрашенного локатора служб.
Как выглядит MediatR и простой эквивалент
Вот каноническая форма MediatR: запрос, handler и вызывающий код, который диспетчеризует через ISender.
// .NET 11, C# 14, MediatR 13+ - request + handler
using MediatR;
public record GetOrderById(int OrderId) : IRequest<OrderDto>;
public sealed class GetOrderByIdHandler(AppDbContext db)
: IRequestHandler<GetOrderById, OrderDto>
{
public async Task<OrderDto> Handle(GetOrderById request, CancellationToken ct)
{
var order = await db.Orders.FindAsync([request.OrderId], ct)
?? throw new OrderNotFoundException(request.OrderId);
return order.ToDto();
}
}
// In an endpoint:
public async Task<OrderDto> Get(int id, ISender sender, CancellationToken ct)
=> await sender.Send(new GetOrderById(id), ct);
Простая версия сворачивает запрос и handler в один метод внедрённого сервиса. Вызывающий код зависит напрямую от интерфейса.
// .NET 11, C# 14 - plain service class, no MediatR
public interface IOrderService
{
Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct);
}
public sealed class OrderService(AppDbContext db) : IOrderService
{
public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
{
var order = await db.Orders.FindAsync([orderId], ct)
?? throw new OrderNotFoundException(orderId);
return order.ToDto();
}
}
// In an endpoint:
public async Task<OrderDto> Get(int id, IOrderService orders, CancellationToken ct)
=> await orders.GetByIdAsync(id, ct);
Простая версия короче, и, что критично, нажатие “перейти к определению” на orders.GetByIdAsync приводит вас к методу, который выполняется. С sender.Send(new GetOrderById(id)) вы попадаете в Send от MediatR и переходите к handler-у угадыванием или окольным путём через “найти реализации”. В небольшой команде эта разница незначительна. В большой кодовой базе с сотнями handler-ов потеря прямой навигации - реальный ежедневный налог, который модель MediatR взимает в обмен на развязку вызывающего кода от типа handler-а. Даёт ли вам что-то эта развязка, зависит от того, заменяет ли кто-нибудь когда-либо handler, не трогая вызывающий код, что на практике редкость.
Регистрация тоже заслуживает сравнения. MediatR сканирует сборки при запуске; простые сервисные классы регистрируются явно, и вы получаете сбой во время запуска (а не при первом запросе), если забудете какой-то из них, что напрямую связано с теми ошибками, что стоят за Unable to resolve service for type while attempting to activate.
// .NET 11, C# 14 - registration, side by side
// MediatR: scan an assembly, register every handler reflectively
builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssemblyContaining<GetOrderById>());
// Plain: explicit, trim-friendly, fails fast at startup if a dep is missing
builder.Services.AddScoped<IOrderService, OrderService>();
Pipeline behavior и как жить без него
Вот где MediatR оправдывает своё место. Pipeline behavior оборачивает каждый запрос, что даёт вам ровно одно место для добавления валидации, журналирования, измерения времени или транзакции.
// .NET 11, C# 14, MediatR 13+ - cross-cutting validation for ALL requests
using MediatR;
public sealed class ValidationBehavior<TRequest, TResponse>(
IEnumerable<IValidator<TRequest>> validators)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken ct)
{
foreach (var validator in validators)
await validator.ValidateAndThrowAsync(request, ct);
return await next(ct);
}
}
Зарегистрированный один раз, этот behavior выполняется перед каждым handler-ом. Заменить это скопированными и вставленными вызовами валидации в каждом простом сервисе было бы регрессом, и это лучший аргумент, чтобы сохранить медиатор. Но вы можете получить то же свойство “обернуть всё” в простом DI с помощью паттерна декоратора, используя Scrutor (под лицензией MIT) для регистрации декоратора вокруг интерфейса:
// .NET 11, C# 14, Scrutor 6.x - a decorator gives you the same cross-cutting hook
public sealed class LoggingOrderService(
IOrderService inner,
ILogger<LoggingOrderService> logger) : IOrderService
{
public async Task<OrderDto> GetByIdAsync(int orderId, CancellationToken ct)
{
logger.LogInformation("Fetching order {OrderId}", orderId);
return await inner.GetByIdAsync(orderId, ct);
}
}
// Registration: decorate the real implementation
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderService>();
Компромисс честен: behavior MediatR универсален для каждого запроса при одной регистрации, тогда как декоратор действует на отдельный интерфейс. Если у вас одна-две сквозные функции и горстка сервисных интерфейсов, декораторы выигрывают в ясности. Если у вас десять behaviors, которые должны единообразно применяться к двумстам типам запросов, универсальный конвейер MediatR действительно требует меньше кода, и это тот сценарий, в котором остаться (и платить или квалифицироваться для Community) оправданно. Для запросных функций, которые на самом деле являются функциями HTTP, учтите, что часть того, что люди помещают в behaviors, относится к middleware или фильтрам, к той же области, что охватывает добавление глобального фильтра исключений в ASP.NET Core 11.
Сколько на самом деле стоит диспетчеризация
Производительность - самый слабый аргумент в любом направлении, и не стоит выбирать только по нему, но полезно знать, где живёт стоимость, чтобы никто не отделывался общими словами. Простой вызов интерфейса - это одна виртуальная диспетчеризация, которую JIT часто может девиртуализировать и встроить; она ничего не аллоцирует и стоит порядка одной наносекунды. Send от MediatR делает больше работы на вызов: он ищет тип запроса в кеше handler-ов, конструирует или извлекает RequestHandlerWrapper, проходит цепочку behaviors через делегаты и разрешает handler из контейнера. Это порядка десятков наносекунд плюс небольшая аллокация на отправку, ещё до того как выполнится тело вашего handler-а.
| Аспект | Send от MediatR | Простой вызов интерфейса |
|---|---|---|
| Разрешение типа в handler | Поиск в словаре на вызов | Никакого, привязано при внедрении |
| Цепочка wrapper / делегатов | Аллоцируется на отправку | Никакой |
| Девиртуализация со стороны JIT | Нет | Часто да |
| Сканирование сборок при запуске | Да, растёт с числом handler-ов | Никакого |
| Порядок величины на вызов | Десятки нс + небольшая аллокация | ~1 нс, ноль аллокаций |
Методологическая честность здесь: на фоне handler-а, который обращается к базе данных или сети, десятки наносекунд незаметны, и вам стоит измерять собственные формы объектов с помощью BenchmarkDotNet, а не доверять обобщённой цифре. Стоимость, которая действительно проявляется на практике, - это запуск. Сканирование сборок для регистрации сотен handler-ов добавляет измеримые миллисекунды к холодному старту, что важно для serverless и относится к тому, с чем вы боретесь при сокращении времени холодного старта AWS Lambda на .NET 11. Явная простая регистрация полностью обходит это и дружелюбнее к trimming и Native AOT, потому что нет рефлексивного обнаружения, которое нужно держать безопасным для trimming.
Деталь, которая решает за вас
Несколько ограничений улаживают этот вопрос ещё до того, как в дело вступает архитектурный вкус.
RPL-1.5 - это настоящая решающая сила, а не цена. Бесплатный вариант с открытым исходным кодом в MediatR 13+ - это Reciprocal Public License 1.5, которая несёт взаимные обязательства copyleft: она спроектирована, чтобы закрыть лазейку SaaS, так что развёртывание сетевой службы, построенной на коде под лицензией RPL, может обязать вас сделать ваш исходный код доступным. Если вы поставляете закрытое коммерческое ПО и находитесь выше порога Community, бесплатная OSS-лицензия для вас на деле непригодна, и “MediatR всё ещё с открытым исходным кодом” в вашем случае вводит в заблуждение. Вы либо покупаете коммерческую лицензию, либо уходите. Для тонкого диспетчера, которым вы на самом деле не пользовались, уйти легко.
Граница в 5 млн дохода и 10 млн капитала щедра. Большинство небольших продуктовых команд, консалтинговых компаний и стартапов оказываются ниже неё и могут и дальше бесплатно использовать новейшую версию MediatR в рамках редакции Community. Если это про вас, лицензия - не повод что-либо вырывать; зарегистрируйте ключ Community, заглушите предупреждение и двигайтесь дальше. Потратить спринт на удаление MediatR, чтобы избежать счёта, который вы не должны, - неверный размен.
Зафиксировать 12.5.0 - реальный вариант с реальной ценой. Apache 2.0 на последней бесплатной версии не истекает. Но вы замораживаетесь на версии, которая не получает патчей безопасности, и наследуете риск сопровождения сами. Это нормально для стабильного внутреннего приложения и опасно для всего, что выставлено в интернет.
Если вы только когда-либо вызываете Send, медиатор вам не нужен. Честная проверка: откройте своё решение и поищите IPipelineBehavior и INotification. Если behaviors ноль и вы не публикуете уведомления, MediatR работает как слой косвенности поверх вызовов методов, и его удаление - механический рефакторинг, который делает код более навигируемым, убирает зависимость и стирает вопрос лицензии одним движением. Это тот же инстинкт рационализации библиотек, что стоит за выбором встроенного варианта в System.Text.Json против Newtonsoft.Json в 2026.
Решение, в одну строку
Для нового кода на .NET 11 в 2026 году пишите простые сервисные классы и внедряйте интерфейсы напрямую: вы получаете переход к определению, связывание при запуске, которое падает быстро, отсутствие накладных расходов на вызов, дружелюбие к trimming и нулевую лицензионную уязвимость. Сохраняйте MediatR только тогда, когда его pipeline behaviors выполняют реальную единообразную работу по множеству типов запросов, и в этом случае решайте осознанно: оставайтесь бесплатно в рамках редакции Community, если вы ниже 5 млн, платите, если вы выше и конвейер оправдывает счёт, и фиксируйте 12.5.0 только как временную меру. Ошибка - относиться к “MediatR стал коммерческим” либо как к не-событию, либо как к пожару пяти тревог. Это ни то ни другое. Это повод спросить себя, нужен ли вам был медиатор вообще, и для большинства кода диспетчеризации запросов ответ всегда был - нет.
Связанное
- Minimal APIs vs controllers in ASP.NET Core 11
- Fix: Unable to resolve service for type while attempting to activate
- Fix: Cannot consume scoped service from singleton
- HttpClient vs HttpClientFactory vs Refit
- System.Text.Json против Newtonsoft.Json в 2026
Источники
- AutoMapper and MediatR Commercial Editions Launch Today - объявление о выпуске от 2025-07-02, граница v13.0 и двойная модель RPL-1.5 / коммерческая.
- Licensing FAQ - Lucky Penny Software - пороги Community в 5 000 000 дохода и 10 000 000 капитала, подсчёт разработчиков по “программному доступу” и проверка лицензии только в журнал.
- MediatR commercial version launched (Discussion #1123) - подтверждение, что старые версии остаются под своей первоначальной лицензией с открытым исходным кодом.
- LuckyPennySoftware/MediatR v13.0.0 release - первый выпуск с двойной лицензией.
- Scrutor на GitHub - расширение
Decorateпод лицензией MIT, используемое для замены pipeline behaviors декораторами.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.