Witam. Robię sobie wieczorami projekt api. Mam tam sobie handlery commands itp. No i jest warstwa domeny (moje encje). Powiedzmy, że chce jak to tylko możliwe, kontrolować tworzenie obiektów domeny (Są konstruktory, prywatne sety itp). Nie chce dopuscić do utworzenia obiektu który będzie miał np puste pole Name.
Jakie jest wasze podejście w tej kwestii?
1 .No można w każdym type (np w konstruktorze ) dawać ify typu string is null or empty i rzucać wyjątek.
2. Można jakieś klasy osobne walidujące obiekt (każdy typ ma swój walidator)
3. Ja ostatnio w ramach małego treningu stwierdziłem, że może zrobię to na atrubutach. coś w tym stylu. https://www.codeproject.com/Articles/28607/Creating-Validation-Engine-for-Domain-Objects . Czyli tworzę obiekt np metodą statyczną zwracającą dany typ i przed zwróceniem obiektu waliduje go tymi powiedzmy ValidationEngine.
internal abstract class BaseValidationEngine<TAttribute, TValue>
where TAttribute : Attribute, IValidationAttribute<TValue>
{
protected List<ValidationError> errors { get; }
public BaseValidationEngine()
{
errors = new List<ValidationError>();
}
protected void Validate<T>(T item)
{
var properties = item.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var validationAttributes = property.GetCustomAttributes(typeof(TAttribute),
true);
foreach (var att in validationAttributes)
{
var valAtt = att as TAttribute;
if (valAtt is null) continue;
if (!valAtt.IsValid((TValue)property.GetValue(item, null)))
{
errors.Add(new ValidationError
{
Field = property.Name,
Message = valAtt.Message
});
}
}
}
}
}
a konkretny walidator wygląda np tak:
internal class NotNullOrEmptyValidationEngine
: BaseValidationEngine<NotNullOrEmptyAttribute, string>, IValidationEngine
{
public List<ValidationError> ValidateEntity<T>(T item)
{
base.Validate(item);
return base.errors;
}
}
A wszystkie walidatory uruchamiam tak:
public static void Validate<T>(T entity) where T : class
{
List<ValidationError> errors = new List<ValidationError>();
var engines = ValidatorsFactory.GetValidationEngines();
foreach (var engine in engines)
{
var result = engine.ValidateEntity(entity);
errors.AddRange(result);
}
if (errors.Any())
throw new DomainValidationException(errors, $"Cannot create {nameof(T)}");
}
Z zalet tego rozwiązania to dla mnie: nie powtarzam kodu (po raz n-ty string.IsNull...), dołożenie nowej walidacji nie wymaga zmian w istniejącym kodzie.
Z minusów: to chyba overenginering :P, refleksja?
4. Co innego można zastosować