Walidacja domeny

0

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ć

2

Jeśli chodzi o encje, to w konstruktorach, bo nigdy nie chcę mieć ich w niepoprawnym stanie.

Takie walidatory są dobre do obiektów wejściowych: jakich viewmodeli czy DTO, bo te z definicji mogą mieć niepoprawne wartości. Tylko nie ma sensu pisać tego samodzielnie, od tego jest FluentValidation..

0
somekind napisał(a):

Jeśli chodzi o encje, to w konstruktorach, bo nigdy nie chcę mieć ich w niepoprawnym stanie.

Takie walidatory są dobre do obiektów wejściowych: jakich viewmodeli czy DTO, bo te z definicji mogą mieć niepoprawne wartości. Tylko nie ma sensu pisać tego samodzielnie, od tego jest FluentValidation..

No Fluenta jak najbardziej używam do tych rzeczy. Mialem na myśli samą domenę. Mam tych pół trochę w danej encji. W konstruktorze to jedno miejsce. Ale potem jest jakiś update. I to samo trzeba robić i tak we wszystkich encjach

4

Ale jeśli encje zmieniasz tylko przez metody, które w nich są to nie powinno się dać ustawić niepoprawnego stanu. Więc zamiast walidacji poprawności to metody do zmiany stanu powinny zapewniać, że ten stan będzie zawsze poprawny.

2
mar-ek1 napisał(a):

Ale jeśli encje zmieniasz tylko przez metody, które w nich są to nie powinno się dać ustawić niepoprawnego stanu. Więc zamiast walidacji poprawności to metody do zmiany stanu powinny zapewniać, że ten stan będzie zawsze poprawny.

Dokładnie, to domena (encja w Twoim przypadku) powinna sama dbać o to, żeby nie znaleźć się nigdy w niepoprawnym stanie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1