Arquitetura / camadas do projeto .NET MVC

11

Ao planejar a arquitetura para um aplicativo da Web MVC de médio porte, como você implementa as camadas para serem o mais dissociadas possível e fácil de testar? (basicamente siga as práticas recomendadas) Digamos que eu estou usando o código primeiro como meu acesso a dados.

Eu luto com o que definir "lógica de negócios" como e como ela deve interagir com a camada de dados. Tomando um aplicativo de vendas de veículos como exemplo, a lógica de negócios seria classes que executavam tarefas como calcular a faixa de impostos para determinados veículos, comparar estatísticas de milhas por galão, etc.? Quanto às entidades comerciais (por exemplo, carros, vans, motocicletas), eu as colocaria na camada de dados junto com a minha DataContextclasse.

Além disso, o que constituiria a lógica do aplicativo em oposição aos negócios - estou adivinhando coisas como validações de entrada de sessão / usuário?

Por exemplo, um controlador de carro pode retornar um resultado de ação / exibição que lista os dez melhores carros filtrados por tipo e melhor mpg. Então, digamos que eu tenha um ICarRepository'carRepo' injetado no meu controlador (usando o padrão de repositório / DI), eu filtrei meus carros a partir de um parâmetro do método de ação, por exemplovar cars = carRepo.getCarsByType("hatchback");

Portanto, mantive o conhecimento de acesso a dados fora do meu controlador usando um repositório, agora para manter a lógica de negócios fora do controlador usando um modelo de domínio - var result = new MpgCalculator (cars); - Digamos que eu precise da classe da calculadora, pois ela precisa executar uma lógica adicional para calcular a melhor eficiência de combustível, mais do que apenas carregar / filtrar entidades do DB. Portanto, agora eu tenho um conjunto de dados para renderizar minha visão que utilizava um repositório para recuperar da camada de acesso a dados e objeto específico do domínio para processar e executar tarefas relacionadas a negócios nesses dados.

Estou cometendo erros aqui? ainda precisamos usar o padrão de repositório ou posso apenas codificar em uma interface para desacoplar o ORM e testar? Neste tópico, como minhas classes concretas de acesso a dados dbcontext estão na camada de dados, as definições de interface devem entrar na camada domínio / negócio, o que significa que, se a tecnologia de acesso a dados for alterada, minhas outras camadas não serão afetadas?

Pelo que estudei até agora, minha estrutura se parece com isso:

MVC Internet Application -> O projeto padrão da Internet - modelos aqui são ViewModels

Camada de domínio / negócios -> classes / modelos específicos de negócios que os controladores podem usar para processar entidades de domínio da camada de dados antes de passar para as visualizações relevantes

Abstração de repositório necessária? -> Eu escuto muito debate sobre isso, especialmente ao usar um ORM

Camada de dados -> Classes de entidade (carro, van, motocicleta), DbContext - Camada de tecnologia de acesso a dados em concreto

Michael Harper
fonte

Respostas:

26

Você tem muitas partes móveis em sua pergunta, abordando muitos conceitos, mas aqui está o meu conselho básico quando se trata de como pensar em um aplicativo MVC em escala de médio a grande porte:

Apresentação <---> Business Logic <---> Acesso a dados

Em primeiro lugar, é melhor não pensar no aplicativo como "um aplicativo MVC". É um aplicativo que usa o padrão MVC como seu componente de apresentação. Pensar dessa maneira o ajudará a separar as preocupações da lógica de negócios das preocupações com a apresentação . Talvez seja bom para aplicativos pequenos empilhar tudo para acessar o banco de dados na estrutura MVC, mas rapidamente se tornará insustentável para um aplicativo de médio a grande porte.

MVC (Apresentação)

No seu aplicativo, o componente ASP.NET MVC deve lidar com a transformação de dados corporativos para fins de exibição (Modelos), exibição da interface do usuário (Exibições) e problemas de comunicação como roteamento, autenticação, autorização, validação de solicitação, manipulação de resposta e o like (Controladores). Se você possui um código que faz outra coisa, ele não pertence ao componente MVC .

Repositório / ORM (Acesso a Dados)

Também no seu aplicativo, a camada de acesso a dados deve se preocupar em recuperar e armazenar dados persistentes. Geralmente isso ocorre na forma de um banco de dados relacional, mas há muitas outras maneiras pelas quais os dados podem ser persistidos. Se você possui um código que não está lendo ou armazenando dados persistentes, ele não pertence à camada de dados . Compartilhei meus pensamentos sobre a discussão sobre ORM / Repositório anteriormente no SO, mas, para recapitular, não considero um ORM a mesma coisa que um Repositório, por várias razões.

Logíca de negócios

Então agora você tem sua camada de apresentação (MVC) e sua camada de dados (repositório ou ORM) ... Todo o resto é sua camada de lógica de negócios (BLL). Todo o seu código que decide quais dados recuperar, ou executa cálculos complicados ou toma decisões de negócios, deve estar aqui. Normalmente, organizo minha lógica de negócios na forma de 'serviços', que minha camada de apresentação pode chamar para realizar o trabalho solicitado. Todos os meus modelos de domínio existem aqui.

Sua Abordagem

É aqui que sua abordagem se divide um pouco para mim. Você descreve seu controlador MVC como o local onde você obteria dados do repositório e solicita ao MPGCalculator que faça algum trabalho, etc. Eu não faria meu controlador fazer nada disso, mas delegaria tudo isso a um serviço no BLL.

Em outras palavras, eu não injetaria um repositório e o MPGCalculator no controlador, o que está dando muita responsabilidade ao controlador (ele já está lidando com todas as coisas do controlador que mencionei acima). Em vez disso, eu teria um serviço no BLL para lidar com tudo isso e passaria os resultados de volta ao controlador. O controlador pode transformar os resultados no modelo correto e transmiti-los para a visualização correta. O controlador não possui nenhuma lógica comercial e as únicas coisas injetadas no controlador seriam os serviços BLL apropriados.

Fazer dessa maneira significa que sua lógica de negócios (por exemplo, dado um conjunto de veículos, calcula o MPG e classifica da melhor para a pior ) é independente das preocupações de apresentação e persistência. Geralmente, ele fica em uma biblioteca que não conhece nem se importa com a estratégia de persistência de dados nem com a estratégia de apresentação.

Eric King
fonte
Oi Eric, excelente resposta - em relação aos repositórios, presumo que as classes concretas viveriam na camada de acesso a dados e o 'ICarRepository' etc na camada de negócios / serviços? Então, eu poderia injetar serviços no meu controlador, que podem conter 1 ou mais repositórios, dependendo dos requisitos?
Michael Harper
@ MichaelHarper Sim, isso soa como uma maneira perfeitamente boa de fazer isso.
Eric Rei
1
Enquanto a autenticação é uma preocupação do controlador (UIs diferentes são autenticadas de maneira diferente), eu diria que a autorização é lógica de negócios e pertence à camada de negócios. Você concorda?
Tom
1
Tom: Sim, você tem um bom argumento. Eu estava pensando em uma autorização simples, pois o usuário tem acesso a essa rota , mas pode haver muito mais do que isso. A parte "muito mais" pertence à camada de negócios.
Eric Rei
1
@HunterNelson Se você estiver mapeando para um modelo de vista, o mapeamento deve ocorrer onde está a vista, na camada de apresentação. Não faria sentido em nenhum outro lugar.
Eric Rei
0

Parece que tudo está correto para sua estrutura. A única coisa que não tenho certeza é que você mencionou que os modelos no MVC são "ViewModels" e que seus controladores conversam com a camada de domínio. Eu acho que isso faz sentido se seu padrão padrão é usar o controlador para acessar a camada de domínio e, em seguida, usar seus "ViewModels" como compilações de informações mais específicas da visualização de várias entidades do domínio, conforme faz sentido para essa visualização específica. Se é isso que você está fazendo, provavelmente está bem.

Existe uma escola de pensamento de que você deve ter uma abstração completa da camada de domínio no aplicativo MVC, se quiser. Pessoalmente, o pensamento de fazer isso em um aplicativo corporativo me causa graves dores mentais.

Prefiro usar o padrão de repositório para gerenciar o acesso à camada de dados, pois aumenta a testabilidade e a flexibilidade. As duas coisas que tendem a fazer as alterações mais drásticas são a interface do usuário e o banco de dados. Imagine se algumas das informações que você está retirando diretamente do banco de dados são alteradas, de forma que elas precisam ser recuperadas de uma chamada de serviço em vez de uma chamada de banco de dados, ou algumas das informações são movidas para um banco de dados diferente que exige um .edmx diferente Arquivo. O padrão do repositório fornece abstração para suportar isso.

wpenberthy
fonte
Obrigado pela resposta William 😊 Eu consideraria meus objetos de negócios / lógica e entidades de domínio como 'modelos' que o controlador usa para processar ações do usuário e modelos de exibição como exibir modelos específicos que podem conter grupos de modelos etc.
Michael Harper