¿Deberías escribir tu back-end como una API?

322

Tuve una acalorada discusión hoy sobre nuestra aplicación MVC. Tenemos un sitio web escrito en MVC ( ASP.NET ), y generalmente sigue el patrón de hacer algo en la vista -> presionar el controlador -> el controlador construye un modelo (llama a un administrador que obtiene los datos, construye el modelo en el método del controlador en sí) -> el modelo va a ver -> enjuagar y repetir.

Dijo que nuestro código estaba demasiado ajustado. Por ejemplo, si también quisiéramos una aplicación de escritorio, no podríamos usar nuestro código existente.

La solución y las mejores prácticas, dijo, es crear una API, y luego construir su sitio web sobre su API, y luego construir una aplicación de escritorio, una aplicación móvil, etc. es muy simple.

Esto me parece una mala idea por varias razones.

De todos modos, parece que no puedo encontrar nada buscando en Google que pueda discutir esta práctica. ¿Alguien tiene alguna información sobre pros, contras, por qué debería, por qué no debería o más lecturas?

Algunas razones, creo que es una mala idea:

  • Es demasiado abstracto ejecutar su back-end desde una API. Estás tratando de hacerlo demasiado flexible, lo que lo convertirá en un desastre inmanejable.

  • Todo lo integrado en MVC parece inútil, como roles y autenticación. Por ejemplo, [Autorizar] atributos y seguridad; Tendrás que rodar el tuyo.

  • Todas sus llamadas a la API requerirán información de seguridad adjunta, y tendrá que desarrollar un sistema de token y demás.

  • Tendrá que escribir llamadas API completas para cada función que su programa realice. Casi todos los métodos que desee implementar deberán ejecutarse desde una API. Un Get / Update / Delete para cada usuario, más una variante para cada operación, por ejemplo, actualizar el nombre de usuario, agregar un usuario a un grupo, etc., etc. y cada uno sería una llamada API distinta.

  • Pierdes todo tipo de herramientas como interfaces y clases abstractas cuando se trata de API. Cosas como WCF tienen un soporte muy tenue para las interfaces.

  • Tiene un método que crea un usuario o realiza alguna tarea. Si desea crear 50 usuarios, puede llamarlo 50 veces. Cuando decida hacer este método como API, su servidor web local puede conectar canales con nombre a él y no hay problema: su cliente de escritorio también puede acceder, pero de repente su creación de usuario a granel implicará martillar la API a través de Internet 50 veces, lo cual no es No está bien. Por lo tanto, debe crear un método masivo, pero en realidad solo lo está creando para clientes de escritorio. De esta manera, terminas teniendo que a) modificar tu API en función de lo que se está integrando con ella, y no puedes simplemente integrarte directamente con ella, b) hacer mucho más trabajo para crear una función adicional.

  • YAGNI . A menos que esté planeando específicamente escribir dos aplicaciones que funcionen de manera idéntica, una aplicación web y una aplicación de Windows, por ejemplo, es una gran cantidad de trabajo de desarrollo adicional.

  • La depuración es mucho más difícil cuando no puede avanzar de punta a punta.

  • Muchas operaciones independientes que requerirán muchos cambios de ida y vuelta, por ejemplo, algún código puede obtener el usuario actual, verificar que el usuario esté en el rol de administrador, obtener la compañía a la que pertenece el usuario, obtener una lista de otros miembros, enviarlos a todos un correo electrónico. Eso requeriría muchas llamadas a la API, o escribir un método a medida para la tarea específica que desea, donde el único beneficio de ese método a medida sería la velocidad, pero la desventaja sería que sería inflexible.

  • Probablemente hay algunas razones más por las que están en la parte superior de mi cabeza.

Simplemente me parece que a menos que realmente necesite dos aplicaciones idénticas, entonces realmente no vale la pena. Tampoco he visto una aplicación ASP.NET creada de esta manera, tendría que escribir dos aplicaciones separadas (la API y su código) y la versión también las controlará a ambas (si su página de usuario obtiene un nuevo campo, usted ' tendría que actualizar la API y su código de consumo simultáneamente para asegurar que no haya efectos nocivos o poner mucho trabajo extra para mantenerlo robusto).


Editar: Algunas respuestas geniales, realmente comienzan a tener una buena idea de lo que todo esto significa ahora. Entonces, para ampliar mi pregunta, ¿cómo estructuraría una aplicación MVC para seguir esta estructura API?

Por ejemplo, tiene un sitio web que muestra información sobre un usuario. Bajo MVC, tienes:

Ver: página HTML (CS) que muestra un controlador UserViewModel: llama a GetUser () y crea un UserViewModel que pasa a la clase View Manager (tipo de su API) que tiene un método GetUser.

El controlador hace GetUser () pero también quieres una aplicación de escritorio. Esto significa que su GetUser necesita ser expuesto a través de algún tipo de API. Es posible que desee una conexión TCP, WCF o quizás Remoting. También desea una aplicación móvil que sea RESTful ya que las conexiones persistentes son escamosas.

Entonces, ¿escribiría una API para cada uno, un servicio web WCF que tenga un método GetUser () y el código simplemente lo haga return new UserManager().GetUser()? ¿Y un método mvc 4 web api que hace lo mismo? ¿Mientras continúa llamando a GetUser directamente en su método de controlador MVC?

¿O elegiría la solución que funcionaría para los tres (servicio REST de API web) y construiría todo sobre eso, de modo que las tres aplicaciones hagan llamadas API (las de mvc, a la máquina local)?

¿Y es solo un escenario teórico perfecto? Puedo ver grandes gastos generales en el desarrollo de esta manera, especialmente si tiene que desarrollar de una manera que le permita realizar operaciones de una manera RESTANTE. Creo que algo de esto ha sido cubierto en las respuestas.


Edición 2: después de leer más cosas, he puesto un comentario debajo que creo que podría explicarlo. La pregunta es un poco una pregunta capciosa, creo. Si escribiera su back-end como API, me confundió pensar que debería haber un solo servicio web que todo (aplicación de mvc, aplicación de escritorio, aplicación móvil) llame para hacer cosas.

La conclusión a la que he llegado es que lo que realmente debe hacer es asegurarse de que su capa de lógica de negocios esté correctamente desacoplada. Mirando mi código, ya hago esto: el controlador llamará GetUser()a un administrador, luego creará un modelo de vista para representarlo con una Vista. Entonces, realmente, la capa de lógica de negocios es una API. Sin embargo, si desea llamarlo desde una aplicación de escritorio, deberá escribir algo como un servicio WCF para facilitar la llamada. Incluso tener un método WCF llamado GetUser()que contenga el código return MyBusinessLayer.GetUser()sería suficiente. Por lo tanto, la API es la lógica empresarial, y las API de WCF / web, etc., son solo titbits de código para permitir que las aplicaciones externas lo llamen.

Por lo tanto, hay algunos gastos generales, ya que debe envolver su capa de lógica de negocios en diferentes API dependiendo de lo que necesite, y tendrá que escribir un método de API para cada operación que desee que hagan sus otras aplicaciones, además tendrá que resuelva una forma de autenticación, pero en su mayor parte es lo mismo. Pegue su lógica de negocios en un proyecto separado (biblioteca de clases), ¡y probablemente no tendrá ningún problema!

Esperemos que esta interpretación sea correcta. Gracias por toda la discusión / comentarios que ha generado.

NibblyPig
fuente
25
¿Podrías exponer las razones por las que crees que sería una mala idea? Hoy en día debo admitir que no veo ninguna razón para NO hacerlo. Hace, entre otras ventajas, portar su aplicación a diferentes plataformas mucho más fácil y permite una gran flexibilidad en el front-end sin siquiera tocar su código de back-end ...
Laurent S.
12
@SLC: Cuando dices API, ¿te refieres a una API de servicio web como una interfaz SOAP o REST? Porque debe hacer que el back-end sea una API, pero no debe convertirlo en un servicio web.
JacquesB
77
@IanNewson "una aplicación móvil, por ejemplo, tienden a tener menos funciones". Nunca he escuchado una razón convincente por la cual las aplicaciones móviles deberían ser ciudadanos de segunda clase ... (sin embargo, todo el mundo parece hacerlo de esta manera)
Michael
3
@IanNewson tal vez solo soy yo entonces ... pero siempre me encuentro obstaculizado por no poder hacer una cosa u otra en el móvil hasta el punto en que hago muy poco en el móvil
Michael
11
Dices que YAGNI se aplica, pero mi experiencia ha sido que las aplicaciones obtienen una reescritura de la IU cada dos años, o todos se quejan de que necesitan una. Seguro que sería bueno si no perdiéramos la lógica de nuestro negocio porque ha llegado una nueva tecnología de front-end.
corsiKa

Respuestas:

282

Si deberías.

No solo hace que su back-end sea reutilizable, sino que permite más seguridad y un mejor diseño. Si escribe su backend como parte de un solo sistema, está haciendo un diseño monolítico que nunca es fácil de extender, reemplazar o mejorar.

Un área donde esto es popular en este momento es en Microservicios . Donde el back-end se divide en muchos servicios pequeños (o incluso grandes) que proporcionan una API que el sistema cliente consume. Si imagina usar muchas fuentes de datos de terceros en su aplicación, se da cuenta de que tal vez ya lo esté haciendo.

Otro beneficio es que la construcción y el mantenimiento de cada servicio pueden transferirse a un equipo diferente, pueden agregarle funciones que no afecten a ningún otro equipo que produzca productos. Solo cuando terminan y liberan su servicio, comienzan a agregar funciones a su producto para consumirlas. Esto puede hacer que el desarrollo sea mucho más fluido (aunque en general es potencialmente más lento, tendería a obtener una mejor calidad y ser comprensible)


Editar: OK, veo tu problema. Piensa en la API como una biblioteca remota. No es. Piense en el servicio como más un servicio de suministro de datos. Llama al servicio para obtener datos y luego realiza operaciones en esos datos localmente. Para determinar si un usuario ha iniciado sesión, debe llamar " GetUser" y luego mirar el 'logged on'valor, por ejemplo. ( YMMV con ese ejemplo, por supuesto).

Su ejemplo para la creación masiva de usuarios es simplemente poner excusas: aquí no hay diferencia, lo que haya podido hacer en un sistema monolítico aún se puede hacer en una arquitectura de servicio (por ejemplo, habría pasado una serie de usuarios para crear en masa, o uno solo para crear. Todavía puede hacer exactamente lo mismo con los servicios).

MVC ya se basa en el concepto de servicios aislados, solo los marcos MVC los agrupan en un solo proyecto. Eso no significa que pierdas nada excepto los paquetes de ayuda que te brinda tu framework. Use un marco diferente y tendrá que usar diferentes ayudantes. O, en este caso, rodar el suyo (o agregarlos directamente usando una biblioteca).

La depuración también es fácil: puede probar exhaustivamente la API de forma aislada para que no necesite depurarla (y puede depurar de extremo a extremo, Visual Studio puede adjuntar a varios procesos simultáneamente).

Cosas como trabajo extra implementando seguridad es algo bueno. Actualmente, si incluye todo el código en su sitio web, si un hacker obtiene acceso a él, también tendrá acceso a todo, incluida la base de datos. Si lo divide en una API, el pirata informático puede hacer muy poco con su código a menos que también piratee la capa API, lo que será increíblemente difícil para ellos (¿alguna vez se preguntó cómo los atacantes obtienen grandes listas de todos los usuarios del sitio web o detalles de cc? Es porque piratearon el sistema operativo o el servidor web y tenía una conexión directa a la base de datos donde podían ejecutar " select * from users" con facilidad).

Diré que he visto muchos sitios web (y aplicaciones cliente-servidor) escritos de esta manera. Cuando trabajaba en la industria de servicios financieros, nadie escribiría un sitio web todo en uno, en parte porque es demasiado riesgo para la seguridad, y en parte porque gran parte del desarrollo es bastante GUI sobre el procesamiento de datos de fondo estable (es decir, heredado) sistemas. Es fácil exponer el sistema DP como un sitio web utilizando una arquitectura de estilo de servicio.

2da Edición: Algunos enlaces sobre el tema (para el OP):

Tenga en cuenta que cuando se habla de estos en el contexto de un sitio web, el servidor web debe considerarse la capa de presentación, porque es el cliente el que llama a los otros niveles y también porque construye las vistas de la interfaz de usuario que se envían al navegador para su representación. Es un tema importante, y hay muchas formas de diseñar su aplicación: centrada en los datos o centrada en el dominio (generalmente considero que el centrado en el dominio es "más puro", pero YMMV ), pero todo se reduce a mantener un nivel lógico en el medio su cliente y su base de datos. Es un poco como MVC si considera que el nivel medio, API, es equivalente a su modelo, solo que el modelo no es un simple contenedor para la base de datos, es más rico y puede hacer mucho más (por ejemplo, datos agregados de 2 fuentes de datos, publicación -procesar los datos para que se ajusten a la API, almacenar en caché los datos, etc.):

gbjbaanb
fuente
2
¿Es un sí desde la perspectiva de la arquitectura astronauta? Puedo entender sus párrafos segundo y tercero desde el punto de vista del servicio, pero estamos hablando de GetUser, CreateUser, IsUserLoggedIn y cientos de pequeñas funciones que anteriormente eran líneas únicas de código que se convertían en llamadas API.
NibblyPig
12
Imagina que lo estás escribiendo como un sitio web: todas esas pequeñas funciones no pueden ser tan interactivas como imaginas, por lo que tendrás que obtener los datos y almacenarlos en caché localmente mientras construyes tu página (o pasarlos como datos potencialmente obsoletos al cliente, según corresponda al sistema). Para mucho de esto, debe cambiar su diseño de "reaccionar bajo demanda" a "anticiparse por adelantado", pero la mayoría de su sistema realizará llamadas API. Diseñe su API para que sea menos granular y más centrada en los datos, para que IsUserLoggedOn no tenga que ser una llamada a la API, solo necesita un "GetUserDetails" una vez que luego inspeccione localmente.
gbjbaanb
55
Utilizamos este método en mi último lugar de trabajo y funcionó maravillosamente. Nuestro producto principal era una aplicación web, pero pudimos crear una aplicación de escritorio e incluso hojas de Excel que podían acceder a los mismos servicios web que nuestra aplicación web para todos sus datos, y además exponer los servicios a nuestros clientes para que pudieran programa contra ellos.
Kik
2
Aquí hay otro beneficio: puede exponer la API de back-end a los clientes de su sitio web. En nuestra compañía, hicimos esto, y algunos clientes de grandes compañías de software (después de probar el backend en nuestro host) pagaron para que el backend se envolviera como un producto autohospedado por sí solo. Dependiendo del producto, algunos clientes están menos interesados ​​en las carillas frontend y mucho más interesados ​​en lo que su producto realmente hace : el backend. Ese es otro producto para vender.
Reid
2
Esto también facilita el uso de la misma lógica de un servicio web. Una de esas cosas que nuestros equipos siempre piensan que nunca tendremos que hacer ... También facilita las pruebas unitarias.
ps2goat
87

No puede evitar construir una API . Incluso si crea "solo un sitio web", de todos modos necesitará obtener sus datos de su backend. Independientemente de cómo decida hacer esto, esa es su API de facto .

Sabiendo esto, la verdadera pregunta no es si construir una API, sino cómo construirla . Puede hacerlo sobre la marcha como una cosa ad hoc , y de hecho, muchos sitios web están construidos exactamente de esta manera, o puede diseñarlo cuidadosamente para que se pueda usar en otros contextos. En este contexto, queda bastante claro que su colega tiene razón: primero debe hacer la API y luego construir su sitio sobre ella.

Sin embargo, esto trae consigo algunas preocupaciones, como usted señala. Para abordarlos:

Es demasiado abstracto ejecutar su back-end desde una API. Estás tratando de hacerlo demasiado flexible, lo que lo convertirá en un desastre inmanejable.

Eso depende de cómo lo hagas. Como George Pólya señala en su excelente texto Cómo resolverlo , a menudo "el problema más general puede ser más fácil de resolver". Esto se llama la paradoja del inventor . En el caso de la programación, a menudo funciona mediante la separación de preocupaciones: su backend ya no tiene que preocuparse por el formato de los datos que ingresa y saca, por lo que su código puede ser mucho más simple. Sus analizadores y procesadores de datos ya no tienen que preocuparse por lo que sucede con los datos que crean, por lo que también pueden ser más simples. Todo funciona dividiendo el código en fragmentos más manejables.

Todo lo integrado en MVC parece inútil, como roles y autenticación. Por ejemplo, [Autorizar] atributos y seguridad; Tendrás que rodar el tuyo.

Confieso que me resulta extremadamente difícil simpatizar con las personas que se niegan a aprender sus herramientas. El hecho de que no entiendas su uso no significa que sean inútiles, y ciertamente no significa que debas rodar el tuyo . Todo lo contrario; no debe ir rodando sus propias herramientas hasta que comprenda las alternativas, de modo que pueda estar seguro de abordar los mismos problemas que ellos hacen (incluso si es solo a su manera).

Considere a Linus Torvalds , quien es más famoso por escribir Linux , pero que también escribió git : ahora uno de los sistemas de control de versiones más populares del mundo. Uno de los factores impulsores en su diseño fue una profunda oposición a Subversion (otro VCS extremadamente popular , y posiblemente el más popular en el momento en que se escribió git); resolvió tomar todo lo que Subversion pudiera, y en la medida de lo posible, resolver esos problemas de manera diferente. Para hacer esto, tuvo que convertirse en un experto en Subversion por derecho propio , precisamente para poder comprender los mismos dominios problemáticos y adoptar un enfoque diferente.

O, en el proceso de aprender sus herramientas, es posible que descubra que son útiles como están y no necesitan ser reemplazadas.

Todas sus llamadas a la API requerirán información de seguridad adjunta, y tendrá que desarrollar un sistema de token y demás.

Si. Así es como debería ser.

Tendrá que escribir llamadas API completas para cada función que su programa realice. Casi todos los métodos que desee implementar deberán ejecutarse desde una API. Un Get / Update / Delete para cada usuario, más una variante para cada operación, por ejemplo, actualizar el nombre de usuario, agregar un usuario a un grupo, etc., etc. y cada uno sería una llamada API distinta.

No necesariamente. Aquí es donde entran en juego arquitecturas como REST . Identifica los recursos con los que trabaja su aplicación y las operaciones que tienen sentido aplicar a esos recursos, y luego los implementa sin preocuparse demasiado por los demás .

Pierdes todo tipo de herramientas como interfaces y clases abstractas cuando se trata de API. Cosas como WCF tienen un soporte muy tenue para las interfaces.

Por el contrario, las interfaces se vuelven mucho más importantes cuando usas una API, no menos . Vienen en las representaciones en las que los representa. La mayoría de las personas hoy en día especifican un formato basado en JSON para esto, pero puede usar cualquier formato que desee, siempre que lo especifique bien. Representa el resultado de sus llamadas a este formato en el backend y lo analiza en lo que desee (probablemente el mismo tipo de objeto) en la interfaz. La sobrecarga es pequeña y las ganancias en flexibilidad son enormes.

Tiene un método que crea un usuario o realiza alguna tarea. Si desea crear 50 usuarios, puede llamarlo 50 veces. Cuando decida hacer este método como API, su servidor web local puede conectar canales con nombre a él y no hay problema: su cliente de escritorio también puede acceder, pero de repente su creación de usuario a granel implicará martillar la API a través de Internet 50 veces, lo cual no es No está bien. Por lo tanto, debe crear un método masivo, pero en realidad solo lo está creando para clientes de escritorio. De esta manera, terminas teniendo que a) modificar tu API en función de lo que se está integrando con ella, y no puedes simplemente integrarte directamente con ella, b) hacer mucho más trabajo para crear una función adicional.

Crear una versión masiva de un método existente no es algo que yo llamaría "mucho más trabajo". Si no le preocupan cosas como la atomicidad, el método masivo puede terminar siendo no mucho más que una interfaz muy delgada para el original.

YAGNI . A menos que esté planeando específicamente escribir dos aplicaciones que funcionen de manera idéntica, una aplicación web y una aplicación de Windows, por ejemplo, es una gran cantidad de trabajo de desarrollo adicional.

No, YANI (ya lo necesitas). Esbocé eso como arriba. La única pregunta es cuánto trabajo de diseño ponerle.

La depuración es mucho más difícil cuando no puede avanzar de punta a punta.

¿Por qué no serías capaz de avanzar de punta a punta?

Pero más concretamente, poder examinar los datos de un lado a otro en un formato fácil de reconocer que corta todo el contenido de la pantalla en realidad tiende a hacer que la depuración sea más fácil , no más difícil.

Muchas operaciones independientes que requerirán muchos cambios de ida y vuelta, por ejemplo, algún código puede obtener el usuario actual, verificar que el usuario esté en el rol de administrador, obtener la compañía a la que pertenece el usuario, obtener una lista de otros miembros, enviarlos a todos un correo electrónico. Eso requeriría muchas llamadas a la API, o escribir un método a medida para la tarea específica que desea, donde el único beneficio de ese método a medida sería la velocidad, pero la desventaja sería que sería inflexible.

REST resuelve esto trabajando en objetos completos ( recursos , para usar los términos de la teoría REST), en lugar de las propiedades individuales de los objetos . Para actualizar el nombre de un usuario, OBTENER el objeto de usuario, cambiar su nombre y PONER el usuario de nuevo. Puede hacer otros cambios al mismo tiempo que cambia el nombre de usuario también. El problema más general se vuelve más fácil de resolver, porque puede eliminar todas esas llamadas individuales para actualizar las propiedades individuales de un objeto: simplemente cárguelo y guárdelo.

De alguna manera, esto no es diferente a las arquitecturas RISC en el lado del hardware. Una de las principales diferencias entre RISC y CISC (su predecesor) es que las arquitecturas CISC tienden a incluir muchas instrucciones que operan directamente en la memoria, mientras que las arquitecturas RISC tienden a operar principalmente en registros: en una arquitectura puramente RISC, las únicas operaciones en memoria son CARGAR (copiar algo de la memoria a un registro) y ALMACENAR (tomar un valor de un registro y guardarlo en la memoria).

Se podría pensar que esto significaría hacer muchos más viajes desde registros a la memoria, lo que ralentizaría la máquina. Pero en la práctica, a menudo sucede lo contrario: el procesador (cliente) hace más trabajo entre los viajes a la memoria (servidor) , y de ahí proviene la aceleración.

Larga historia corta: su colega tiene razón. Este es el camino a seguir. A cambio de un poco de trabajo inicial, simplificará drásticamente el código de su sitio web y permitirá una mejor integración con otros sitios web y aplicaciones. Ese es un precio que vale la pena pagar.

Otras lecturas:

  1. Diseño de API REST - Modelado de recursos
El más cuchara
fuente
77
Incluso estos tienen API de facto de algún tipo. Tienden a hacer que muchos otros desarrolladores palidezcan de horror, pero son API de todos modos; simplemente no muy bien diseñados.
The Spooniest
77
Eso lo convierte en una API realmente pobre: ​​tan pobre que muchas personas ni siquiera piensan en ella como una API. Pero aún define la forma en que la interfaz interactúa con el backend, por muy cruda que sea. Pensar en esto como una API ayuda a llevar a casa la importancia de hacerlo bien.
The Spooniest
1
Creo que Linus hizo git porque la comunidad Linux se rebeló contra el uso del DVCS Bitkeeper comercial que se usaba para el núcleo.
gbjbaanb
2
Tu primera oración disipa toda mi confusión. Asociaba el término API con un servicio web y esa es la razón principal por la que estaba tan confundido.
NibblyPig
44
@IanNewson: hay una manera de interactuar con el código, se llama http. Puede tener muchos requisitos irrelevantes y devolver muchos datos irrelevantes, pero eso es lo que lo convierte en una API pésima.
jmoreno
63

Sé que los microservicios están de moda en este momento, pero no siempre valen la pena. Sí, el objetivo es un código débilmente acoplado. Pero no debería venir a expensas de un ciclo de desarrollo más doloroso.

Un buen término medio sería crear un proyecto de datos separado en su solución. El proyecto de datos sería una biblioteca de clases .NET. Su proyecto ASP.NET MVC agregaría una referencia a la biblioteca de datos y todos los modelos se extraerían del proyecto de datos. Luego, cuando llegó el momento de crear una aplicación de escritorio o móvil, puede hacer referencia al mismo código. Por lo tanto, puede que no sea una API oficial, pero funcionará como una. Si desea que sea accesible como una API, puede crear un proyecto web simple que actúe como un contenedor en el proyecto de datos.

Microsoft ha estado promoviendo este concepto, al que llaman Bibliotecas de clases portátiles .

fotijr
fuente
13
He tenido que mantener un proyecto donde la lógica se puso en la capa de la interfaz de usuario, llamando a las mismas estructuras de datos compartidos. He tenido que corregir un error treinta veces debido a eso ("si necesitamos usar la misma lógica nuevamente, copiaremos y pegaremos! No es necesario un API"). Había estado allí una capa lógica (ahora la hay) habría sido suficiente con solo una solución.
SJuan76
1
Esta respuesta, además de incluir esa biblioteca en su propio paquete NuGet y alojar su propio servidor / feed de paquetes NuGet, también es una buena manera de hacerlo. No necesita preocuparse por las redes delicadas y puede hacer que todas las llamadas sean locales a un subproceso (y, por lo tanto, más rápido), además de introducir la versión adecuada a su clase lib con NuGet brinda flexibilidad a otros equipos cuando se actualizan.
Greg Burghardt
34

No, no deberías . Si no tiene planes inmediatos para crear frontends alternativos (como aplicaciones móviles o de escritorio o aplicaciones web separadas) que accedan al mismo backend, entonces no debe introducir una capa de servicio web. YAGNI .

El acoplamiento flojo siempre es deseable (junto con una alta cohesión), pero es un principio de diseño y no significa que tenga que separar físicamente los objetos en diferentes servidores. Y una API de servicio mal diseñada puede crear un acoplamiento estrecho a través de los límites del servidor, por lo que tener una API no garantiza un acoplamiento flojo.

Si surge la necesidad de un API de servicio en el futuro, siempre puede presentarlo en ese punto. Siempre y cuando mantenga su código en capas (el acceso a los datos y la lógica de negocios separados de forma lógica de la interfaz de usuario), no será más difícil de introducir más tarde de lo que es ahora. Y el diseño resultante será mucho mejor cuando se diseñe para cumplir con los requisitos reales.


Tenga en cuenta que supongo que la pregunta es si debe crear una API de servicio web o no. La pregunta solo dice API, pero API también puede significar la interfaz de una biblioteca, y luego, por supuesto, cada capa tendrá una API por definición. La conclusión es que la lógica de su negocio y las capas de acceso a datos deben estar separadas limpiamente de la lógica de la interfaz de usuario en el nivel de diseño, pero no debe introducir una capa de servicio web si no la necesita.

JacquesB
fuente
8
Todo mal diseñado no es bueno. Construir una API no es más tiempo y está más preparado para el futuro. La capacidad de adaptarse al cambio es vital hoy en día, es mejor construir una base sólida para satisfacer cualquier necesidad que ni siquiera conozca, pero que podría llegar antes de lo que piensa ...
Laurent S.
99
@Bartdude: la introducción de una complejidad innecesaria en aras de "preparar el futuro" para un futuro que no llegará es simplemente desperdiciar recursos.
JacquesB
66
@Bartdude agregar una API es definitivamente más tiempo. No tengo idea de cómo crees que puedes reclamar lo contrario.
Ian Newson
13
API "no deberías introducir una capa de servicio web" = servicio web. Si tiene su lógica empresarial detrás de una API, puede exponer esa API como un servicio web en algún momento. Sin embargo, no es un requisito inicial.
Celos
2
@JacquesB: ... así que de hecho no desarrollas características si no estás seguro de que lo vas a necesitar. Eso es lo que entiendo de YAGNI. Sin embargo, la arquitectura no es una característica y las malas elecciones arquitectónicas pueden (y muy probablemente lo harán) conducir a un fracaso miserable. Una vez más, supongo que esta discusión puede incluso ocurrir, lo que a veces no es el caso si fuera por razones de presupuesto, tiempo de comercialización, recursos o falta de conocimiento ... Creo que podemos estar totalmente de acuerdo en no estar de acuerdo en esto, aunque Entiendo su punto de vista, ya que a menudo tuve la misma discusión conmigo mismo ^ _ ^
Laurent S.
29

Mi empresa tiene una aplicación construida así. Inicialmente, se nos encargó construir un back-end con API para un front-end que otro desarrollador estaba creando. Cuando el otro desarrollador no pudo desarrollar ese front-end, también nos encargaron construir el front-end. Si bien definitivamente hay beneficios para este enfoque, hay una gran desventaja: el costo. La construcción inicial será significativamente más costosa, y el mantenimiento continuo será más costoso, debido a más código para mantener y tener dos sistemas separados también implementados. Debido al costo adicional, esto siempre debe ser una decisión comercial, no tomada por capricho por los desarrolladores.

Para poner una cifra, estimaría que el proyecto que menciono arriba costó un 20% más debido a este enfoque. No describe en qué tipo de proyecto está trabajando en qué tipo de empresa trabaja, pero si es un emprendedor que construye su producto, ese costo adicional podría ser la diferencia entre enviar algunas características adicionales que hacen que su producto un éxito.

Otra razón para no hacerlo, al menos no universalmente, es que si decide crear esa segunda interfaz, o rara vez, existe una asignación de funcionalidad uno a uno. Si crea una aplicación móvil, por ejemplo, tienden a tener menos funciones. Esto significa que algunos de sus métodos API nunca se reutilizarán. Por lo tanto, un compromiso con su colega podría ser decidir entre usted las llamadas más cruciales / críticas y agregarlas a una API, y usar métodos más tradicionales para todo lo demás.

Otro punto a considerar es que su colega está diciendo que no podrá reutilizar su código existente, lo cual no es cierto si tiene alguna separación de su lógica comercial. Simplemente necesita crear un envoltorio de servicio web delgado alrededor de sus API internas, lo cual no es una tarea particularmente grande. Sería ingenuo pensar que de todos modos podría reutilizar una capa de servicio web para otro front-end sin ningún cambio.

Ian Newson
fuente
22

Depende del tipo de aplicación y del tipo de mercado en el que se encuentre.

Hay compensaciones y beneficios para ir de esta manera. No es una respuesta clara que una forma es mejor que la otra.

Hablaré por experiencia personal. Fui yo quien decidió tomar la base de código en la que trabajo en esta dirección en 2007. Esa base de código está en algún lugar del orden de un millón de líneas de código ahora, la mitad de las cuales es código de servidor oculto detrás de una gran cantidad de servicio web. API, la otra mitad es una flotilla de clientes, escritorio nativo, escritorio web, móvil, integraciones de back-end, etc. Esta decisión no estuvo exenta de inconvenientes, pero con una perspectiva 20/20, puedo decir que lo volvería a hacer. . Permítanme indicar algunas de las compensaciones involucradas.

Beneficios

  • Flexibilidad. Ya sea una solicitud para crear una aplicación móvil para aumentar la experiencia de escritorio o una solicitud para integrarse con el back-end de SAP, todo se vuelve más fácil cuando ya tiene una API para llamar. Cuando tenga suficientes solicitudes, evolucionará orgánicamente hacia una API, y la única pregunta es si tiene un servicio web estándar o si es una API interna donde los servicios web están hechos a medida.

  • Escalabilidad (del equipo). En nuestro caso, tenemos muchos grupos diferentes de desarrolladores, todos construyendo sobre esta API. Incluso tenemos equipos dedicados de API, que hablan con los diferentes grupos, resumen las necesidades y construyen una API para todo uso. Llegó al punto en que ya ni siquiera nos dicen que las personas están construyendo cosas sobre la API, y no todos los que lo hacen trabajan para nuestra empresa.

  • Seguridad. Tener una división clara entre las partes inseguras y seguras de su base de código es útil para razonar la seguridad. Mezclar la interfaz de usuario y el código de fondo juntos tiende a confundir las cosas.

Compensaciones

  • Flexibilidad. Tienes que hacer el trabajo para construir "correctamente" algo en la API. No es posible ejecutar rápidamente una consulta de base de datos desde el código de la interfaz de usuario para resolver un problema específico. Además, las API que son realmente reutilizables deben tener en cuenta tantos casos de uso que la solución rápida suele ser la solución incorrecta. La API se vuelve menos flexible para evolucionar, especialmente porque ya hay mucho código de cliente (estamos haciendo la transición a una API versionada por esa razón).

  • Velocidad de desarrollo inicial. Es más lento desarrollar API primero, sin ninguna duda. Solo lo recuperas cuando tienes suficientes clientes construidos sobre la API. Pero luego descubres que necesitas 3 implementaciones de clientes diferentes antes de que tu API haya evolucionado para ser lo suficientemente genérica. Descubrimos que la mayoría de nuestros diseños iniciales de API estaban equivocados y hemos tenido que revisar enérgicamente nuestras pautas sobre cómo crear servicios web.

pistas falsas

Mencionaste un montón de estos. En realidad no importan en la práctica.

  • Abstracción. Su API se vuelve lo suficientemente abstracta como para cubrir todos los casos de uso que su producto necesita para servir, y no más que eso. Incluso sin servicios web, tendrá una API interna que hace esto o tendrá un montón de código duplicado. Prefiero la abstracción sobre la duplicación.

  • Abandonando la pila MVC del lado del servidor. En la actualidad, casi todos los sistemas necesitarán una aplicación móvil después de un tiempo. Cuando construya servicios web para satisfacer esa aplicación móvil, tendrá que descubrir cómo hacer la autenticación y la autorización en un contexto API de todos modos. En realidad, es menos trabajo cuando solo tienes una forma de hacerlo, la forma en que lo haces en tus servicios web.

  • Operaciones masivas. Por lo general, se resuelve creando una API masiva que inicia un trabajo de fondo y devuelve una identificación de trabajo para consultas de estado. No es tan grande de un acuerdo.

  • Depuración Descubrí que, en general, se volvió un poco más fácil solucionar problemas del sistema. Todavía puede establecer puntos de interrupción tanto en el código de front-end como en el de back-end, por lo que en la práctica no es tan difícil avanzar, y obtiene la capacidad de crear pruebas de API automatizadas e instrumentar la API para monitorear los sistemas de producción.

  • Muchas operaciones independientes. Eso es cuestión de cómo diseñas las cosas. Si insiste en tener una API CRUD pura, entonces sí, sufrirá este problema. Pero tener algunas API de CQRS para aumentar es una buena idea, y si se ha asegurado de tener una API interna para la cual los servicios son un front-end, puede reutilizar fácilmente esa API interna para construir servicios para esos específicos Escenario.

En resumen

En un sistema que se utiliza en suficientes contextos diferentes, una API evolucionará naturalmente, ya que es la forma más fácil de satisfacer todas las necesidades. Pero definitivamente hay un caso de YAGNI pasando. Hay compensaciones y no tiene sentido hasta que tenga sentido. El punto clave es no ser dogmático y mantener una mente abierta hacia diferentes enfoques en arquitectura para satisfacer las necesidades cambiantes del producto.

Joeri Sebrechts
fuente
Lectura interesante, ¿puedes explicar qué hiciste mal al diseñar la API y qué aprendiste?
aaaaaaaaaaaa
3
Los tres errores principales fueron: (1) sobreajustar la API a las necesidades de la interfaz de usuario primaria, (2) desarrollar el estado en múltiples solicitudes usando sesiones (gradualmente nos estamos quedando sin sesiones) y (3) solo apoyar el uso de db id como identificador donde un código configurable por el usuario es a menudo un mejor identificador (para integraciones con sistemas externos, por lo general, quieren cargar identificadores en nuestro sistema para su uso posterior en API, en lugar de viceversa). Esos tres junto con una documentación débil y mensajes de error inútiles hicieron que la API fuera imposible de usar sin ayuda.
Joeri Sebrechts
10

Lo que describe su colega es una arquitectura orientada al servicio. Esta puede ser una forma tremendamente escalable, comprobable y sensata de codificar, pero realmente depende de lo que esté haciendo.

Hay algunos beneficios significativos para SOA, que intentaré enumerar:

Escalabilidad

Dado que su back-end está desacoplado, su front-end se convierte en solo una serie de plantillas, incluso archivos planos. Los archivos planos son tremendamente rápidos y baratos para servir desde cualquier CDN. Pueden minimizarse y precompilarse en HTML estático, y luego rellenarse con datos del lado del cliente.

Su API tiene que permanecer consistente, pero se puede cambiar por una tecnología más rápida sin romper su pila si supera su tecnología existente. Podría rehacerlo en Go, por ejemplo. Podría reconstruirlo poco a poco y distribuir la carga entre los servidores. Mientras la interfaz permanezca igual, la tecnología se abstrae.

Testabilidad

El MVC generalmente comienza limpio, pero en la práctica los controladores rara vez se concentran en un solo recurso. Cuantas más cosas hagan sus métodos de controlador, menos comprobables se volverán.

Una API evita este problema. Cada llamada API extrae un recurso y lo sirve. Limpio y comprobable.

Separación garantizada de preocupaciones

Su front-end y back-end están completamente divorciados. Puede dar el front-end a otro desarrollador o diseñador. Esto es MVC llevado a otro nivel. Estoy seguro de que no querrás renunciar a MVC. SOA es MVC pero más.

Desventajas

Por supuesto, hay algunos inconvenientes. Un monolito es a menudo más rápido para comenzar. Puede ser a lo que estás acostumbrado. Puede encajar mejor en su pila. Sus herramientas pueden estar optimizadas para la creación de monolitos.

En mi opinión, ninguno de estos son motivos particularmente buenos, y es posible que desee considerar la posibilidad de actualizarlos si se aplican a usted.

superluminario
fuente
Esta es la respuesta más clara hasta ahora.
Tony Ennis
7

Aquí hay muchas buenas respuestas, así que solo agregaré mi experiencia de implementación.

Así es como hago las cosas:

  • Cree una capa de acceso a la base de datos que maneje toda / solo la interacción de la base de datos (generalmente se usa SQL manual para velocidad y control, sin ORM) . Insertar, Actualizar, Eliminar, Seleccionar ...
  • Cree un interface( virtual class) que exponga / aplique las funciones API que necesito. Cuando se implementen, utilizarán las funciones DBAL altamente especializadas para lograr los resultados. También me ayuda a aplicar la API a nivel de compilador, así que me aseguro de que la implementación de la API de Server + tenga todas las funciones integradas.
  • Cree una segunda capa que implemente la interfaz (esta es la API real) y aplique restricciones de seguridad. También interactúa con API externas aquí.
  • El sitio web utilizará la segunda capa directamente (para el rendimiento) sin pasar por una API de acceso remoto (como SOAP, JSON) .
  • Se construye un servidor independiente que implementa la interfaz y expone la segunda capa como una API real de acceso remoto a clientes externos de escritorio / móviles (acceso no al sitio web) . Todo lo que hace es decodificar solicitudes y codificar respuestas y administrar / desconectar clientes. También admite capacidades de retroceso para notificar a los clientes en masa de eventos generados por otros pares conectados (funcionalidad que un sitio web generalmente no requiere) .

Entonces, técnicamente, la API es la segunda capa. Lo usa directamente con el sitio web y lo expone a clientes remotos a través de un servidor. El código se reutiliza y no hay ningún fragmento de código reutilizable en línea. (vive y muere según esta regla y todo es increíble) Ayuda con el mantenimiento, las pruebas ... todo.

Nunca conecte el sitio web al servidor API de escritorio / móvil (a menos que su sitio sea AJAX y se ejecute en JSON) . Pero si el sitio presenta contenido dinámico en el marcado, pasar por una API intermedia disparará su rendimiento. ¡El sitio web debe ser rápido! El acceso remoto al cliente puede ser un poco más lento.

PD : Sí, el mantenimiento es un poco más complejo ya que más ruedas trabajan juntas, pero a la larga es más fácil. Entonces, si su proyecto está destinado a vivir por un tiempo y es un poco complejo, siempre tenga una API. También es mucho más fácil probar cada capa por sí solo.

CodeAngry
fuente
Eso suena bastante genial y tiene mucho sentido, especialmente poner una interfaz en las funciones de tipo API. ¡Probaré esta configuración la próxima vez que cree un proyecto!
NibblyPig
6

El punto de disputa no es si debe usar una API, sino qué es realmente una "API". La única alternativa para usar una API diseñada es usar una API que es un desorden de código aleatorio. Usted escribe que una API hace que las cosas sean "demasiado flexibles", lo que a su vez hace que las cosas sean inmanejables. Esto apunta a un malentendido completo y completo de lo que es una API. Si ese malentendido no se comparte entre usted y su compañero de trabajo, entonces perdió mucho tiempo discutiendo sobre cosas completamente diferentes.

Al no utilizar una API bien definida, puede hacer lo que quiera. Por definición, esta es la opción más flexible. Además, por definición, "haz lo que quieras" sigue siendo una API. El único trabajo de una API es eliminar la flexibilidad. Al eliminar la flexibilidad, una buena API alienta al usuario a hacer cosas similares de manera similar.

Por supuesto, una API incorrecta puede proporcionar demasiada o muy poca flexibilidad, o incluso ambas al mismo tiempo. Una API realmente mal diseñada puede matar un proyecto incluso más rápido que el enfoque "todo vale". Sin embargo, la mejor práctica es simplemente tener programadores competentes que desarrollen y desarrollen la API junto con su aplicación.

Ejemplo

• Muchas operaciones independientes que requerirán muchos cambios de ida y vuelta, por ejemplo, algunos códigos pueden obtener el usuario actual, verificar que el usuario esté en el rol de administrador, obtener la compañía a la que pertenece el usuario, obtener una lista de otros miembros, enviarlos Todo un correo electrónico. Eso requeriría muchas llamadas a la API, o escribir un método a medida para la tarea específica que desea, donde el único beneficio de ese método a medida sería la velocidad, pero la desventaja sería que sería inflexible.

El número de llamadas a la API que esto requeriría en una API decente probablemente sería 1. Sí, es inflexible, pero ¿por qué querría que fuera flexible?

Peter
fuente
4

Dijo que nuestro código estaba demasiado ajustado. Por ejemplo, si también quisiéramos una aplicación de escritorio, no podríamos usar nuestro código existente.

Bueno Si no, entonces esa es una declaración bastante irrelevante.

Yo diría que si fuera a crear una nueva aplicación en 2015, considere seriamente algo con una interfaz de usuario que implique una API y no páginas HTML generadas por el servidor. Hay costos claros pero también beneficios claros.

Pero si tiene un sitio existente sin planes concretos para tener varias interfaces diferentes (por lo que puedo decir), entonces sus comentarios son irrelevantes.

RemcoGerlich
fuente
4

Versión corta: su controlador es efectivamente una API sin importar qué; aunque ASP.NET puede estar ocultando eso.

Versión más larga:

Piense en una aplicación web MVC básica que proporcione información sobre cerveza y, opcionalmente, le venda una. ¿Cómo son las rutas?

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

En una aplicación web normal, es probable que haya algunas rutas auxiliares como:

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

En una API web, no son necesarios, ya que se infieren del método HTTP.

Dada la simetría anterior, creo que este es un caso bastante convincente de que su API y controlador están tan cerca que podrían ser lo mismo.

Después de investigar un poco, he determinado que este podría ser el estado de las cosas dependiendo de la versión de ASP.NET que esté utilizando. El MVC 5 anterior y anterior carece de las convenciones y la interfaz para unificar sólidamente las dos implementaciones. En versiones anteriores, los retornos de aplicaciones web llenan una vista, mientras que la API proporciona una respuesta Http. En cualquier caso, sin embargo, están generando exactamente la misma respuesta semánticamente.

Si está utilizando MVC 6, obtiene ambos en una clase de controlador unificado que puede ser inteligente sobre lo que devuelve. No he encontrado ningún buen código de ejemplo ASP para este modelo, pero he encontrado algunos códigos Rails con el mismo patrón. Considere este controlador para "me gusta" del proyecto Diáspora . Cada método de controlador tiene rutas definidas por una "convención ingeniosa" aquí que equivale a la LCRUD en una API.

Sin embargo, si lee las implementaciones, cada una posiblemente puede responder a HTML, HTML móvil o JSON. Esto, combinado con una convención para encontrar las vistas, unifica completamente la aplicación web y la API web. También notará que no todos los métodos realmente proporcionan cada respuesta (lo cual tiene sentido, ya que la interfaz de usuario puede requerir métodos que la API no requerirá, y viceversa).

Esta es una falta de coincidencia de impedancia porque ASP.NET resolvió todo esto tarde, mientras que Rails ha abrazado la simetría durante algún tiempo y lo deja muy claro.

Especulación:

Probablemente su compañero de trabajo tenga razón o no, según la versión de ASP que esté utilizando. Bajo la versión anterior de MVC, la diferencia entre la API y la aplicación probablemente lo convirtió en una "mejor práctica" para construir la API por adelantado porque el modelo de ASP.NET realmente no permitía una buena reutilización del código allí.

Con el más nuevo, tiene más sentido usar código unificado porque se ha vuelto más fácil reutilizar código con la clase base de controlador unificado.

En cualquier caso, sin embargo, el controlador es efectivamente la API.

Jayson
fuente
Esta pregunta ha sido respondida a muerte, pero no creo que las otras respuestas hayan sido tan claras. El "No puede evitar construir una API". la respuesta fue bastante acertada, y la respuesta aceptada bailó en torno al mismo problema; pero ambos no se dirigieron a ASP específicamente de una manera que sentí condujo a casa.
Jayson
Más respuestas, mejor, ayudan a obtener una apreciación completa de lo que otras personas sienten sobre esto.
NibblyPig
2

Cuando comencé mi carrera en 2006, este tipo de arquitectura estaba de moda en el mundo .NET. Trabajé en 3 proyectos separados concebidos a mediados de la década de 2000 con un servicio web entre la capa de lógica de negocios y la interfaz web. Por supuesto, en estos días los servicios web eran SOAP, pero sigue siendo la misma arquitectura. Los supuestos beneficios fueron la capacidad de cambiar el frente o el backend e incluso desarrollar un programa de escritorio. Finalmente, YAGNI demostró ser verdad. Nunca vi que algo de esto sucediera. Todo este tiempo solo vi el costo de dividir el proyecto de esta manera. Incluso terminé arrancando el servicio web de uno de los proyectos (me llevó medio año eliminarlo paso a paso mientras hacía otras cosas) y todo el equipo estaba feliz. Nunca intenté ese enfoque desde entonces y no lo haré a menos que se me dé una razón muy específica. 5 años de experiencia probando esta arquitectura me enseñaron que no la voy a necesitar y que ningún experto que me diga lo contrario me convencerá de que lo haré. Solo un proyecto donde lo necesito puede hacer eso.

Ahora que lo digo, me esfuerzo por desarrollar una capa entre la lógica empresarial y los controladores / presentadores. Por ejemplo, tengo una capa de servicio, nunca expongo las consultas, uso interfaces para todos mis servicios y las inyecto en controladores con IoC. Si alguna vez necesito un servicio web en mi arquitectura, podré presentarlo con un costo razonable. Simplemente no quiero pagar este costo por adelantado.

También me gusta bastante la idea de microservicios, pero entiendo que microservicios significa módulos verticales en lugar de capas horizontales. Por ejemplo, si está creando Facebook, la función de chat será un servicio separado implementado por separado en sus propios servidores, etc. Este es el tipo de servicios independientes que recomendaría.

Stilgar
fuente
2

¿Terceros lo usarán? Sí, debería hacerlo .

¿Planea reutilizarlo en un futuro no muy lejano? Si deberías.
Usted será su tercero , tener una API documentada (o documentable) o utilizable por terceros le proporcionará una sólida capacidad de reutilización y modularidad.

¿Tienes prisa? No, no deberías.
Refactorizar después es más fácil y rápido de lo que la mayoría de las metodologías y los maestros predicen y cuentan. Es más importante tener algo que funcione (incluso con un mal diseño interno, ya que puede y será refactorizado) que no tener nada en absoluto. (pero con un diseño interno increíble , wohoo)

¿El front-end nunca podría ver la luz del día por razones? Sí, debería hacerlo .
Agregué esta razón porque, bueno, esto me pasó mucho.
Y al menos me quedan mis componentes para reutilizar y redistribuir, etc.

ZJR
fuente
1

Hay buenas respuestas aquí. Publico esto como una respuesta parcial; tal vez sería mejor como comentario. Sin embargo, pegar el mismo comentario en numerosas publicaciones no es bueno.

No se puede reclamar que YAGNI es una razón para no crear una API.

La API es un punto final de prueba natural y lógico. Por lo tanto, desde el día 0, hay dos aplicaciones que usan la API: la interfaz de usuario y el conjunto de pruebas. Uno está destinado a humanos, el otro a máquinas. Son necesariamente diferentes. Probar el comportamiento de front-end es una tarea muy diferente a probar el comportamiento de back-end. Por lo tanto, las técnicas, y probablemente las herramientas, son completamente diferentes. La API permite utilizar la mejor herramienta para el trabajo. Además, con la separación que ofrece la API, los probadores de front-end no tienen que probar la funcionalidad de back-end.

La API también ayuda a mantener a los codificadores front-end fuera de las preocupaciones del codificador back-end, y viceversa. Estas son habilidades muy diferentes en nuestra empresa; La API nos permite centrarnos donde somos más fuertes.

Tony Ennis
fuente