Return of Unit: Warum dein Validator nicht ausgeführt wird
In einem meiner letzten Projekte bin ich über ein Verhalten gestolpert, das zunächst seltsam erschien: Der FluentValidation-Validator für einen MediatR-Command wurde nicht ausgeführt, obwohl alles korrekt implementiert schien. Die Ursache? Eine Kleinigkeit mit großer Wirkung: Der Command hatte keinen Rückgabewert.
1. Das Problem
Stellen wir uns folgenden Command vor:
public class SomeCommand : IRequest
{
public string SomeProperty { get; set; }
}
Und den zugehörigen Validator:
public class SomeCommandValidator : AbstractValidator<SomeCommand>
{
public SomeCommandValidator()
{
RuleFor(x => x.SomeProperty).NotEmpty();
}
}
Der Handler sah so aus:
public class SomeCommandHandler : IRequestHandler<SomeCommand>
{
public Task Handle(SomeCommand request, CancellationToken cancellationToken)
{
// ... Business Logik ...
return Task.CompletedTask;
}
}
Alles scheint korrekt und doch wird der Validator nicht aufgerufen.
2. Die Ursache: Kein Rückgabewert
Wenn ein Command IRequest
implementiert (ohne generischen Typ), geht MediatR davon aus, dass kein Rückgabewert erwartet wird und überspringt dabei stillschweigend die Validierung. Erst wenn IRequest<Unit>
verwendet wird, greift die Validierung wie erwartet:
public class SomeCommand : IRequest<Unit>
{
public string SomeProperty { get; set; }
}
public class SomeCommandHandler : IRequestHandler<SomeCommand, Unit>
{
public Task<Unit> Handle(SomeCommand request, CancellationToken cancellationToken)
{
// ... Business Logik ...
return Task.FromResult(Unit.Value);
}
}
Warum Unit
?
Unit
ist ein spezieller Rückgabetyp, der als Ersatz für void
dient, wenn man trotzdem einen Wert benötigt (z.B. für generische Constraints). Mit Unit
erzwingst du die Struktur, die MediatR benötigt, um die Pipeline, inklusive Validatoren, korrekt zu durchlaufen.
3. Zweite Falle: Zugriffsmodifizierer
Ein weiterer häufiger Stolperstein ist die Sichtbarkeit des Validators. Ist dein Validator internal
statt public
, wird er von der Dependency Injection nicht gefunden:
// FALSCH – Validator wird nicht gefunden
internal class SomeCommandValidator : AbstractValidator<SomeCommand> { ... }
// RICHTIG – Validator wird erkannt
public class SomeCommandValidator : AbstractValidator<SomeCommand> { ... }
In einer typischen Clean Architecture, bei der die Validators in einem separaten Projekt (z. B. Application Layer) liegen, ist es wichtig, dass alle benötigten Klassen öffentlich
deklariert sind.
4. Fazit
Zwei kleine Details, der Rückgabewert Unit
und die Sichtbarkeit public
, entscheiden darüber, ob dein Validator ausgeführt wird oder nicht. Wenn du mit MediatR arbeitest, stelle sicher:
- Dein Command implementiert
IRequest<Unit>
- Dein Handler gibt
Unit.Value
zurück - Dein Validator ist
public
Diese kleinen, aber essenziellen Best Practices helfen dir dabei, robuste und valide Anwendungsschichten zu entwickeln.