Паттерн "Команда" в C# — объекты действий, а не методы
Модель и масштаб — Основы БД, опорные темы, проектирование БД, пакетная работа. Карта — о разделе.
Краткий обзор паттерна есть в поведенческих паттернах. Здесь — практика на C#: когда ICommand действительно полезен, а когда хватает делегата или обычного сервисного метода.
Play ITЗагрузка интерактивного демо…
Задача паттерна
Команда превращает действие в объект. Такой объект можно:
- передавать между слоями;
- класть в очередь;
- логировать;
- повторять и отменять.
Клиент вызывает команду через общий интерфейс и не знает детали выполнения.
Play ITЗагрузка интерактивного демо…
Классический вариант
Код ITЗагрузка примера кода…
ToolbarButton (invoker) хранит ICommand и вызывает Execute(). Такая форма удобна в UI, редакторах и workflow-системах.
Что даёт C# поверх канона
Для простых сценариев можно хранить команды как делегаты:
var actions = new Dictionary<string, Action>
{
["refresh"] = () => cache.Reload(),
["clear"] = () => cache.Clear()
};
actions["refresh"]();
Минимум кода, но нет явной модели Undo, метаданных команды и сериализации.
Если нужен контекст действия, версия, user id, idempotency key, объект команды оказывается удобнее:
public record CreateOrderCommand(Guid UserId, IReadOnlyList<OrderItemDto> Items);
Command и MediatR в .NET
В современных ASP.NET Core проектах паттерн Command часто проявляется через MediatR:
CreateOrderCommand : IRequest<Guid>— объект намерения;CreateOrderCommandHandler— исполнитель;ISender.Send(command)— invoker.
Это вариант паттерна, адаптированный под DI и pipeline-поведения (валидация, логирование, трейсинг). Детально — в MediatR и pipeline.
Когда Command уместен
| Ситуация | Почему помогает Command |
|---|---|
| Undo/redo в UI | Команда хранит данные для отката |
| Очереди и отложенное исполнение | Объект можно сериализовать и выполнить позже |
| Аудит действий | Один объект = одна бизнес-операция с метаданными |
| CQRS в Application-слое | Явное разделение сценариев записи и чтения |
Когда это лишний слой
Если операция:
- короткая;
- не требует очереди, истории и отката;
- вызывается в одном месте;
обычный сервисный метод обычно проще:
await orderService.CreateAsync(dto, ct);
Иногда ICommand + Handler + Invoker только увеличивают число файлов без новой ценности.
Чек-лист
| Вопрос | Рекомендация |
|---|---|
| Нужно отменять и повторять действия | Command-объект |
| Нужны очередь, ретраи, журнал | Command-объект |
| Просто вызвать одну операцию | Сервисный метод или Action |
| Есть Application-слой с MediatR | Команды как IRequest |
В C# Command хорошо работает как контракт бизнес-действий и транспорт операции через слои.
Для локальных коротких вызовов проще и дешевле остаются обычные методы.
Итог
Паттерн Command остаётся актуальным, когда действие нужно трактовать как отдельную сущность системы. В .NET это часто реализуется через MediatR, очереди и pipeline. Для простых вызовов разумнее оставить плоский сервисный API.