Como implementar regras de negócio que podem mudar com o passar do tempo sem violar o “Open/Closed Principle” do SOLID? Afinal, a violação desse princípio deixaria os códigos difíceis de manter além de implicar na escritas de testes que “quebrariam” pelos motivos errados.
Na programação orientada a objeto, o princípio do aberto/fechado estabelece que “entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação“; isto é, a entidade pode permitir que o seu comportamento seja estendido sem modificar seu código-fonte. (Wikipedia)
Há anos, temos visto sempre os mesmos exemplos, triviais, porém distantes do “mundo real”, onde a solução para o desafio imposto pelo princípio implica em suportar alguma variação de polimorfismo.
public abstract class Employee { // ... public abstract bool IsElegibleForRetirement(); // .. }
O problema destas abordagens é que elas ignoram cenários onde comportamentos expressos por mais de um elemento do contrato (mais de um método) variam independentemente. Além disso, soluções baseadas em herança geram evidente custo de acoplamento.
Uma solução, mais elegante em nossa opinião, possível em diversos cenários, é recorrer a adoção do Specification pattern.
Specification Pattern é um padrão de design de software, onde regras de negócio podem ser recombinadas através de lógica booleana. (Wikipedia em tradução livre)
public class RetirementLaw_287_2016 : IRetirementSpecification { public bool IsSatisfiedBy(Employee e) { /* .. */ } }
A “beleza” dessa abordagem é que ela se encaixa perfeitamente para, por exemplo, implementar suporte a leis e regulações (como no caso da lei de aposentadoria, indicado no exemplo). Assim, sempre que uma nova regulação entra em vigor, podemos criar uma nova versão da especificação. Outro ponto forte dessa ideia é a facilidade de tornar o código compreensível pelo especialista do domínio.
O que acha?
Já usei esse padrão alinhado com builder pattern para construir objetos de domínio, “forçando” a passar pelas especificações e manter todo objeto na memória válido perante regras de negócio.
Eu tenho uma dúvida: O specification pattern, é chamado dentro do domain service ou pode ser chamado em camadas mais acimas para fazer as validações antes de criar a minha entidade?