Comments 21
Было куда интереснее, если бы автор привел небольшое описание зачем вообще все это нужно. И для каких целей. Для тех людей, которые не в курсе терминов.
Всё просто, на самом деле. Смотрите здесь https://github.com/jbogard/MediatR/wiki/Behaviors
Очень удобная штука. Вынесли валидацию и транзакции
public class DataAnnotationsValidationPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
public Task Process([NotNull] TRequest request, CancellationToken cancellationToken)
{
var context = new ValidationContext(request);
var results = new List<ValidationResult>();
if (Validator.TryValidateObject(request, context, results))
{
return Task.CompletedTask;
}
var errors = results.Select(x => new ValidationFailure(x.MemberNames.First(), x.ErrorMessage));
throw new ValidationException(errors);
}
}
Где код до и после то?
Ну, в общем-то, в статье. Под заголовками "Код без cross-cutting concern" и "Код с cross-cutting concern"
Ну т.е. просто делаем очень общий интферфейс и на него вешаем 1 декоратор.
Примеров декораторов статье больше нет, видимо подразумевается что он будет аналогичен начальному.
Я понял так
Добавьте дополнительное логирование в необходимых ветках. Можно явно с помощью императивного кода или с помощью декораторов для декораторов. Это как удобнее. Цель статьи — показать как можно очистить доменную логику от инфраструктурного кода. Взаимодействие инфраструктуры с инфраструктурой — другой вопрос.
Пример "инфраструктуры с инфраструктурой" просто из вашего кода. Пускай надо логировать именно отдельные ветки доменной логики. Причём не ошибки, которые клиенту доменной логики надо как-то обрабатывать, а просто debug/info уровень, о том куда мы пошли, какая ветка бизнес-правила сработала. Приходит в голову возвращать Result или его наследника типа LoggableResult, в котором есть итерируемое поле типа logMessages, из которого декоратор, если он применён, может доставать сообщения и логировать их. Нет декоратора — они просто теряются или добавляются к LoggableResult самого клиента — может клиент клиента решит их залогировать.
Не пробовали такой подход? Из очевидных минусов — потенциально логируемые куски логики, юзкейсы из примеров, всегда должны возвращать Result, а не быть void или какого-то другого типа. Тогда добавить логирование можно будет с минимальным изменением кода, вплоть только в DI-контейнере декоратор добавить. Но логика клиента должна будет всегда учитывать, что ожидаемый результат упакован.
Очень не хочется в уровень доменной логики вводить логгеры, пускай даже какой-то NullLogger по умолчанию. Лучше уж полноценную систему доменных событий внедрять сразу, пускай по началу только логгеры на неё подписываться и будут или вообще в пустоту события эмитировать.
Аналогичный вопрос для контроллера, как он запускает пайплайн?
Начал копать эту тему, счас движусь от «быстрорастворимого» в ширину, так что извиняюсь за элементарные вопросы.
public class CreateUserHandler
: ICommandHanler<CreateUserRequest, CreateUserResponse>
{
CreateUserResponse Handle(CreateUserRequest request)
{
var hash = _mediator.Send(new HashPasswordRequest(request.Password));
// save user logic
_mediator.Publish(new UserCreatedRequest(user.Email));
};
}
public class SecurityService
: ISecurityService
: IUseCaseHandler<HashPasswordRequest, HashPasswordResponse>
{
public HashPasswordResponse HashPassword(HashPasswordRequest request)
{
//...
}
HashPasswordResponse IUseCaseHandler<HashPasswordRequest, HashPasswordResponse>.Handle(HashPasswordRequest request)
=> HashPassword(request);
}
public class EmailService
: IEmailService
: IUseCaseHandler<UserCreatedEvent>
{
public void SendUserCreatedEmail(UserCreatedEvent event)
{
//...
}
IUseCaseHandler<UserCreatedRequest>.Handle(UserCreatedEvent event)
=> SendUserCreatedEmail(event);
}
О декораторах, сквозной функциональности, CQRS и слоеной архитектуре