Situação
No início desta noite, respondi a uma pergunta no StackOverflow.
A questão:
A edição de um objeto existente deve ser feita na camada de repositório ou em serviço?
Por exemplo, se eu tiver um usuário com dívida. Eu quero mudar a dívida dele. Devo fazê-lo no UserRepository ou no serviço, por exemplo, BuyingService, obtendo um objeto, editando-o e salvando-o?
Minha resposta:
Você deve deixar a responsabilidade de alterar um objeto para o mesmo objeto e usar o repositório para recuperar esse objeto.
Exemplo de situação:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Um comentário que recebi:
A lógica de negócios deve realmente estar em um serviço. Não em um modelo.
O que a internet diz?
Então, isso me fez procurar, porque eu nunca (conscientemente) realmente usei uma camada de serviço. Comecei a ler sobre o padrão Camada de serviço e o padrão Unidade de trabalho, mas até agora não posso dizer que estou convencido de que uma camada de serviço deve ser usada.
Tomemos, por exemplo, este artigo de Martin Fowler sobre o antipadrão de um modelo de domínio anêmico:
Existem objetos, muitos nomeados após os substantivos no espaço do domínio, e esses objetos estão conectados aos ricos relacionamentos e estrutura que os verdadeiros modelos de domínio possuem. O problema ocorre quando você olha para o comportamento e percebe que quase não há comportamento nesses objetos, tornando-os pouco mais do que sacos de caçadores e caçadores. Na verdade, esses modelos geralmente vêm com regras de design que dizem que você não deve colocar nenhuma lógica de domínio nos objetos de domínio. Em vez disso, há um conjunto de objetos de serviço que capturam toda a lógica do domínio. Esses serviços ficam no topo do modelo de domínio e usam o modelo de domínio para dados.
(...) A lógica que deve estar em um objeto de domínio é lógica de domínio - validações, cálculos, regras de negócios - como você quiser chamar.
Para mim, isso parecia exatamente o que era a situação: eu advoguei a manipulação dos dados de um objeto introduzindo métodos dentro dessa classe que fazem exatamente isso. No entanto, percebo que isso deve ser um dado de qualquer maneira, e provavelmente tem mais a ver com a forma como esses métodos são chamados (usando um repositório).
Eu também tinha a sensação de que, naquele artigo (veja abaixo), uma Camada de Serviço é mais considerada uma fachada que delega trabalho para o modelo subjacente do que uma camada que exige muito trabalho.
Camada de aplicativo [seu nome para Camada de serviço]: define os trabalhos que o software deve executar e direciona os objetos de domínio expressivos para solucionar problemas. As tarefas pelas quais essa camada é responsável são significativas para os negócios ou necessárias para a interação com as camadas de aplicativos de outros sistemas. Essa camada é mantida fina. Ele não contém regras ou conhecimentos de negócios, mas apenas coordena tarefas e delegados trabalham para colaborações de objetos de domínio na próxima camada abaixo. Não possui um estado que reflita a situação do negócio, mas pode ter um estado que reflete o progresso de uma tarefa para o usuário ou o programa.
O que é reforçado aqui :
Interfaces de serviço. Os serviços expõem uma interface de serviço à qual todas as mensagens de entrada são enviadas. Você pode pensar em uma interface de serviço como uma fachada que expõe a lógica de negócios implementada no aplicativo (normalmente, lógica na camada de negócios) a possíveis consumidores.
E aqui :
A camada de serviço deve ser desprovida de qualquer aplicativo ou lógica de negócios e deve se concentrar principalmente em algumas preocupações. Ele deve agrupar as chamadas da camada de negócios, traduzir seu domínio em um idioma comum que seus clientes possam entender e manipular o meio de comunicação entre o servidor e o cliente solicitante.
Este é um contraste sério com outros recursos que falam sobre a camada de serviço:
A camada de serviço deve consistir em classes com métodos que são unidades de trabalho com ações que pertencem à mesma transação.
Ou a segunda resposta a uma pergunta que eu já vinculei:
Em algum momento, seu aplicativo desejará alguma lógica de negócios. Além disso, convém validar a entrada para certificar-se de que não haja algo ruim ou não-desempenho sendo solicitado. Essa lógica pertence à sua camada de serviço.
"Solução"?
Seguindo as diretrizes nesta resposta , criei a seguinte abordagem que usa uma Camada de Serviço:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Conclusão
Tudo junto não mudou muito aqui: o código do controlador foi movido para a camada de serviço (o que é uma coisa boa, então há uma vantagem nessa abordagem). No entanto, isso não parece ter nada a ver com a minha resposta original.
Percebo que os padrões de design são diretrizes, não regras definidas em pedra a serem implementadas sempre que possível. Ainda não encontrei uma explicação definitiva da camada de serviço e como ela deve ser considerada.
É um meio de simplesmente extrair a lógica do controlador e colocá-la dentro de um serviço?
Ele deveria formar um contrato entre o controlador e o domínio?
Deve haver uma camada entre o domínio e a camada de serviço?
E, por último mas não menos importante: após o comentário original
A lógica de negócios deve realmente estar em um serviço. Não em um modelo.
Isso está correto?
- Como introduziria minha lógica de negócios em um serviço em vez do modelo?