Pergunta

Esta questão é agnóstico língua, mas eu sou um cara C #, então eu usar o POCO termo para significar um objeto que o armazenamento de dados apenas pré-formas, geralmente usando campos get e set.

Eu só reformulado o meu modelo de domínio a ser super-duper POCO e fiquei com um par de preocupações sobre como garantir que os valores de propriedade faz sentido witin o domínio.

Por exemplo, o EndDate de um serviço não deve exceder a EndDate do contrato que o serviço é abaixo. No entanto, parece que uma violação de sólido para colocar o cheque para o setter Service.EndDate, para não mencionar que, como o número de validações que precisa ser feito cresce minhas classes POCO vai tornar-se confuso.

Eu tenho algumas soluções (vou postar nas respostas), mas eles têm suas desvantagens e estou querendo saber quais são algumas abordagens favoritos para resolver este dilema?

Foi útil?

Solução

Eu acho que você está começando com uma má hipótese, ou seja, que você deve ter objetos que fazem dados nada além da loja, e não têm métodos, mas assessores. Toda a ponto de ter objetos é a dados Encapsular e comportamentos . Se você tem uma coisa que é apenas, basicamente, uma estrutura, quais os comportamentos que você está encapsulando?

Outras dicas

Eu sempre ouvi argumento pessoas para um "Validar" ou método "IsValid".

Pessoalmente acho que isso pode funcionar, mas com a maioria dos projetos DDD você geralmente acabam com várias validações que são permitidas, dependendo do estado específico do objeto.

Então eu prefiro "IsValidForNewContract", "IsValidForTermination" ou similar, porque eu acredito que a maioria dos projetos acabam com múltiplas tais validadores / estados por turma. Isso também significa que eu recebo nenhuma interface, mas posso escrever validadores agregados que ler muito bem refletir as condições de negócios que estou afirmando.

Eu realmente acredito que as soluções genéricas, neste caso, muitas vezes tomar foco afastado do que é importante - o que o código está fazendo - para um ganho muito menor na elegância técnica (a interface, delegado ou qualquer outra coisa ). Apenas voto-me para baixo para ele;)

Um colega meu veio com uma idéia que funcionou muito bem. Nós nunca veio com um grande nome para ele, mas nós o chamamos Inspector / Juiz.

O Inspector iria olhar para um objeto e dizer-lhe todas as regras que violados. O juiz iria decidir o que fazer sobre isso. Esta separação vamos fazer um par de coisas. Ele vamos colocar todas as regras em um lugar (Inspector), mas poderíamos ter vários juízes e escolher o Juiz pelo contexto.

Um exemplo do uso de vários juízes gira em torno da regra que disse que um cliente deve ter um endereço. Este foi um aplicativo de três camadas padrão. Na camada de interface do usuário, o juiz iria produzir algo que a interface do usuário poderia usar para indicar os campos que tinham de ser preenchidas. A UI juiz não lançar exceções. Na camada de serviço não havia outro juiz. Se se verificar um cliente sem um endereço durante Salvar seria lançar uma exceção. Nesse ponto, você realmente tem que parar as coisas de prosseguir.

Também juízes que eram mais rigorosas como o estado dos objetos alterados. Foi um pedido de seguro e durante o processo Citando uma política foi autorizado a ser salvo em um estado incompleto. Mas uma vez que a política estava pronto para ser feito ativo um monte de coisas tinha que ser conjunto. Assim, o Citando juiz no lado do serviço não foi tão rigoroso como o Juiz de Ativação. No entanto, as regras utilizadas no Inspector ainda eram os mesmos que você pode ainda dizer o que não estava completo, mesmo se você decidiu não fazer nada sobre isso.

Uma solução é ter DataAccessObject de cada objeto dar uma lista de validadores. Quando Salvar é chamada, ela pré-formas uma verificação contra cada validador:

public class ServiceEndDateValidator : IValidator<Service> {
  public void Check(Service s) {
    if(s.EndDate > s.Contract.EndDate)
      throw new InvalidOperationException();
  }
}

public class ServiceDao : IDao<Service> {
  IValidator<Service> _validators;
  public ServiceDao(IEnumerable<IValidator<Service>> validators) {_validators = validators;}
  public void Save(Service s) {
    foreach(var v in _validators)
      v.Check(service);
    // Go on to save
  }
}

O benefício, é muito claro SoC, a desvantagem é que nós não recebem o cheque até Save () é chamado.

No passado eu normalmente delegada validação para um serviço a sua própria, como um ValidationService. Isso, em princípio, ouve anúncios ainda para a filosofia de DDD.

Internamente esta conteria uma coleção de validadores e um conjunto muito simples de métodos públicos, como Validate () que pode retornar uma coleção de objeto de erro.

Muito simplesmente, algo como isso em C #

public class ValidationService<T>
{
  private IList<IValidator> _validators;

  public IList<Error> Validate(T objectToValidate)
  {
    foreach(IValidator validator in _validators)
    {
      yield return validator.Validate(objectToValidate);
    }
  }
}

validadores pode ser adicionado dentro de um construtor padrão ou injectada por meio de uma outra classe, tais como um ValidationServiceFactory.

Eu acho que, provavelmente, seria o melhor lugar para a lógica, na verdade, mas isso é só comigo. Você pode ter algum tipo de método IsValid que verifica todas as condições também e retorna verdadeiro / falso, talvez algum tipo de coleção ErrorMessages mas isso é um assunto duvidoso desde as mensagens de erro não são realmente uma parte do Modelo de Domínio. Estou um pouco tendenciosa como eu fiz algum trabalho com RoR e isso é essencialmente o que seus modelos fazer.

Outra possibilidade é ter cada uma das minhas classes implementam

public interface Validatable<T> {
  public event Action<T> RequiresValidation;
}

E tem cada setter para cada aumento de classe do evento antes de configuração (talvez eu poderia conseguir isso por meio de atributos).

A vantagem é em tempo real, verificação de validação. Mas o código mais confusa e não está claro quem deve fazer a fixação.

Aqui é outra possibilidade. Validação é feita através de um proxy ou decorador no objeto Domínio:

public class ServiceValidationProxy : Service {
  public override DateTime EndDate {
    get {return EndDate;}
    set {
      if(value > Contract.EndDate)
        throw new InvalidOperationexception();
      base.EndDate = value;
    }
  }
}

Vantagem: validação instantânea. Pode facilmente ser configurado através de um IoC.

Desvantagem: Se um proxy, propriedades validadas deve ser virtual, se um decorador de todos os modelos de domínio deve ser baseada em interface. As classes de validação vai acabar um peso pesado bit - proxys tem que herdar a classe e decoradores têm de implementar todos os métodos. Nomeação e organização pode ficar confusa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top