Onde a “camada de lógica de negócios” se encaixa em um aplicativo MVC?

86

Em primeiro lugar, antes que alguém grite enganado, tive dificuldade em resumir em um título simples. Outro título poderia ser "Qual é a diferença entre um modelo de domínio e um modelo MVC?" ou "O que é um modelo?"

Conceitualmente, entendo um modelo como os dados usados ​​pelas visualizações e pelo controlador. Além disso, parece haver muitas opiniões divergentes sobre o que constitui o modelo. O que é um modelo de domínio versus um modelo de aplicativo, versus um modelo de visualização, versus um modelo de serviço, etc.

Por exemplo, em uma pergunta recente que fiz sobre o padrão do repositório, fui informado que o repositório faz parte do modelo. No entanto, li outras opiniões de que o modelo deve ser separado do modelo de persistência e da camada de lógica de negócios. Afinal, o padrão Repositório não deveria separar o método de persistência concreto do modelo? Outras pessoas dizem que há uma diferença entre o modelo de Domínio e o modelo MVC.

Vamos dar um exemplo simples. O AccountController que está incluído no projeto padrão MVC. Eu li várias opiniões de que o código de conta incluído é de design ruim, viola SRP, etc. etc. Se alguém fosse criar um modelo de associação "adequado" para um aplicativo MVC, o que seria?

Como você separaria os serviços ASP.NET (provedor de associação, provedor de função, etc.) do modelo? Ou você faria?

A meu ver, o modelo deve ser "puro", talvez com lógica de validação .. mas deve ser separado das regras de negócios (diferente de validação). Por exemplo, digamos que você tenha uma regra de negócios que diz que alguém deve receber um e-mail quando uma nova conta é criada. Isso realmente não pertence ao modelo, na minha opinião. Então, onde ele pertence?

Alguém se preocupa em lançar alguma luz sobre esse assunto?

Erik Funkenbusch
fonte
1
É por isso que você deve fazer quatro perguntas diferentes.
John Farrell
3
A palavra-chave é "quase". É realmente a mesma pergunta, talvez com subquestões usadas para ilustrar a pergunta primária.
Erik Funkenbusch
3
Modelo - Visão - Controlador. É o repositório / BL View? Não. É o controlador? Não. O que resta :)? É MVC, não MSVC, MRVC, não MBLVC. Existem apenas três camadas. Portanto, o repositório é parte do modelo, BL é parte do modelo. E você pode fazer separação adicional, mas isso é feito dentro da camada do modelo.
LukLed
3
@LukeLed, @bslm - Na verdade não. O MVC não diz que não pode haver outras camadas com as quais o controlador ou modelo interage.
John Farrell
3
@LukLed - Discordo - MVC é meramente um padrão de camada de apresentação. Não tem impacto sobre como você estrutura suas outras camadas, como BLL e DAL.
Casa Cory

Respostas:

69

A maneira como eu fiz isso - e não estou dizendo que seja certo ou errado, é ter minha Visão e então um modelo que se aplica à minha visão. Este modelo tem apenas o que é relevante para a minha visão - incluindo anotações de dados e regras de validação. O controlador hospeda apenas a lógica para a construção do modelo. Tenho uma camada de serviço que contém toda a lógica de negócios. Meus controladores chamam minha camada de serviço. Além disso, está minha camada de repositório.

Meus objetos de domínio são alojados separadamente (em seu próprio projeto, na verdade). Eles têm suas próprias anotações de dados e regras de validação. Meu repositório valida os objetos em meu domínio antes de salvá-los no banco de dados. Como cada objeto em meu domínio herda de uma classe base com validação embutida, meu repositório é genérico e valida tudo (e requer que herde da classe base).

Você pode pensar que ter dois conjuntos de modelos é duplicação de código, e até certo ponto. Porém, existem casos perfeitamente razoáveis ​​em que o objeto de domínio não é apropriado para a visualização.

O caso em questão é ao trabalhar com cartões de crédito - tenho que exigir um cvv ao processar um pagamento, mas não posso armazenar o cvv (é uma multa de $ 50.000 para fazer isso). Mas também quero que você edite seu cartão de crédito - mudança de endereço, nome ou data de validade. Mas você não vai me dar o número ou o cvv ao editá-lo, e certamente não vou colocar o número do seu cartão de crédito em texto simples na página. Meu domínio tem esses valores necessários para salvar um novo cartão de crédito porque você os fornece para mim, mas meu modelo de edição nem inclui o número do cartão ou cvv.

Outro benefício para tantas camadas é que, se arquitetado corretamente, você pode usar o structuremap ou outro contêiner IoC e trocar peças sem afetar negativamente o seu aplicativo.

Em minha opinião, o código do controlador deve ser apenas direcionado para o modo de exibição. Mostre isso, oculte aquilo etc. A camada de serviço deve abrigar a lógica de negócios do seu aplicativo. Gosto de ter tudo em um só lugar, então é fácil mudar ou ajustar uma regra de negócios. A camada de repositório deve ser relativamente burra - desprovida de lógica de negócios e apenas consultar seus dados e retornar seus objetos de domínio. Ao separar os modelos de visualização do modelo de domínio, você tem muito mais flexibilidade quando se trata de regras de validação customizadas. Isso também significa que você não precisa despejar todos os dados em sua visualização em campos ocultos e empurrá-los para frente e para trás entre o cliente e o servidor (ou reconstruí-los no backend).

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

Embora tudo pareça espalhado e sobreposto, tem o propósito de ser arquitetado dessa forma. É perfeito? Na verdade não. Mas eu prefiro isso a alguns designs anteriores de chamar repositórios do controlador e ter a lógica de negócios misturada no controlador, repositório e modelo.

Josh
fonte
Quase um espelho do que tenho em nosso aplicativo MVC corporativo. Uma arquitetura N-Tier. O aplicativo MVC interage apenas com objetos de negócios e serviços nas áreas N-Tier.
Ed DeGagne,
Quase o mesmo aqui. Projetos separados para definições, modelos, modelos de visão, DAL, etc. A única diferença é que meu DAL inclui lógica para nivelar dados para a web para otimizar a distribuição de dados complexos para relatórios ou visualizações personalizadas do cliente. Agora evito manter as coisas no cache do aplicativo para tabelas de pesquisa, etc, com farms da Web e nuvens do Azure em jogo.
Robert Achmann
1
@Josh, seria útil se você pudesse mostrar uma captura de tela de seu projeto de amostra?
shaijut
@Josh e se seu projeto não tiver banco de dados. Ele interage com referências de serviço. Todas as classes e métodos de domínio vêm dessas referências. Este cenário é adequado para estrutura em camadas?
user6395764
17

Muitas vezes me perguntei como exatamente os elementos MVC se encaixam em uma estrutura de aplicativo da web tradicional, onde você tem visualizações (páginas), controladores, serviços e objetos de dados (modelo). Como você disse, existem muitas versões disso.

Eu acredito que a confusão existe por causa da arquitetura amplamente aceita acima declarada, que usa o "modelo de domínio anêmico" (alegado) padrão -anti. Não vou entrar em muitos detalhes sobre o "antipadronismo" do modelo de dados anêmico (você pode ver um esforço meu para explicar as coisas aqui (baseado em Java, mas relevante para qualquer linguagem)). Mas, em resumo, isso significa que nosso modelo contém apenas dados e a lógica de negócios é colocada em serviços / gerentes.

Mas vamos supor que temos uma arquitetura orientada ao domínio , e nossos objetos de domínio são da maneira que se espera que sejam - tendo tanto estado quanto lógica de negócios. E nesta perspectiva orientada para o domínio, as coisas acontecem:

  • a visão é a IU
  • o controlador reúne as entradas da IU, invoca métodos no modelo e envia de volta uma resposta à IU
  • o modelo são os nossos componentes de negócios - contendo os dados, mas também tendo lógica de negócios.

Acho que isso responde às suas perguntas principais. As coisas ficam complicadas quando adicionamos mais camadas, como a camada de repositório. Frequentemente, é sugerido que ele deve ser chamado pela lógica de negócios colocada no modelo (e, portanto, cada objeto de domínio tem uma referência a um repositório). No meu artigo que vinculei, argumento que essa não é bem uma prática recomendada. E que, na verdade, não é ruim ter uma camada de serviço. A propósito, o design orientado ao domínio não exclui a camada de serviço, mas deve ser 'fina' e apenas coordenar objetos de domínio (portanto, não há lógica de negócios aí).

Para o paradigma do modelo de dados anêmicos, que é amplamente adotado (para o bem ou para o mal), o modelo seria a camada de serviço e seus objetos de dados.

Bozho
fonte
Excelente ponto! Uma observação: há a mesma confusão com os Serviços. Pelo menos os serviços podem ser serviços de aplicativo e serviços de domínio. O serviço de aplicativo é apenas um wrapper fino, que coleta informações de Repositórios, etc. O serviço de domínio fornece lógica de negócios, ou seja, a combinação de modelos de domínio ou apenas coisas que nem sempre se ajustam ao modelo de domínio.
Artru
e se o seu projeto não tiver banco de dados. Ele interage com referências de serviço. Todas as classes e métodos de domínio vêm dessas referências. Este cenário é adequado para estrutura em camadas?
user6395764
3

Na minha opinião,

Modelo -

Não deve conter lógica de negócios, deve ser conectável (cenário semelhante ao WCF). Ele é usado para vincular a visualização, portanto, deve ter propriedades.

Logíca de negócios -

Deve ser colocado em "Camada de Serviços de Domínio", é uma camada completamente separada. Além disso, adicionará mais uma camada aqui "Serviços de aplicativos".

Os Serviços de Aplicativos se comunicam com a camada de Serviços de Domínio para aplicar a lógica de negócios e, por último, retornar o Modelo.

Assim, o controlador solicitará o modelo do serviço de aplicativo e o fluxo será como,

    Controller->Application Services(using domain services)->Model
py2020
fonte
2

O padrão MVC e a estrutura Asp.net não fazem distinção sobre o que o modelo deve ser.

Os próprios exemplos da MS incluem classes de persistência no modelo. Sua pergunta sobre a adesão estar no modelo. Isso depende. As classes em seu modelo pertencem a algo? Existe um link entre quem efetua login e quais dados são exibidos? Existe filtragem de parte de dados de um sistema de permissões que é editável? Quem atualizou ou editou pela última vez um objeto faz parte do seu domínio como se outra pessoa precisasse vê-lo ou algo para suporte de back-end?

O exemplo do email também depende. Você está familiarizado com eventos de domínio ou eventos em particular? Você tem um serviço separado para enviar e-mails? O ato de enviar um e-mail faz parte do seu domínio ou é uma preocupação no nível do aplicativo fora do escopo do seu sistema? A IU precisa saber se um e-mail foi enviado com sucesso ou não? Os e-mails que falham no envio precisam de novas tentativas? O conteúdo do e-mail enviado precisa ser armazenado para atendimento aos requisitos de atendimento ao cliente?

Esses tipos de perguntas são muito amplos e subjetivos, mas estou respondendo para que você e todos que votaram em você possam entender isso.

Seus requisitos / cronogramas / recursos todos se infiltram na arquitetura do seu sistema. Até o modelo de receita pode ter um efeito. Você também deve considerar o padrão que está buscando. O DDD é muito diferente dos aplicativos de persistência como modelo e todas as falhas intermediárias também são válidas para determinados aplicativos. Você está atirando para testar o aplicativo? Tudo isso tem um efeito.

John Farrell
fonte