What is the correct place to store the validation messages in a . NET project?

Asked

Viewed 120 times

1

I’m creating a relatively simple class, but I’ve included some validations in its properties.

public class Sala : EntidadeBase
    {
        #region Enums

        public enum StatusSala
        {
            Disponivel,
            Reservada,
            Inativa,
            Bloqueada
        };

        #endregion

        #region Propriedades

        public string Nome { get; private set; }
        public int Capacidade { get; private set; }
        public StatusSala Status { get; private set; }

        #endregion

        #region Contrutores

        public Sala(string nome, int capacidade, StatusSala status) : base()
        {
            SetNome(nome);
            SetCapacidade(capacidade);
            SetStatus(status);
        }

        #endregion

        #region Métodos

        #region SettersPrivados

        private void SetNome(string nome)
        {
            ValidacaoDominio.Validar()
                .Quando(string.IsNullOrEmpty(nome), "O nome não pode ser vazio")
                .Quando(!string.IsNullOrEmpty(nome) && nome.Length < 3, "o nome deve ter pelo menos 3 caracteres")
                .DispararValidacao();

            Nome = nome;
        }

        private void SetCapacidade(int capacidade)
        {
            ValidacaoDominio.Validar()
                .Quando(capacidade < 1, "Capacidade da sala Inválida")
                .DispararValidacao();

            Capacidade = capacidade;
        }

        private void SetStatus(StatusSala status)
        {
            ValidacaoDominio.Validar()
            .Quando(!Enum.TryParse(typeof(StatusSala), status.ToString(), out var retorno), "Status Inválido")
                .DispararValidacao();

            Status = status;
        }

        #endregion

        #region Publicos

        public void AlterarNome(string nomeAlterado)
        {
            SetNome(nomeAlterado);
        }

        public void AlterarCapacidade(int capacidadeAlterada)
        {
            SetCapacidade(capacidadeAlterada);
        }

        public void AlterarStatus(StatusSala statusAlterado)
        {
            SetStatus(statusAlterado);
        }

        #endregion

        #endregion

As a matter of organisation, I would not like to leave a string fixed in the validation messages, as some can be repeated (example: Invalid name. This can be referenced to any entity that owns the Name property) and the possibility to escape from the pattern is large.

As far as I know, I can do that Resources or create a helper class to concentrate these messages.

Initially, I concentrated the messages in static classes, separating them by categories (from the most general to the most specific), but nothing prevents to do otherwise.

Class for "general messages":

public class GeralMsgs
    {
        public const string NomeInvalido = "O Nome é invalido";
        public const string NomeMenorTresCaracteres = "O nome deve ter pelo menos três caracteres";
    }

Class for class specific messages Sala:

    public class SalaMsgs
    {
        public const string CapacidadeInvalida = "A capacidade  da sala é inválida";
        public const string StatusInvalido = "O status da sala é inválido ";
    }

In this way, the validations would reference the constants

ValidacaoDominio.Validar()
                .Quando(string.IsNullOrEmpty(nome), GeralMsgs.NomeInvalido)
                .Quando(!string.IsNullOrEmpty(nome) && nome.Length < 3, GeralMsgs.NomeMenorTresCaracteres)
                .DispararValidacao();
...
ValidacaoDominio.Validar()
                .Quando(capacidade < 1, SalaMsgs.CapacidadeInvalida)
                .DispararValidacao();
...
 ValidacaoDominio.Validar()
            .Quando(!Enum.TryParse(typeof(StatusSala), status.ToString(), out var retorno), SalaMsgs.StatusInvalido)
                .DispararValidacao();

From the health point of view of the application, thinking about scalability and good practices, what is the right place to concentrate these messages? I leave it that way or I play it all for a file Resource of the project or whatever?

My goal is always to try to do the best possible way, but without creating a laser gun to kill an ant, but also do not want in the future to look at the 147 that the Maniero always put and realize that my application or mine mindset led me to the "Important is to work".

  • "147 that Maniero always put" what is 147?

  • 1

    https://pt.wikipedia.org/wiki/Fiat_147

  • kkk o fiat 147 which represents "working is different from being right"->https://answall.com/a/42262626/69359

  • 1

    I always used from the beginning this bookstore and never again have I seen anything with as many lines of code as it shows :) it’s simple, each class gets its validator, helps maintenance in the future...

  • @balexandre, I traded the current validation for this approach, but using Notify Me, which is a Flunt-based package, from Andre Baltieri, but I still have the same problem that is looking for where I should include the validation messages.

2 answers

3


It’s not always easy to answer these things. I’ve done them myself in different ways. Today I like to do it in a way that I don’t even indicate because it takes a certain cancha to make it right that is the use of code generation. Everything that is given of the application and can have a flexibility and need of a centralized control I put in the database and Gero code to give efficiency, what is known as data dictionary (now I have preferred the term application dictionary because it is something broader) that is not famous so no one learns to use.

Doing in a more traditional way and if you do not need to have this flexibility to exchange messages according to the installation then the use of Resource is not indicated. Then I would do more or less as you are doing. But I don’t have all the information to make the best decision, there can always be a detail, even small, that can change my mind.

I don’t really like having a class of messages for the main class, in general I think the messages should be in itself. A general one can make sense. It can have certain groupings, then it gets complicated, it starts to be difficult to know where to put. That’s why I prefer the database, gives flexibility.

It may be interesting to have a more sophisticated mechanism to pick up the messages than constants, but you have to analyze the question more deeply.

Some people like to abstract the form to be able to change the implementation detail later, and it can be a good one (when I do code generation I have it without losing efficiency). If you do this no matter where the message comes from, if it is Resource, database, an own source, variables or constants and where the message is. There is a case that abstraction worsens the readability of the code, but if it is to get flexibility there is no way. It is to do such a indirect.

Any indirect brings a cost, even a simple constant, need to prove the gain.

Can be abusing abstraction to avoid the magic number.

Every standard to be applied turns to antipadhon if the dose is wrong.

It’s not easy to deal with it properly, so if you don’t have a very clear advantage it might be better to do something like this, and leave the normal messages as is standard.

I don’t know if all this care is so necessary. It may be, I don’t know your context, I don’t see a lot of people doing it (but it’s not an argument, they may be doing it wrong, but my perception is that you don’t need that much). Maybe the problem is trying to do with all messages and not just some cases that make more sense.

You ever stop to think that if people use it wrong, it might not solve anything or make it worse? If you get this mechanism right maybe it’s a bird-killing cannon and then it comes out more simply.

I focused on the question of messages, the validation mechanics seems to me outside the scope of the question, but I see a lot of people following recipes that seem to help but the person has never seen real gain and has problems that she doesn’t even notice. I like abstractions that bring advantage, but I see direct abuse.

Maintain balance

Your concern to maintain balance is commendable, but you can’t do that if you’re looking for a unique formula that solves everything, this is perhaps the biggest mistake every developer makes today. People don’t want to do what’s right, they want to learn a way and do it all the time. That’s why there’s so much trouble and it works even though it’s not right.

Within this point it is important to note that the search for perfection is already a way of doing wrong. Systems should solve problems, it is not acceptable to abuse the gambiarras, to do anyway, to have no worries, but also to try to do academically correct is already a mistake from the engineering point of view. It has to take into account cost, effort, and other problems that this can entail, which is not always easy to predict. It depends on experience.

The error is part of the development process. You’ll make a mistake once, you’ll see what happened, you’ll try to clean up and not make the same mistake the next time, even if you make another one. A good engineer accumulates these experiences and will do better. A bad engineer may not even make the same mistake next time, but will make another, probably opposite, trying to avoid a mistake he has learned. That is, the person is a follower of cake recipe, he always does something he learned without understanding when to apply, without thinking about all questions. This is so common nowadays that it has become rare to find those who do not do so.

  • 1

    Upvote and mark as response is too little for that kind of response.... This project itself is only for studies, but analyzing your answer better, the scenario as a whole and thinking about flexibility, I believe that leaving in Resources is the best way, since I can do the unit tests without relying on the bank(which could be an interesting solution as well, but then I would have to mock the messages in my tests) and in a way the tool I use today (.NET) is already prepared to use Resources, so the effort to make it more flexible is much less than I try to invent something...

1

I have done all the domain validation through Exceptions with specific types for each Entity (or Aggregate Root if we are talking about DDD).

This makes it even easier to implement translations into other languages in the future, as Exceptions receive only the values, not the explicit message.

For example:

public class SalaException: Exception
{
    private SalaException (string message)
       : base(message)
    { }

    public static SalaException CapacidadeDaSalaInvalida() 
    {
        return new SalaException("Capacidade da sala Inválida");
    }

    public static SalaException NomeDaSalaNaoInformado() 
    {
        return new SalaException("O nome não pode ser vazio.");
    }

    public static SalaException NomeDaSalaInsuficiente(string nome) 
    {
        return new SalaException($"Nome inválido ({nome}). O nome deve ter pelo menos 3 caracteres");
    }

    public static SalaException StatusDaSalaInvalido(string status) 
    {
        return new SalaException($"Status Inválido: {status}");
    }
}

public class Sala : EntidadeBase
{
    public string Nome { get; private set; }
    public int Capacidade { get; private set; }
    public StatusSala Status { get; private set; }

    public Sala(string nome, int capacidade, StatusSala status) : base()
    {
        SetNome(nome);
        SetCapacidade(capacidade);
        SetStatus(status);
    }

    private void SetNome(string nome)
    {
        if (string.IsNullOrEmpty(nome))
            throw SalaException.NomeDaSalaNaoInformado();

        if (nome.Length < 3)
            throw SalaException.NomeDaSalaInsuficiente();           

        Nome = nome;
    }

    private void SetCapacidade(int capacidade)
    {
        if (capacidade < 1)
            throw SalaException.CapacidadeDaSalaInvalida(1);

        Capacidade = capacidade;
    }

    private void SetStatus(StatusSala status)
    {       
        if (!Enum.TryParse(typeof(StatusSala), status.ToString(), out var retorno)) 
            throw SalaException.StatusDaSalaInvalido(status.ToString());

        Status = status;
    }
}
  • The solution is even interesting, but taking into account the possibility of translation, what is not my case, leaving the messages the way you are leaving is not opening reasons for a change in your classes? I switched the validations and now I’m using a package that implements the Notification Pattern, so I make my code much cleaner.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.