Onde traçamos a linha entre delegação e encapsulamento da lógica de negócios? Parece-me que quanto mais delegamos, mais anêmicos nos tornamos. No entanto, a delegação também promove a reutilização e o diretor DRY. Então, o que é apropriado delegar e o que deve permanecer em nossos modelos de domínio?
Tome as seguintes preocupações como exemplos:
Autorização . O objeto de domínio deve ser responsável por manter suas regras de controle de acesso (como uma propriedade CanEdit) ou deve ser delegado a outro componente / serviço exclusivamente responsável pelo gerenciamento do acesso, por exemplo, IAuthorizationService.CanEdit (objeto)? Ou deveria ser uma combinação dos dois? Talvez o objeto de domínio tenha uma propriedade CanEdit que delegue para um IAuthorizationService interno para executar o trabalho real?
Validação . A mesma discussão acima se refere à validação. Quem mantém as regras e quem é responsável por avaliá-las? Por um lado, o estado do objeto deve pertencer a esse objeto e a validade é um estado, mas não queremos reescrever o código usado para avaliar regras para cada objeto de domínio. Nós poderia usar a herança neste caso ...
Criação de Objetos . Classe de fábrica versus métodos de fábrica versus 'atualização' de uma instância. Se usarmos uma classe de fábrica separada, somos capazes de isolar e encapsular a lógica de criação, mas à custa de abrir o estado do nosso objeto para a fábrica. Isso pode ser gerenciado se nossa camada de domínio estiver em um assembly separado, expondo um construtor interno usado pela fábrica, mas isso se tornará um problema se houver vários padrões de criação. E, se tudo o que a fábrica está fazendo é chamar o construtor certo, qual é o sentido de ter a fábrica?
Os métodos de fábrica na classe eliminam o problema com a abertura do estado interno do objeto, mas, como são estáticos, não podemos quebrar dependências através da injeção de uma interface de fábrica, como podemos com uma classe de fábrica separada.
Persistência . Alguém poderia argumentar que, se nosso objeto de domínio vai expor o CanEdit ao delegar a responsabilidade de executar a verificação de autorização para outra parte (IAuthorizationService), por que não ter um método Save no objeto de domínio que faça a mesma coisa? Isso nos permitiria avaliar o estado interno do objeto para determinar se a operação pode ser executada sem interromper o encapsulamento. É claro que exige que injetemos a instância do repositório em nosso objeto de domínio, o que me cheira um pouco, então aumentamos um evento de domínio e permitimos que um manipulador executasse a operação de persistência?
Veja para onde estou indo com isso?
Rockford Lhotka tem uma ótima discussão sobre suas razões para seguir a rota Class-in-Charge para sua estrutura CSLA, e eu tenho um pouco de história com essa estrutura e posso ver sua ideia de objetos de negócios paralelos a objetos de domínio de várias maneiras. Mas, tentando me tornar mais aderente aos bons ideais de DDD, estou me perguntando quando a colaboração se torna demais.
Se eu terminar com um IAuthorizationService, IValidator, IFactory e IRepository para minha raiz agregada, o que resta? Ter um método Publish que altera o estado do objeto de Rascunho para Publicado é suficiente para considerar a classe como um objeto de domínio não anêmico?
Seus pensamentos?