Você deve escrever seu back-end como uma API?


322

Hoje tive uma discussão acalorada sobre nosso aplicativo MVC. Temos um site escrito em MVC ( ASP.NET ) e, geralmente, segue o padrão de fazer algo na exibição -> pressionar o controlador -> o controlador cria um modelo (chama um gerente que obtém os dados, cria o modelo no diretório próprio método do controlador) -> o modelo é exibido -> enxaguar e repetir.

Ele disse que nosso código estava muito acoplado. Por exemplo, se quiséssemos um aplicativo de desktop também, não poderíamos usar nosso código existente.

A solução e as melhores práticas que ele disse é criar uma API e, em seguida, criar seu site em cima da API, e criar um aplicativo de desktop, aplicativo móvel etc. é muito simples.

Parece-me uma má ideia por várias razões.

De qualquer forma, parece que não consigo encontrar nada pesquisando no Google que possa discutir essa prática. Alguém tem alguma informação sobre prós, contras, por que você deveria, por que não deveria ou mais leituras?

Por algumas razões, acho uma má ideia:

  • É muito abstrato para executar seu back-end em uma API. Você está tentando torná-lo muito flexível, o que tornará uma bagunça incontrolável.

  • Todo o material incorporado ao MVC parece inútil, como funções e autenticação. Por exemplo, [Autorizar] atributos e segurança; você terá que fazer o seu próprio.

  • Todas as suas chamadas à API exigirão informações de segurança anexadas, e você precisará desenvolver um sistema de token e outros enfeites.

  • Você terá que escrever chamadas completas da API para todas as funções que seu programa executará. Praticamente todos os métodos que você deseja implementar precisarão ser executados em uma API. Uma operação Obter / Atualizar / Excluir para cada usuário, além de uma variante para a outra operação, por exemplo, atualizar nome do usuário, adicionar usuário a um grupo, etc. etc. e cada um deles seria uma chamada de API distinta.

  • Você perde todos os tipos de ferramentas, como interfaces e classes abstratas, quando se trata de APIs. Coisas como o WCF têm suporte muito tênue para interfaces.

  • Você tem um método que cria um usuário ou executa alguma tarefa. Se você deseja criar 50 usuários, basta chamá-lo 50 vezes. Quando você decide fazer esse método como uma API, seu servidor da web local pode se conectar a pipes nomeados e não há problema - o seu cliente de desktop também pode acessá-lo, mas de repente sua criação de usuários em massa envolve martelar a API pela Internet 50 vezes, o que não é um problema. é bom. Então você precisa criar um método em massa, mas na verdade você está apenas criando para clientes de desktop. Dessa forma, você acaba tendo que: a) modificar sua API com base no que está se integrando a ela e não pode se integrar diretamente a ela; b) fazer muito mais trabalho para criar uma função extra.

  • YAGNI . A menos que você esteja planejando especificamente escrever dois aplicativos que funcionem de forma idêntica, um da Web e um do Windows, por exemplo, é uma enorme quantidade de trabalho extra de desenvolvimento.

  • A depuração é muito mais difícil quando você não consegue avançar de ponta a ponta.

  • Muitas operações independentes que exigirão muitas idas e vindas, por exemplo, algum código pode obter o usuário atual, verificar se o usuário está na função de administrador, obter a empresa à qual o usuário pertence, obter uma lista de outros membros, enviar todos eles um email. Isso exigiria muitas chamadas de API ou a gravação de um método sob medida na tarefa específica que você deseja, onde o único benefício desse método sob medida seria a velocidade, mas a desvantagem seria que seria inflexível.

  • Provavelmente, mais algumas razões pelas quais essas coisas estão fora de minha cabeça.

Parece-me que, a menos que você realmente precise de dois aplicativos idênticos, não vale a pena. Também nunca vi um aplicativo ASP.NET criado dessa maneira, você teria que escrever dois aplicativos separados (a API e o seu código) e a versão controlá-los também (se sua página de usuário receber um novo campo, você ' você precisa atualizar a API e seu código de consumo simultaneamente para garantir que não haja efeitos negativos ou colocar muito trabalho extra para mantê-la robusta).


Edit: Algumas ótimas respostas, realmente começando a ter uma boa idéia do que tudo isso significa agora. Então, para expandir minha pergunta, como você estruturaria um aplicativo MVC para seguir essa estrutura da API?

Por exemplo, você tem um site que exibe informações sobre um usuário. No MVC, você tem:

Página HTML de exibição - (CS) que exibe um controlador UserViewModel - chama GetUser () e cria um UserViewModel que ele passa para a classe Manager de exibição (tipo de sua API) que possui um método GetUser.

O controlador usa GetUser (), mas você também deseja um aplicativo de desktop. Isso significa que seu GetUser precisa ser exposto por meio de algum tipo de API. Você pode querer uma conexão TCP, WCF ou talvez Remoting. Você também deseja um aplicativo móvel que seja RESTful, pois as conexões persistentes são difíceis.

Então, você escreveria uma API para cada uma, um serviço Web WCF que possui o método GetUser () e o código apenas return new UserManager().GetUser()? E um método mvc 4 web api que faz a mesma coisa? Continuando a chamar GetUser diretamente no seu método de controlador MVC?

Ou você escolheria a solução que funcionaria para todos os três (serviço REST da API da Web) e criaria tudo sobre isso, para que todos os três aplicativos façam chamadas de API (as de mvc, para a máquina local).

E este é apenas um cenário perfeito teórico? Eu posso ver grandes despesas gerais no desenvolvimento dessa maneira, especialmente se você precisar desenvolver de uma maneira que permita executar operações de maneira RESTful. Penso que parte disso foi abordada nas respostas.


Edit 2: Depois de ler mais coisas, coloquei um comentário abaixo que acho que pode explicar. A questão é um pouco complicada, eu acho. Se você escrever seu back-end como uma API, confundi-me pensando que deveria haver um único serviço da Web que tudo (aplicativo mvc, aplicativo para desktop, aplicativo móvel) chama para fazer coisas.

A conclusão a que cheguei é que o que você realmente deve fazer é garantir que a camada de lógica de negócios esteja corretamente dissociada. Observando meu código, eu já faço isso - o controlador chama GetUser()um gerente e cria um modelo de exibição para renderizar com um View. Realmente, a camada de lógica de negócios é uma API. Se você deseja chamá-lo em um aplicativo de desktop, precisará escrever algo como um serviço WCF para facilitar a chamada. Mesmo apenas ter um método WCF chamado GetUser()que contém o código return MyBusinessLayer.GetUser()seria suficiente. Portanto, a API é a lógica de negócios, e as APIs WCF / Web etc. são apenas um pouco de código para permitir que aplicativos externos o chamem.

Portanto, há alguma sobrecarga, pois é necessário agrupar sua camada de lógica de negócios em diferentes APIs, dependendo do que você precisar, e você precisará escrever um método de API para cada operação que deseja que seus outros aplicativos façam, além de precisar resolvemos uma maneira de fazer autenticação, mas na maioria das vezes é a mesma coisa. Coloque sua lógica de negócios em um projeto separado (biblioteca de classes) e você provavelmente não terá problemas!

Espero que esta interpretação esteja correta. Obrigado por toda a discussão / comentários que gerou.


25
Poderia, por favor, expor as razões pelas quais você acha que seria uma má idéia? Hoje em dia devo admitir que não vejo razão para NÃO fazê-lo. Faz, entre outras vantagens, portar seu aplicativo para diferentes plataformas muito mais fácil e permite uma grande flexibilidade no front-end sem sequer tocar o seu código de back-end ...
Laurent S.

12
@SLC: Quando você diz API, você quer dizer uma API de serviço da web como uma interface SOAP ou REST? Porque você deve tornar o back-end uma API, mas não um serviço da Web.
JacquesB

7
@IanNewson "um aplicativo móvel, por exemplo, eles tendem a ter menos recursos". Eu nunca ouvi uma razão convincente pela qual os aplicativos móveis deveriam ser cidadãos de segunda classe ... (mas todo mundo parece fazê-lo dessa maneira) #
Michael

3
@IanNewson talvez seja apenas me então ... mas eu sempre encontrar-me paralisado por não ser capaz de fazer alguma coisa ou outra no celular ao ponto onde eu faço muito pouco no celular
Michael

11
Você diz que o YAGNI se aplica, mas minha experiência tem sido os aplicativos obterem uma interface de usuário reescrita a cada dois anos ou todos reclamam que precisam de um. Certamente seria bom se não perdêssemos nossa lógica de negócios porque uma nova tecnologia de front-end chegou.
corsiKa

Respostas:


282

Sim você deveria.

Isso não apenas reutiliza o back-end, mas também oferece mais segurança e melhor design. Se você escreve seu back-end como parte de um único sistema, está criando um design monolítico que nunca é fácil de ampliar, substituir ou aprimorar.

Uma área em que isso é popular no momento é em microsserviços . Onde o back-end é dividido em muitos serviços pequenos (ou até grandes), que fornecem uma API que o sistema cliente consome. Se você imagina usar muitas fontes de dados de terceiros em seu aplicativo, percebe que já deve estar fazendo isso.

Um outro benefício é que a construção e a manutenção de cada serviço podem ser entregues a uma equipe diferente; eles podem adicionar recursos a ele que não afetam nenhuma outra equipe que produz o produto. Somente quando eles terminam e liberam o serviço, eles começam a adicionar recursos ao seu produto para consumi-los. Isso pode tornar o desenvolvimento muito mais suave (embora potencialmente mais lento no geral, você tenderia a obter melhor qualidade e compreensão)


Edit: OK, vejo seu problema. Você pensa na API como uma biblioteca remota. Não é. Pense no serviço como mais um serviço de fornecimento de dados. Você chama o serviço para obter dados e, em seguida, executa essas operações localmente. Para determinar se um usuário está conectado, você chamaria " GetUser" e verificaria o 'logged on'valor, por exemplo. ( YMMV com esse exemplo, é claro).

Seu exemplo para a criação de usuários em massa está apenas dando desculpas - não há diferença aqui, o que você poderia ter feito em um sistema monolítico ainda pode ser feito em uma arquitetura de serviço (por exemplo, você teria passado vários usuários para criar em massa ou um único para criar. Você ainda pode fazer exatamente o mesmo com os serviços).

O MVC já está baseado no conceito de serviços isolados, apenas as estruturas do MVC os agrupam em um único projeto. Isso não significa que você perca nada, exceto os auxiliares fornecidos por sua estrutura. Use uma estrutura diferente e você terá que usar ajudantes diferentes. Ou, nesse caso, rolando por conta própria (ou adicionando-as diretamente usando uma biblioteca).

A depuração também é fácil - você pode testar minuciosamente a API isoladamente para não precisar depurá-la (e pode depurar de ponta a ponta, o Visual Studio pode se conectar a vários processos simultaneamente).

Coisas como trabalho extra na implementação da segurança são boas. Atualmente, se você agrupar todo o código em seu site, se um hacker obtiver acesso a ele, ele também terá acesso a tudo, incluindo o DB. Se você o dividir em uma API, o hacker pode fazer muito pouco com o seu código, a menos que também invadam a camada da API - o que será incrivelmente difícil para eles (você já se perguntou como os invasores ganham uma vasta lista de usuários de todos os sites ou detalhes de CC? eles invadiram o sistema operacional ou o servidor da web e ele tinha uma conexão direta com o banco de dados onde " select * from users" eles poderiam executar com facilidade).

Direi que vi muitos sites (e aplicativos cliente-servidor) escritos assim. Quando eu trabalhei no setor de serviços financeiros, ninguém jamais escreveria um site tudo em um, em parte porque isso representa um grande risco à segurança, e em parte porque muito do desenvolvimento é bastante GUIs sobre processamento de dados de back-end estável (ou seja, legado) sistemas. É fácil expor o sistema DP como um site usando uma arquitetura de estilo de serviço.

2ª Edição: Alguns links sobre o assunto (para o OP):

Observe que, ao falar sobre eles no contexto de um site, o servidor da web deve ser considerado a camada de apresentação, porque é o cliente que chama as outras camadas e também porque constrói as visualizações da interface do usuário enviadas ao navegador para renderização. É um assunto importante, e há muitas maneiras de projetar seu aplicativo - centralizado em dados ou centralizado em domínio (eu normalmente considero centralizado em domínio como 'mais puro', mas YMMV ), mas tudo se resume a manter uma camada lógica entre elas. seu cliente e seu banco de dados. É um pouco como o MVC, se você considerar o nível intermediário da API equivalente ao seu modelo, apenas o modelo não é um invólucro simples para o banco de dados, é mais rico e pode fazer muito mais (por exemplo, agregar dados de 2 fontes de dados, publicar -processa os dados para caber na API, armazenar em cache os dados etc.):


2
Isso é um sim do ponto de vista da astronauta da arquitetura? Eu posso entender seus parágrafos 2 e 3 do ponto de vista do serviço, mas estamos falando de GetUser, CreateUser, IsUserLoggedIn e centenas de pequenas funções que antes eram linhas de código únicas sendo convertidas em chamadas de API.
NibblyPig

12
Imagine que você está escrevendo como um site - todas essas pequenas funções não podem ser tão interativas quanto você imagina; portanto, você terá que obter os dados e armazená-los em cache localmente enquanto constrói sua página (ou passá-los como dados potencialmente obsoletos para o cliente, conforme apropriado ao sistema). Por muito disso, é necessário alterar seu design de "reagir sob demanda" para "antecipar antecipadamente", mas a maior parte do seu sistema fará chamadas de API. Projete sua API para ser menos granular e mais centrada em dados, para que IsUserLoggedOn não precise ser uma chamada de API, você só precisará de um "GetUserDetails" depois de inspecionar localmente.
Gbjbaanb

5
Usamos esse método no meu último local de trabalho e funcionou maravilhosamente. Nosso principal produto era um aplicativo da web, mas conseguimos criar um aplicativo de desktop e até planilhas do Excel que pudessem acessar os mesmos serviços da web que nosso aplicativo da web fazia para todos os seus dados, além de expor os serviços aos nossos clientes para que eles pudessem programa contra eles.
Kik

2
Aqui está outro benefício: você pode expor a API de back-end aos clientes do seu site. Em nossa empresa, fizemos isso e alguns grandes clientes de empresas de software (depois de experimentar o back-end em nosso host) pagaram para que o back-end fosse embrulhado como um produto auto-hospedado por si só. Dependendo do produto, alguns clientes estão menos interessados ​​no verniz de front-end e muito mais interessados ​​no que seu produto realmente faz - o back-end. Esse é outro produto para vender.
Reid

2
Isso também facilita o uso da mesma lógica em um serviço da web. Uma dessas coisas que nossas equipes sempre pensam que nunca precisaremos fazer ... Também facilita o teste de unidade.
Ps2goat

87

Você não pode evitar a criação de uma API . Mesmo se você criar "apenas um site", ele ainda precisará obter os dados do seu back-end de alguma forma. No entanto, você decide fazer isso, essa é a sua API de fato .

Sabendo disso, a verdadeira questão não é se deve criar uma API, mas como construí-la . Você pode fazê-lo on-the-fly como algo ad hoc - e, de fato, muitos sites são criados exatamente dessa maneira - ou você pode projetá-lo com cuidado para ser utilizável em outros contextos. Colocado nesse contexto, fica bem claro que seu colega está certo: você deve primeiro fazer a API e, em seguida, criar seu site sobre ela.

No entanto, isso traz algumas preocupações, como você aponta. Para resolvê-los:

É muito abstrato para executar seu back-end em uma API. Você está tentando torná-lo muito flexível, o que o tornará uma bagunça incontrolável.

Isso depende de como você faz. Como George Pólya aponta em seu excelente texto Como resolvê-lo , muitas vezes "o problema mais geral pode ser mais fácil de resolver". Isso é chamado de paradoxo do inventor . No caso da programação, geralmente funciona por meio da separação de preocupações: seu back-end não precisa mais se preocupar com o formato dos dados que coloca e retira, e, portanto, seu código pode ser muito mais simples. Seus analisadores e renderizadores de dados não precisam mais se preocupar com o que acontece com os dados que eles criam, para que eles também possam ser mais simples. Tudo funciona dividindo o código em partes mais gerenciáveis.

Todo o material incorporado ao MVC parece inútil, como funções e autenticação. Por exemplo, [Autorizar] atributos e segurança; você terá que fazer o seu próprio.

Confesso que acho extremamente difícil simpatizar com as pessoas que se recusam a aprender suas ferramentas. Só porque você não entende o uso deles não significa que eles são inúteis, e isso certamente não significa que você deve usar o seu . Pelo contrário; você não deve rolar suas próprias ferramentas até entender as alternativas, para ter certeza de resolver os mesmos problemas que eles (mesmo que apenas do seu jeito).

Considere Linus Torvalds , que é mais famoso por escrever Linux , mas também escreveu git : agora um dos sistemas de controle de versão mais populares do mundo. Um dos fatores determinantes em seu design foi uma profunda oposição ao Subversion (outro VCS extremamente popular e provavelmente o mais popular na época em que o git foi escrito); ele resolveu pegar tudo o que o Subversion poderia, e na medida do possível, resolver esses problemas de maneira diferente. Para fazer isso, ele teve que se tornar um especialista em Subversion , precisamente para poder entender os mesmos domínios de problemas e adotar uma abordagem diferente.

Ou, no processo de aprendizado de suas ferramentas, você pode achar que elas são úteis como estão e não precisam ser substituídas.

Todas as suas chamadas à API exigirão informações de segurança anexadas, e você precisará desenvolver um sistema de token e outros enfeites.

Sim. Assim é como deve ser.

Você terá que escrever chamadas completas da API para todas as funções que seu programa executará. Praticamente todos os métodos que você deseja implementar precisarão ser executados em uma API. Uma operação Obter / Atualizar / Excluir para cada usuário, além de uma variante para a outra operação, por exemplo, atualizar nome do usuário, adicionar usuário a um grupo, etc. etc. e cada um deles seria uma chamada de API distinta.

Não necessariamente. É aqui que arquiteturas como REST entram em cena. Você identifica os recursos com os quais seu aplicativo trabalha e as operações que fazem sentido aplicar a esses recursos e os implementa sem se preocupar tanto com os outros .

Você perde todos os tipos de ferramentas, como interfaces e classes abstratas, quando se trata de APIs. Coisas como o WCF têm suporte muito tênue para interfaces.

Pelo contrário, as interfaces se tornam muito mais importantes quando você usa uma API, e não menos . Eles aparecem nas representações em que você os renderiza. Atualmente, a maioria das pessoas especifica um formato baseado em JSON para isso, mas você pode usar qualquer formato que desejar, desde que o especifique bem. Você renderiza a saída de suas chamadas para esse formato no back-end e analisa-o como desejar (provavelmente o mesmo tipo de objeto) no front-end. A sobrecarga é pequena e os ganhos em flexibilidade são enormes.

Você tem um método que cria um usuário ou executa alguma tarefa. Se você deseja criar 50 usuários, basta chamá-lo 50 vezes. Quando você decide fazer esse método como uma API, seu servidor da web local pode se conectar a pipes nomeados e não há problema - o seu cliente de desktop também pode acessá-lo, mas de repente sua criação de usuários em massa envolve martelar a API pela Internet 50 vezes, o que não é um problema. é bom. Então você precisa criar um método em massa, mas na verdade você está apenas criando para clientes de desktop. Dessa forma, você acaba tendo que: a) modificar sua API com base no que está se integrando a ela e não pode se integrar diretamente a ela; b) fazer muito mais trabalho para criar uma função extra.

Criar uma versão em massa de um método existente dificilmente seria algo que eu chamaria de "muito mais trabalho". Se você não está preocupado com coisas como atomicidade, o método em massa pode acabar sendo não muito mais do que uma interface muito fina para o original.

YAGNI . A menos que você esteja planejando especificamente escrever dois aplicativos que funcionem de forma idêntica, um da Web e um do Windows, por exemplo, é uma enorme quantidade de trabalho extra de desenvolvimento.

Não, YANI (você já precisa). Eu descrevi isso como acima. A única questão é quanto trabalho de design colocar nele.

A depuração é muito mais difícil quando você não consegue avançar de ponta a ponta.

Por que você não seria capaz de avançar de ponta a ponta?

Mas, mais importante, ser capaz de examinar os dados em um formato facilmente reconhecido que elimina toda a quantidade de exibição tende a tornar a depuração mais fácil , e não mais difícil.

Muitas operações independentes que exigirão muitas idas e vindas, por exemplo, algum código pode obter o usuário atual, verificar se o usuário está na função de administrador, obter a empresa à qual o usuário pertence, obter uma lista de outros membros, enviar todos eles um email. Isso exigiria muitas chamadas de API ou a gravação de um método sob medida na tarefa específica que você deseja, onde o único benefício desse método sob medida seria a velocidade, mas a desvantagem seria que seria inflexível.

O REST resolve isso trabalhando em objetos completos ( recursos , para usar os termos da teoria REST), em vez das propriedades individuais dos objetos . Para atualizar o nome de um usuário, obtenha o objeto de usuário, altere seu nome e COLOQUE o usuário de volta. Você pode fazer outras alterações ao mesmo tempo em que também altera o nome do usuário. O problema mais geral fica mais fácil de resolver, porque você pode eliminar todas as chamadas individuais para atualizar propriedades individuais de um objeto: basta carregá-lo e salvá-lo.

De certa forma, isso não é diferente das arquiteturas RISC no lado do hardware. Uma das principais diferenças entre o RISC e o CISC (seu antecessor) é que as arquiteturas CISC tendem a incluir muitas instruções que operam diretamente na memória, enquanto as arquiteturas RISC tendem a operar principalmente em registros: em uma arquitetura puramente RISC, as únicas operações na memória são LOAD (copie algo da memória para um registro) e STORE (pegue um valor de um registro e coloque-o na memória).

Você pensaria que isso significaria fazer muito mais viagens dos registros para a memória, o que tornaria a máquina mais lenta. Mas, na prática, acontece frequentemente o contrário: o processador (cliente) trabalha mais entre as viagens à memória (servidor) , e é daí que a aceleração vem.

Para encurtar a história: seu colega está certo. Este é o caminho a percorrer. Em troca de um pouco de trabalho inicial, simplificará drasticamente o código do seu site e permitirá uma melhor integração com outros sites e aplicativos. Esse é um preço que vale a pena pagar.

Leitura adicional:

  1. Design da API REST - Modelagem de Recursos

7
Mesmo estes possuem APIs de fato de um tipo. Eles tendem a fazer muitos outros desenvolvedores empalidecerem de horror, mas são APIs da mesma forma; apenas não muito bem desenhados.
O Spooniest

7
Isso cria uma API realmente ruim: tão ruim que muitas pessoas nem pensam nela como uma API. Mas ainda define a maneira como o front-end interage com o back-end, por mais bruto que seja. Pensar nisso como uma API ajuda a destacar a importância de fazê-lo bem.
O Spooniest

1
Acho que Linus fez o git porque a comunidade Linux se rebelou contra o uso do Bitkeeper DVCS comercial usado para o kernel.
Gbjbaanb

2
Sua primeira frase dissipa toda a minha confusão. Associei o termo API a um serviço da Web e esse é o principal motivo pelo qual fiquei tão confuso.
NibblyPig

4
@ IanNewson: existe uma maneira de interagir com o código, chamado http. Pode ter muitos requisitos irrelevantes e retornar muitos dados irrelevantes, mas é isso que a torna uma péssima API.
jmoreno

63

Sei que os microsserviços estão no caminho certo agora, mas nem sempre valem a pena. Sim, o código fracamente acoplado é o objetivo. Mas não deve ocorrer às custas de um ciclo de desenvolvimento mais doloroso.

Um bom meio termo seria criar um projeto de dados separado em sua solução. O projeto de dados seria uma biblioteca de classes .NET. Seu projeto ASP.NET MVC adicionaria uma referência à biblioteca de dados e todos os modelos seriam extraídos do projeto de dados. Então, quando chegou a hora de criar um aplicativo para computador ou celular, você pode fazer referência ao mesmo código. Portanto, pode não ser uma API oficial, mas funcionará como uma. Se você quiser torná-lo acessível como uma API, poderá criar um projeto da Web simples que atue como um wrapper no projeto de dados.

A Microsoft tem promovido esse conceito, que eles chamam de Bibliotecas de Classes Portáteis .


13
Eu tive que manter um projeto em que a lógica foi colocada na camada da interface do usuário, chamando as mesmas estruturas de dados compartilhadas. Eu tive que corrigir um bug trinta vezes por causa disso ("se precisarmos usar a mesma lógica novamente, copiaremos e colaremos! Não há necessidade de uma API"). Se houvesse uma camada lógica (agora existe), seria suficiente com apenas uma correção.
SJuan76

1
Essa resposta, além de agrupar essa biblioteca em seu próprio pacote NuGet e hospedar seu próprio feed / servidor de pacote NuGet, também é um bom caminho. Você não precisa se preocupar com redes sofisticadas e pode fazer todas as chamadas locais para um encadeamento (e, portanto, mais rápido), além de apresentar uma versão adequada à sua lib de classe com o NuGet, oferece flexibilidade a outras equipes na atualização.
precisa

34

Não, você não deveria . Se você não tem planos imediatos para criar front-ends alternativos (como aplicativos móveis ou de desktop ou aplicativos da Web separados) que acessam o mesmo back-end, não deve introduzir uma camada de serviço da Web. YAGNI .

Um acoplamento fraco é sempre desejável (junto com alta coesão), mas é um princípio de design e não significa que você precise separar objetos fisicamente em diferentes servidores! E uma API de serviço mal projetada pode criar um acoplamento rígido entre os limites do servidor, portanto, ter uma API não garante um acoplamento flexível.

Se a necessidade de uma API de serviço surgir no futuro, você sempre poderá apresentá-la nesse momento. Enquanto você mantiver seu código em camadas (acesso a dados e lógica de negócios de forma limpa e lógica de interface do usuário), não será mais difícil introduzir mais tarde do que é agora. E o design resultante será muito melhor quando projetado para atender aos requisitos reais.


Observe que estou assumindo que a pergunta é se você deve criar uma API de serviço da web ou não. A pergunta diz apenas API, mas API também pode significar apenas a interface de uma biblioteca e, é claro, todas as camadas terão uma API por definição. A conclusão é que a lógica de negócios e as camadas de acesso a dados devem ser separadas de forma limpa da lógica da interface do usuário no nível do design, mas você não deve introduzir uma camada de serviço da Web se não precisar.


8
Nada mal projetado não é bom. Construir uma API não é mais tempo e é mais à prova de futuro. A capacidade de se adaptar às mudanças sendo vital hoje em dia, melhor construir uma base forte para cumprir todas as necessidades que você nem sequer sabem sobre mas que poderia vir mais cedo do que você pensa ...
Laurent S.

9
@ Bartdude: A introdução de uma complexidade desnecessária em prol da "prova do futuro" para um futuro que não chegará está apenas desperdiçando recursos.
JacquesB

6
@ Bartdude adicionando uma API é definitivamente mais tempo. Não faço ideia de como você acha que pode reivindicar o contrário.
26615 Ian Newson

13
"você não deve introduzir uma camada de serviço da web" API! = serviço da web. Se você tiver sua lógica de negócios por trás de uma API, poderá expor essa API como um serviço da Web em algum momento. Não é um requisito inicial, no entanto.
26615 Celos

2
@ JacquesB: ... então você realmente não desenvolve recursos se não tiver certeza de que precisará. É o que eu entendo de YAGNI. No entanto, a arquitetura não é um recurso e más escolhas arquitetônicas podem (e muito provavelmente levarão) a uma falha miserável. Mais uma vez, suponho que essa discussão possa ocorrer, o que às vezes não é o caso por razões de orçamento, tempo de colocação no mercado, recursos ou falta de conhecimento ... Acho que podemos concordar totalmente em discordar disso, embora Eu entendo o seu ponto como muitas vezes eu tinha a mesma discussão com me ^ _ ^
Laurent S.

29

Minha empresa possui um aplicativo criado assim. Inicialmente, fomos contratados para criar um back-end com API para um front-end que outro desenvolvedor estava criando. Quando o outro desenvolvedor não conseguiu desenvolver esse front end, fomos contratados para criar o front end também. Embora haja definitivamente benefícios nessa abordagem, há uma enorme desvantagem: custo. A construção inicial será significativamente mais cara e a manutenção contínua será mais cara, devido a mais código a ser mantido e à implantação de dois sistemas separados. Devido ao custo extra, essa sempre deve ser uma decisão de negócios, não tomada por capricho pelos desenvolvedores.

Para colocar um número, eu estimaria que o projeto mencionado acima custasse 20% a mais devido a essa abordagem. Você não descreve em que tipo de projeto está trabalhando e em que tipo de empresa trabalha, mas se você é um iniciante na construção do produto, esse custo extra pode ser a diferença entre o envio de alguns recursos extras que tornam seu produto um sucesso.

Outro motivo para não fazê-lo, pelo menos não universalmente, é que, se você decidir criar a segunda interface, raramente haverá um mapeamento de funcionalidade individual. Se você cria um aplicativo móvel, por exemplo, eles tendem a ter menos recursos. Isso significa que alguns dos seus métodos de API nunca serão reutilizados. Portanto, um compromisso com seu colega pode ser o de decidir entre você as chamadas mais cruciais / críticas e adicioná-las a uma API e usar métodos mais tradicionais para todo o resto.

Outro ponto a considerar é que seu colega está dizendo que você não poderá reutilizar seu código existente, o que não é verdade se você tiver alguma separação da lógica de negócios. Você só precisa criar um invólucro de serviço da Web fino em torno de suas APIs internas, o que não é uma tarefa particularmente grande. Seria ingênuo pensar que você poderia reutilizar uma camada de serviço da web para outro front end de qualquer maneira, sem nenhuma alteração.


22

Depende do tipo de aplicação e do tipo de mercado em que você está.

Existem vantagens e desvantagens em seguir esse caminho. Não é uma resposta clara que um caminho seja melhor que o outro.

Vou falar por experiência pessoal. Fui eu quem decidiu usar a base de código em que trabalho nessa direção em 2007. Essa base de código está na ordem de um milhão de linhas de código agora, metade do qual é código de servidor oculto por uma enorme quantidade de serviços da web APIs, a outra metade é uma flotilha de clientes, desktop nativo, web para desktop, dispositivos móveis, integrações de back-end, etc ... Essa decisão não foi isenta de desvantagens, mas com uma retrospectiva de 20/20, posso dizer que o faria novamente . Deixe-me indicar algumas das compensações envolvidas.

Benefícios

  • Flexibilidade. Seja uma solicitação para criar um aplicativo móvel para aprimorar a experiência de desktop ou uma integração com o back-end da SAP, tudo fica mais fácil quando você já tem uma API para chamar. Quando você receber o suficiente dessas solicitações, evoluirá organicamente em direção a uma API, e a única questão é se ele possui um serviço da Web padrão à sua frente ou se é uma API interna em que os serviços da Web são personalizados.

  • Escalabilidade (da equipe). No nosso caso, temos muitos grupos diferentes de desenvolvedores, todos construídos sobre essa API. Temos equipes de API dedicadas, que conversam com os diferentes grupos, resumem as necessidades e constroem uma API para todos os fins. Chegou ao ponto em que nem mais nos dizem que as pessoas estão construindo coisas sobre a API, e nem todo mundo que faz isso trabalha para a nossa empresa.

  • Segurança. Ter uma divisão clara entre as partes inseguras e seguras da sua base de código é útil para determinar a segurança. Confundir a interface do usuário e o código de back-end juntos tende a confundir as coisas.

Trade-offs

  • Flexibilidade. Você precisa fazer o trabalho para "criar corretamente" algo na API. Não é possível executar rapidamente uma consulta ao banco de dados a partir do código da interface do usuário para resolver um problema específico. Além disso, as APIs que são realmente reutilizáveis ​​devem levar em consideração tantos casos de uso que a solução rápida geralmente é a solução errada. A API se torna menos flexível para evoluir, especialmente porque já existe muito código de cliente (estamos fazendo a transição para uma API com versão por esse motivo).

  • Velocidade de desenvolvimento inicial. É mais lento desenvolver a API primeiro, sem um pingo de dúvida. Você só ganha de volta quando tem clientes suficientes criados sobre a API. Mas então você descobre que precisa de três implementações de clientes diferentes antes que sua API evolua para ser genérica o suficiente. Descobrimos que a maioria dos nossos designs iniciais de API estava errada e tivemos que revisar fortemente nossas diretrizes sobre como criar serviços da Web.

Arenques vermelhos

Você mencionou um monte deles. Na verdade, eles não importam na prática.

  • Abstração. Sua API se torna abstrata o suficiente para cobrir todos os casos de uso que seu produto precisa atender, e não mais do que isso. Mesmo sem os serviços da web, você terá uma API interna que faz isso ou terá muito código duplicado. Eu prefiro abstração do que duplicação.

  • Abandonando a pilha MVC do lado do servidor. Hoje em dia, quase todos os sistemas precisam de um aplicativo móvel após algum tempo. Ao criar serviços da Web para atender a esse aplicativo móvel, você precisará descobrir como fazer autenticação e autorização em um contexto de API de qualquer maneira. Na verdade, é menos trabalhoso quando você tem apenas uma maneira de fazer isso, a maneira como faz nos seus serviços da web.

  • Operações em massa. Geralmente resolvido com a criação de uma API em massa que inicia um trabalho de back-end e retorna um ID do trabalho para consulta de status. Não é grande coisa.

  • Depuração. Descobri que, no geral, ficou um pouco mais fácil solucionar problemas do sistema. Você ainda pode definir pontos de interrupção no código de front-end e back-end, portanto, na prática, não é tão difícil assim, e você ganha a capacidade de criar testes de API automatizados e instrumentá-la para monitorar os sistemas de produção.

  • Muitas operações independentes. É uma questão de como você projeta as coisas. Se você insistir em ter uma API CRUD pura, sim, você sofrerá com esse problema. Porém, ter algumas APIs do CQRS para aumentar normalmente é uma boa ideia e, se você tiver uma API interna para a qual os serviços são front-end, poderá reutilizar facilmente essa API interna para construir serviços para aqueles específicos. cenário.

Em suma

Em um sistema usado em contextos diferentes o suficiente, uma API evoluirá naturalmente, pois é a maneira mais fácil de atender a todas as necessidades. Mas há definitivamente um caso de YAGNI acontecendo. Existem trade-offs e isso não faz sentido até que faça sentido. O ponto principal é não ser dogmático e manter a mente aberta em relação a diferentes abordagens na arquitetura para atender às crescentes necessidades do produto.


Leitura interessante, você pode elaborar o que fez de errado ao criar a API e o que aprendeu?
Aaaaaaaaaaaa

3
Os três principais erros foram: (1) adaptando a API às necessidades da interface do usuário principal, (2) construindo o estado através de várias solicitações usando sessões (estamos gradualmente ficando sem sessão) e (3) apenas suportando o uso de db id como identificador, em que um código configurável pelo usuário geralmente é um identificador melhor (para integrações com sistemas externos, normalmente eles desejam carregar identificadores em nosso sistema para uso posterior em APIs, em vez de vice-versa). Esses três, juntamente com documentação fraca e mensagens de erro inúteis, impossibilitaram o uso da API sem assistência.
Joeri Sebrechts

10

O que seu colega está descrevendo é uma arquitetura orientada a serviços. Essa pode ser uma maneira tremendamente escalável, testável e sensata de codificar, mas realmente depende do que você está criando.

Existem alguns benefícios significativos para a SOA, que tentarei enumerar:

Escalabilidade

Como seu back-end é desacoplado, seu front-end se torna apenas uma série de modelos, até arquivos simples. Os arquivos simples são tremendamente rápidos e baratos para servir em qualquer CDN. Eles podem ser minificados e pré-compilados em HTML estático e preenchidos com dados dos clientes.

Sua API precisa permanecer consistente, mas pode ser trocada por uma tecnologia mais rápida sem quebrar sua pilha se você superar a tecnologia existente. Você pode refazê-lo no Go, por exemplo. Você pode reconstruí-lo aos poucos e distribuir a carga pelos servidores. Enquanto a interface permanecer a mesma, a tecnologia será abstraída.

Testabilidade

O MVC geralmente começa limpo, mas na prática os controladores raramente ficam focados em um único recurso. Quanto mais os métodos do seu controlador fizerem, menos testável eles se tornam.

Uma API contorna esse problema. Cada chamada de API extrai um recurso e o serve. Limpo e testável.

Separação garantida de preocupações

Seu front end e back end são totalmente divorciados. Você pode fornecer o front end para outro desenvolvedor ou designer. Este é o MVC levado para outro nível. Tenho certeza que você não gostaria de desistir do MVC. SOA é MVC, mas mais ainda.

Desvantagens

É claro que existem algumas desvantagens. Um monólito geralmente é mais rápido para começar. Pode ser o que você está acostumado. Pode se encaixar melhor na sua pilha. Suas ferramentas podem ser otimizadas para criação de monólitos.

Nenhuma dessas são razões particularmente boas na minha opinião, e você pode considerar reequipar se elas se aplicarem a você.


Esta é a resposta mais clara até agora.
Tony Ennis

7

Há muitas boas respostas aqui, então adicionarei minha experiência de implementação.

É assim que eu faço as coisas:

  • Crie uma camada de acesso ao banco de dados que lide com todas / somente a interação com o banco de dados (geralmente o SQL manual é usado para velocidade e controle, sem ORM) . Inserir, Atualizar, Excluir, Selecionar ...
  • Crie um interface( virtual class) que exponha / reforce as funções de API necessárias. Quando implementados, eles usarão as funções DBAL altamente especializadas para obter os resultados. Isso também me ajuda a impor a API no nível do compilador, para garantir que a implementação do servidor + API tenha todas as funções incorporadas.
  • Crie uma segunda camada que implemente a interface (essa é a API real) e imponha restrições de segurança. Você também interage com APIs externas aqui.
  • O site usará a segunda camada diretamente (para desempenho) sem passar por uma API acessível remotamente (como SOAP, JSON) .
  • Um servidor autônomo é construído que implementa a interface e expõe a Segunda Camada como uma API acessível remota real a clientes externos de desktop / dispositivos móveis (acesso que não é do site) . Tudo o que faz é decodificar solicitações e codificar respostas e gerenciar / desconectar clientes. Ele também suporta recursos de pushback para notificar clientes em massa sobre eventos gerados por outros pares conectados (funcionalidade que um site geralmente não exige) .

Portanto, tecnicamente, a API é a segunda camada. Você o usa diretamente no site e o expõe a clientes remotos por meio de um servidor. O código é reutilizado e nenhum pedaço reutilizável de código está embutido. (viva e morra de acordo com essa regra e tudo é incrível) Ajuda com manutenção, teste ... tudo.

Você nunca conecta o site ao servidor de API para desktop / dispositivo móvel (a menos que seu site seja AJAX e execute em JSON) . Mas se o site renderizar conteúdo dinâmico na marcação, passar por uma API intermediária diminuirá seu desempenho. O site precisa ser rápido! O acesso remoto ao cliente pode ser um pouco mais lento.

PS : Sim, a manutenção é um pouco mais complexa, pois mais rodas trabalham juntas, mas é mais fácil a longo prazo. Portanto, se seu projeto tiver a duração de um tempo e for um pouco complexo, sempre tenha uma API. Também é muito mais fácil testar cada camada por conta própria.


Isso soa bem legal e faz muito sentido, especialmente colocando uma interface nas funções do tipo API. Vou tentar isso da próxima vez que criar um projeto!
NibblyPig

6

O ponto de discórdia não é se você deve usar uma API, mas o que realmente é uma "API". A única alternativa ao uso de uma API projetada é usar uma API que é uma bagunça aleatória de código. Você escreve que uma API torna as coisas "flexíveis demais", o que, por sua vez, torna as coisas incontroláveis. Isso aponta para um completo e completo mal-entendido sobre o que é uma API. Se esse mal-entendido não é compartilhado entre você e seu colega de trabalho, você perde muito tempo discutindo coisas completamente diferentes.

Ao não usar uma API bem definida, você pode fazer o que quiser. Por definição, esta é a opção mais flexível. Além disso, por definição "faça o que quiser" ainda é uma API. O único trabalho de uma API é remover a flexibilidade. Ao remover a flexibilidade, uma boa API incentiva o usuário a fazer coisas semelhantes de maneiras semelhantes.

Obviamente, uma API incorreta pode oferecer muita ou pouca flexibilidade, ou mesmo as duas ao mesmo tempo. Uma API realmente mal projetada pode matar um projeto ainda mais rápido do que a abordagem "vale tudo". No entanto, a melhor prática é simplesmente ter programadores competentes que desenvolvam e evoluem a API ao lado de seu aplicativo.

Exemplo

• Muitas operações independentes que exigirão muitas idas e vindas, por exemplo, alguns códigos podem obter o usuário atual, verificar se o usuário está na função de administrador, obter a empresa à qual o usuário pertence, obter uma lista de outros membros, enviá-los tudo um email. Isso exigiria muitas chamadas de API ou a gravação de um método sob medida na tarefa específica que você deseja, onde o único benefício desse método sob medida seria a velocidade, mas a desvantagem seria que seria inflexível.

O número de chamadas de API que isso exigiria em uma API decente provavelmente seria 1. Sim, é inflexível, mas por que você deseja que ela seja flexível?


4

Ele disse que nosso código estava muito acoplado. Por exemplo, se quiséssemos um aplicativo de desktop também, não poderíamos usar nosso código existente.

Bem e você? Caso contrário, essa é uma afirmação bastante irrelevante.

Eu diria que, se você fosse criar um novo aplicativo em 2015, analise seriamente algo com uma interface de usuário que envolve uma API e não páginas HTML geradas pelo servidor. Existem custos claros, mas também benefícios claros.

Mas se você tem um site existente, sem planos concretos para ter várias interfaces diferentes (até onde eu sei), os comentários dele são irrelevantes.


4

Versão curta: Seu controlador efetivamente é uma API, não importa o quê; embora o ASP.NET possa estar ocultando isso.

Versão mais longa:

Pense em um MVC Web App básico que fornece informações sobre cerveja e, opcionalmente, vende uma. Como são as rotas?

/sign_in
/sign_out
/beer
/beer/{beer_name}
/order
/order/{order_number}

Em um aplicativo Web normal, provavelmente existem algumas rotas auxiliares como:

/beer/new
/beer/{beer_name}/edit
/beer/{beer_name}/delete
/order/new
/order/{order_number}/edit
/order/{order_number}/delete

Em uma API da Web, isso não é necessário, pois é deduzido do método HTTP.

Dada a simetria acima, acho que isso faz um caso bastante convincente de que sua API e Controller estão tão próximos que podem ser a mesma coisa.

Depois de fazer algumas pesquisas, concluí que esse pode ser o estado de coisas para você, dependendo da versão do ASP.NET que você está usando. O MVC 5 anterior e anterior não possuem as convenções e a interface para unificar profundamente as duas implementações. Nas versões antigas, o retorno do Web App preenche uma Visualização, enquanto a API fornece um HttpResponse. Em ambos os casos, porém, eles estão gerando exatamente a mesma resposta semanticamente.

Se você estiver usando o MVC 6, obtém os dois em uma classe de controlador unificado que pode ser inteligente sobre o que ele retorna. Não encontrei nenhum bom código de exemplo ASP para este modelo, mas encontrei algum código Rails com o mesmo padrão. Considere este controlador para "curtidas" do projeto da diáspora . Cada método do controlador possui rotas definidas por uma "convenção de recursos" aqui que equivale ao LCRUD em uma API.

Se você ler as implementações, no entanto, cada uma delas poderá responder a HTML, HTML móvel ou JSON. Isso, combinado com uma convenção para encontrar as visualizações, unifica completamente o Web App e a API da Web. Você também observará que nem todos os métodos realmente fornecem cada resposta (o que faz sentido, pois a interface do usuário pode exigir métodos que a API não exige e vice-versa).

Essa é uma incompatibilidade de impedância, porque o ASP.NET meio que descobriu tudo isso tarde, enquanto o Rails adotou a simetria há algum tempo e deixa muito claro.

Especulação:

Seu colega de trabalho provavelmente está certo e errado, dependendo da versão ASP que você está usando. Sob a versão antiga do MVC, a diferença entre a API e o aplicativo provavelmente tornou uma "prática recomendada" criar a API antecipadamente, porque o modelo do ASP.NET realmente não permitia uma boa reutilização de código.

Com o mais recente, faz mais sentido usar o código unificado porque ficou mais fácil reutilizar o código com a classe base do controlador unificado.

Em ambos os casos, porém, o Controller é efetivamente a API.


Esta pergunta foi respondida até a morte, mas não acho que as outras respostas tenham sido tão claras. O "Você não pode evitar a criação de uma API". a resposta foi praticamente imediata e a resposta aceita dançou em torno do mesmo problema; mas ambos não abordaram o ASP especificamente de uma maneira que eu senti que levou o ponto para casa.
Jayson

Quanto mais respostas, melhor, elas ajudam a obter uma apreciação completa do que as outras pessoas sentem sobre isso.
NibblyPig 01/07/2015

2

Quando iniciei minha carreira em 2006, esse tipo de arquitetura estava na moda no mundo .NET. Trabalhei em 3 projetos separados, concebidos em meados dos anos 2000, com um serviço da web entre a camada de lógica de negócios e o front-end da web. É claro que hoje em dia os serviços da web eram SOAP, mas ainda é a mesma arquitetura. Os supostos benefícios foram a capacidade de alternar entre front-end ou back-end e até desenvolver um programa de desktop. Em última análise, YAGNI provou ser verdade. Eu nunca vi nada disso acontecer. Todo esse tempo, vi apenas o custo de dividir o projeto dessa maneira. Até acabei retirando o serviço da web de um dos projetos (demorou meio ano para removê-lo passo a passo enquanto fazia outras coisas) e toda a equipe ficou feliz. Eu nunca tentei essa abordagem desde então e não vou, a menos que seja dada uma razão muito específica. Cinco anos de experiência ao experimentar essa arquitetura me ensinaram que não vou precisar dela e que muitos especialistas me dizem que o contrário vai me convencer de que vou. Somente um projeto em que eu preciso pode fazer isso.

Agora que foi dito, eu tentei desenvolver uma camada entre a lógica de negócios e os controladores / apresentadores. Por exemplo, eu tenho uma camada de serviço, nunca exponho desejos, uso interfaces para todos os meus serviços e os injeto nos controladores com IoC. Se eu precisar de um serviço da web em minha arquitetura, poderei apresentá-lo com um custo razoável. Só não quero pagar esse custo antecipadamente.

Também gosto bastante da idéia de microsserviços, mas meu entendimento é que microsserviços significa módulos verticais, e não camadas horizontais. Por exemplo, se você estiver criando o facebook, o recurso de bate-papo será um serviço separado, implantado separadamente em seus próprios servidores, etc. Esse é o tipo de serviço independente que eu recomendaria.


2

Terceiros vão usá-lo? Sim você deveria .

Você planeja reutilizá-lo em um futuro não tão distante? Sim você deveria.
Você será seu terceiro , ter uma API documentada - ou documentável - ou utilizável por terceiros - fornecerá sólida reutilização e modularidade.

Você está com pressa? Não você não deveria.
A refatoração posterior é mais fácil e rápida do que a maioria das metodologias e professores prevêem e informam. É mais importante ter algo que está funcionando (mesmo com um design interno ruim, como pode e será refatorado) do que não ter absolutamente nada. (mas com um design interno incrível , wohoo)

O front-end pode nunca ver a luz do dia por motivos? Sim você deveria .
Eu adicionei esse motivo porque, bem, isso aconteceu muito comigo.
E pelo menos fiquei com meus componentes para reutilizar e redistribuir etc.


1

Há boas respostas aqui. Eu posto isso como uma resposta parcial; talvez fosse melhor como um comentário. No entanto, manter o mesmo comentário em várias postagens não é bom.

Não se pode afirmar que o YAGNI é um motivo para não criar uma API.

A API é um terminal de teste natural e lógico. Portanto, desde o dia 0, existem dois aplicativos que usam a API: a interface do usuário e o conjunto de testes. Um é destinado a humanos, o outro é destinado a máquinas. Eles são necessariamente diferentes. Testar o comportamento do front-end é uma tarefa muito diferente do teste do comportamento do back-end. Assim, as técnicas, e provavelmente as ferramentas, são completamente diferentes. A API permite que a melhor ferramenta seja usada para o trabalho. Além disso, com a separação oferecida pela API, os testadores de front-end não precisam testar a funcionalidade de back-end.

A API também ajuda a manter os codificadores front-end afastados das preocupações do codificador back-end e vice-versa. Essas são habilidades muito diferentes em nossa empresa; a API nos permite focar onde somos mais fortes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.