é uma prática ruim que o controlador chame o repositório em vez do serviço?

44

é uma prática ruim que o controlador chame o repositório em vez do serviço?

para explicar mais:

Descobri que, em um bom design, os controladores chamam serviço e serviço usam repositório.

mas às vezes no controlador eu não tenho / preciso de nenhuma lógica e só preciso buscar no db e passá-lo para visualização.

e posso fazer isso apenas chamando o repositório - não é necessário chamar o serviço - é uma prática ruim?

mohsenJsh
fonte
Como você está ligando para o serviço? Através de uma interface REST?
Robert Harvey
2
Eu uso essa abordagem de design com bastante frequência. Meu controlador (ou uma classe de compositor subjacente) solicitará dados ou enviará dados para o repositório e passará para todas as classes de serviço que precisam executar o processamento. Não há razão para combinar classes de processamento de dados com classes de recuperação / gerenciamento de dados, são preocupações diferentes, embora eu saiba que a abordagem típica é fazer dessa maneira.
Jimmy Hoffa
3
Meh. Se for um aplicativo pequeno e você estiver apenas tentando obter dados de um banco de dados, uma camada de serviço é uma perda de tempo, a menos que essa camada de serviço faça parte de alguma API pública, como uma interface REST. "O leite é bom ou ruim para você?" Depende se você é intolerante à lactose.
Robert Harvey
4
Não existe uma regra rígida e rápida de que você deve ter uma estrutura Controller -> Service -> Repository sobre Controller -> Repository. Escolha o padrão certo para o aplicativo certo. O que eu diria é que você deve tornar seu aplicativo consistente.
NikolaiDante
Talvez você possa criar um serviço genérico que apenas encaminhe sua solicitação ao repositório e depois retorne. Isso pode ser útil para manter uma interface uniforme e seria simples se, no futuro, você precisar adicionar um serviço real para fazer algo antes de chamar o repositório.
Henrique Barcelos

Respostas:

32

Não, pense assim: um repositório é um serviço (também).

Se as entidades que você recuperar através do repositório manipularem a maior parte da lógica de negócios, não haverá necessidade de outros serviços. Apenas ter o repositório é suficiente.

Mesmo se você tiver alguns serviços pelos quais você deve passar para manipular suas entidades. Pegue a entidade do repositório primeiro e depois passe para o referido serviço. Ser capaz de lançar um HTTP 404 antes mesmo de tentar é muito conveniente.

Também para o cenário de leitura é comum, você só precisa da entidade para projetá-lo em um DTO / ViewModel. Ter uma camada de serviço no meio do caminho geralmente resulta em muitos métodos de passagem, o que é bastante feio.

Joppe
fonte
2
Bem dito! Minha preferência é chamar repositórios, e somente em casos, então um repositório não é suficiente (ou seja, duas entidades devem ser modificadas usando repositórios diferentes), eu crio um serviço que é responsável por essa operação e o chamo do controlador.
Zygimantas
Eu notei um código bastante complicado apenas para justificar o uso de um serviço. Absurdo, o mínimo ...
Gi1ber7 16/01
Portanto, meu repositório retorna uma lista de 'objetos de negócios' que eu preciso converter em 'objetos xml'. Isso é suficiente para ter uma camada de serviço? Estou chamando um método em cada objeto para convertê-lo em outro tipo e adicionar a uma nova lista.
bot_bot 24/10
O acesso direto ao DAO é perigoso nos controladores, pode torná-lo suscetível a injeções de SQL e dá acesso a ações perigosas como ,, deleteAll '' Eu definitivamente evitaria isso.
Anirudh 28/10
4

Não é uma prática recomendada para um controlador chamar um repositório diretamente. Um "serviço" é apenas outra ferramenta, portanto, use-o onde fizer sentido.

NikolaiDante comentou:

... Escolha o padrão certo para a aplicação correta. O que eu diria é que você deve tornar seu aplicativo consistente.

Não acho que a consistência seja o aspecto mais importante. Uma classe "service" deve encapsular alguma lógica de nível superior, para que o controlador não precise implementá-la. Se não houver uma "lógica de nível superior" necessária para uma determinada operação, basta ir diretamente ao repositório.

Para promover boas Separações de Preocupações e testabilidade, o repositório deve ser uma dependência que você injeta no serviço por meio de um construtor:

IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);

service.DoSomething(...);

Se a procura de registros no banco de dados precisar de algum tipo de consulta parametrizada, uma classe de serviço pode ser um bom lugar para levar em seu modelo de exibição e criar uma consulta que será executada pelo repositório.

Da mesma forma, se você tiver um modelo de exibição complexo para um formulário, uma classe de serviço poderá encapsular a lógica de criação, atualização e exclusão de registros chamando métodos em seus Modelos / Entidades de Domínio e persistindo-os usando um repositório.

Indo na direção oposta, se o seu controlador precisar obter um registro por seu ID, delegar a um objeto de serviço é como acertar uma tachinha com uma marreta - é muito mais do que você precisa.

Eu descobri que o controlador está na melhor posição para lidar com a transação, ou um objeto Unit Of Work . O controlador ou o objeto Unit Of Work, em seguida, delegaria objetos de serviço para operações complexas ou iria diretamente ao repositório para operações simples (como encontrar um registro por ID).

public class ShoppingCartsController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, ShoppingCartForm model)
    {
        // Controller initiates a database session and transaction
        using (IStoreContext store = new StoreContext())
        {
            // Controller goes directly to a repository to find a record by Id
            ShoppingCart cart = store.ShoppingCarts.Find(id);

            // Controller creates the service, and passes the repository and/or
            // the current transaction
            ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);

            if (cart == null)
                return HttpNotFound();

            if (ModelState.IsValid)
            {
                // Controller delegates to a service object to manipulate the
                // Domain Model (ShoppingCart)
                service.UpdateShoppingCart(model, cart);

                // Controller decides to commit changes
                store.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            else
            {
                return View(model);
            }
        }
    }
}

Eu acho que uma combinação de serviços e trabalhar diretamente com repositórios é perfeitamente aceitável. Você pode encapsular ainda mais a transação em um objeto de Unidade de trabalho, se achar necessário.

A repartição de responsabilidades é assim:

  • O controlador controla o fluxo do aplicativo
    • Retorna "404 não encontrado" se o carrinho de compras não estiver no banco de dados
    • Renderiza novamente o formulário com mensagens de validação, se a validação falhar
    • Salva o carrinho de compras se tudo sair
  • O controlador delega para uma classe de serviço para executar a lógica comercial nos seus Modelos de Domínio (ou Entidades). Objetos de serviço não devem implementar lógica de negócios! Eles executam a lógica de negócios.
  • Os controladores podem delegar diretamente aos repositórios para operações simples
  • Os objetos de serviço pegam dados no modelo de exibição e delegam aos Modelos de Domínio para executar a lógica de negócios (por exemplo, o objeto de serviço chama métodos nos Modelos de Domínio antes de chamar métodos no repositório)
  • Objetos de serviço delegados a repositórios para persistência de dados
  • Os controladores devem:
    1. Gerenciar a vida útil de uma transação ou
    2. Crie um objeto Unit Of Work para gerenciar o tempo de vida de uma transação
Greg Burghardt
fonte
1
-1 para colocar o DbContext em um controlador, em vez de um repositório. A repo é utilizado para gerenciar provedores de dados para que ninguém mais tem que, no caso um provedor de dados mudanças (do MySQL para arquivos JSON planas, por exemplo, a mudança em um lugar)
Jimmy Hoffa
@ JimmyHoffa: Na verdade, estou olhando para o código que escrevi e, para ser sincero, crio um objeto de "contexto" para meus repositórios, não sendo necessário o banco de dados. Eu acho que DbContexté um nome ruim nesse caso. Eu vou mudar isso. Eu uso o NHibernate e os repositórios (ou o contexto, se for útil) gerenciam o final do banco de dados; portanto, a alteração dos mecanismos de persistência não requer alterações de código fora do contexto.
Greg Burghardt
você parece estar confundindo controlador com repositório pela aparência do seu código ... quero dizer, seu "contexto" está todo errado e absolutamente não deveria existir no controlador.
Jimmy Hoffa
6
Não preciso responder e não tenho certeza se essa é uma boa pergunta para começar, mas não aceito porque acho que sua abordagem é um projeto ruim. Sem ressentimentos, apenas desencorajo contextos pertencentes a controladores. OMI que um controlador não deve iniciar e confirmar transações como esta. Esse é o trabalho de vários outros lugares. Prefiro que os controladores delegem tudo o que não está simplesmente cumprindo a solicitação HTTP em outro lugar.
Jimmy Hoffa
1
Um repositório, é normalmente responsável por todas as informações contexto de dados para garantir que as necessidades nada mais saber nada sobre outros do que o domínio em si precisa de saber das preocupações de dados
Jimmy Hoffa
1

Depende da sua arquitetura. Eu uso o Spring e a transacionalidade é sempre gerenciada pelos serviços.

Se você chamar repositórios diretamente para operações de gravação (ou serviços simples sem lógica que simplesmente delegam ao repositório), provavelmente você está usando várias transações de banco de dados para uma operação que deve ser executada em uma. Isso levará a dados incoerentes no seu banco de dados. Como regra geral, as operações do banco de dados devem funcionar, ou devem falhar, mas as operações de meio trabalho são a causa de dores de cabeça.

Por esse motivo, acho que chamar repositórios diretamente de controladores ou usar serviços simples de delegação é uma prática ruim. Você começa a fazer isso apenas para leitura e muito em breve você ou um de seus colegas começará a fazer isso para operações de gravação.

Rober2D2
fonte