Cuando se discuten las API entre sistemas (a nivel empresarial), a menudo hay dos puntos de vista diferentes en nuestro equipo: algunas personas prefieren un enfoque abstracto más, digamos, genérico , otro un enfoque "concreto" directo.
Ejemplo: el diseño de una API simple de "búsqueda de personas". la versión concreta sería
searchPerson(String name, boolean soundEx,
String firstName, boolean soundEx,
String dateOfBirth)
Las personas a favor de la versión concreta dicen:
- la API es autodocumentada
- es facil de entender
- es fácil de validar (compilador o como servicio web: validación de esquema)
- BESO
El otro grupo de personas en nuestro equipo diría "Eso es solo una lista de criterios de búsqueda"
searchPerson(List<SearchCriteria> criteria)
con
SearchCritera {
String parameter,
String value,
Map<String, String> options
}
posiblemente haciendo "parámetro" de algún tipo de enumeración.
Los defensores dicen:
- sin cambiar la (declaración de) API, la implementación puede cambiar, por ejemplo, agregar más criterios o más opciones. Incluso sin sincronizar dicho cambio en el momento del despliegue.
- la documentación es necesaria incluso con la variante concreta
- la validación del esquema está sobrevalorada, a menudo hay que validar más, el esquema no puede manejar todos los casos
- ya tenemos una API similar con otro sistema - reutilice
El contraargumento es
- mucha documentación sobre parámetros válidos y combinaciones válidas de parámetros
- mayor esfuerzo de comunicación porque es más difícil de entender para otros equipos
¿Hay alguna mejor práctica? ¿Literatura?
api
web-services
api-design
erik
fuente
fuente
Respuestas:
Depende de cuántos campos esté hablando y cómo se usen. Es preferible el concreto para consultas altamente estructuradas con solo unos pocos campos, pero si la consulta tiende a ser de forma muy libre, entonces el enfoque concreto rápidamente se vuelve difícil de manejar con más de tres o cuatro campos.
Por otro lado, es muy difícil mantener una API genérica pura. Si realiza una búsqueda simple de nombres en muchos lugares, eventualmente alguien se cansará de repetir las mismas cinco líneas de código y lo envolverá en una función. Dicha API evoluciona invariablemente en un híbrido de una consulta genérica junto con contenedores concretos para las consultas más utilizadas. Y no veo nada malo en eso. Te da lo mejor de ambos mundos.
fuente
Diseñar una buena API es un arte. Se aprecia una buena API incluso después de que pase el tiempo. En mi opinión, no debería haber un sesgo general en la línea abstracta-concreta. Algunos parámetros pueden ser tan concretos como los días de la semana, algunos requieren estar diseñados para la extensibilidad (y es bastante estúpido hacerlos concretos, por ejemplo, parte de los nombres de funciones), y otros pueden ir aún más lejos y tener un estilo elegante. API one necesita proporcionar devoluciones de llamada o incluso un lenguaje específico de dominio ayudará a combatir la complejidad.
Raramente ocurren cosas nuevas bajo la Luna. Eche un vistazo a la técnica anterior, especialmente a los estándares y formatos establecidos (por ejemplo, muchas cosas se pueden modelar después de los feeds, las descripciones de los eventos se elaboraron en ical / vcal). Haga su API fácilmente aditiva, donde las entidades frecuentes y omnipresentes son concretas y las extensiones previstas son diccionarios. También hay algunos patrones bien establecidos para tratar situaciones específicas. Por ejemplo, el manejo de solicitudes HTTP (y similares) puede modelarse en la API con objetos de solicitud y respuesta.
Antes de diseñar API, haga una lluvia de ideas sobre aspectos, incluidos aquellos que no se incluirán, pero que debe tener en cuenta. Ejemplos de tales son lenguaje, dirección de escritura, codificación, localización, información de zona horaria y similares. Preste atención a los lugares donde pueden aparecer múltiples: use la lista, no un valor único para ellos. Por ejemplo, si está diseñando API para el sistema de videochat, su API será mucho más útil, si asume N participantes, no solo dos (aunque sus especificaciones en este momento son tales).
A veces, ser abstracto ayuda a reducir drásticamente la complejidad: incluso si diseña una calculadora para agregar solo 3 + 4, 2 + 2 y 7 + 6, puede ser mucho más sencillo implementar X + Y (con límites técnicamente posibles en X y Y, e incluye ADD (X, Y) a tu API en lugar de ADD_3_4 (), ADD_2_2 (), ...
Con todo, elegir una forma u otra es solo un detalle técnico. Su documentación debe describir casos de uso frecuente de manera concreta.
Independientemente de lo que haga en el lado de la estructura de datos, proporcione un campo para una versión de API.
En resumen, la API debe minimizar la complejidad cuando se trata con su software. Para apreciar la API, el nivel de complejidad expuesta debe ser adecuado. Decidir sobre la forma de la API depende mucho de la estabilidad del dominio del problema. Por lo tanto, debería haber alguna estimación en qué dirección crecerá el software y su API, porque esta información puede afectar la ecuación de complejidad. Además, el diseño de API está ahí para que la gente lo entienda. Si hay buenas tradiciones en el área de tecnología de software en la que se encuentra, trate de no desviarse mucho de ellas, ya que esto ayudará a comprender. Ten en cuenta para quién escribes. Los usuarios más avanzados apreciarán la generalidad y la flexibilidad, mientras que aquellos con menos experiencia pueden sentirse más cómodos con la concreción. Sin embargo, cuide a la mayoría de los usuarios de API allí,
Por el lado de la literatura, puedo recomendar a los principales programadores de "Beautiful Code" que expliquen cómo piensan. Por Andy Oram, Greg Wilson, ya que creo que la belleza se trata de percibir la optimización oculta (y la idoneidad para algún propósito).
fuente
Mi preferencia personal es ser abstracto, sin embargo, las políticas de mi empresa me impiden ser concreto. Ese es el final del debate para mí :)
Has hecho un buen trabajo enumerando los pros y los contras de ambos enfoques, y si sigues cavando encontrarás muchos argumentos a favor de ambos lados. Siempre que la arquitectura de su API se desarrolle correctamente, lo que significa que ha pensado en cómo se usará hoy y cómo puede evolucionar y crecer en el futuro, entonces debería estar bien de cualquier manera.
Aquí hay dos marcadores que tenía con puntos de vista opuestos:
Favoreciendo clases abstractas
Interfaces favorecedoras
Pregúntese: "¿La API cumple con los requisitos de mi negocio? ¿Tengo criterios bien definidos para el éxito? ¿Se puede escalar?". Parecen las mejores prácticas realmente simples a seguir, pero honestamente son mucho más importantes que las concretas frente a las genéricas.
fuente
No diría que una API abstracta es necesariamente más difícil de validar. Si los parámetros de criterios son lo suficientemente simples y tienen pocas dependencias entre sí, no hay mucha diferencia si pasa los parámetros por separado o en una matriz. Aún necesita validarlos a todos. Pero eso depende del diseño de los parámetros de criterios y de los propios objetos.
Si la API es lo suficientemente compleja, tener métodos concretos no es una opción. En algún momento, probablemente terminará con métodos con muchos parámetros o con métodos demasiado simples que no cubrirán todos los casos de uso requeridos. En cuanto a mi experiencia personal en el diseño de una API de consumo, es mejor tener métodos más genéricos en el nivel de API e implementar envoltorios necesarios específicos en el nivel de la aplicación.
fuente
El argumento de cambio debe descartarse con YAGNI. Básicamente, a menos que realmente tenga al menos 3 casos de uso diferentes que usen la API genérica de manera diferente, es muy probable que la diseñe para que no tenga que cambiar cuando aparezca el próximo caso de uso (y cuando tenga el uso- casos, obviamente necesita la interfaz genérica, punto). Así que no intentes y prepárate para el cambio.
El cambio no necesita estar sincronizado para la implementación en ninguno de los casos. Cuando generaliza la interfaz más adelante, siempre puede proporcionar la interfaz más específica para la compatibilidad con versiones anteriores. Pero en la práctica, cualquier implementación tendrá tantos cambios que la sincronizará de todos modos para que no tenga que probar los estados intermedios. Yo tampoco lo vería como argumento.
En cuanto a la documentación, cualquiera de las soluciones puede ser fácil de usar y obvia. Pero es un argumento importante. Implemente la interfaz para que sea fácil de usar en sus casos reales. A veces específico puede ser mejor y a veces genérico puede ser.
fuente
Yo favorecería el enfoque abstracto de la interfaz. Hacer una consulta a ese tipo de servicio (de búsqueda) es un problema común y probablemente ocurrirá nuevamente. Además, es probable que encuentre más candidatos para el servicio que sean adecuados para reutilizar una interfaz más general. Para poder proporcionar una interfaz común coherente para esos servicios, no enumeraría los parámetros de consulta actualmente identificados en la definición de interfaz.
Como se señaló anteriormente, me gusta la oportunidad de cambiar o ampliar la implementación sin modificar la interfaz. Agregar otros criterios de búsqueda no debe reflejarse en la definición del servicio.
Aunque no se trata de diseñar interfaces bien definidas, concisas y expresas, siempre tendrá que proporcionar documentación adicional. Agregar el alcance de definición para criterios de búsqueda válidos no es una carga tan pesada.
fuente
El mejor resumen que he visto es la escala de Rusty, ahora llamada manifiesto de diseño API de Rusty . Solo puedo recomendar fuertemente eso. En aras de la exhaustividad, cito el resumen de la escala desde el primer enlace (el mejor en la parte superior, el peor a continuación):
Buenas API
API malas
Ambas páginas de detalles aquí y aquí vienen con una discusión en profundidad de cada punto. Es realmente una lectura obligada para los diseñadores de API. Gracias Rusty, si alguna vez lees esto.
fuente
En palabras simples:
fuente
Si amplía
SearchCriteria
un poco la idea, puede darle flexibilidad, como crearAND
,OR
etc. criterios. Si necesita dicha funcionalidad, este sería el mejor enfoque.De lo contrario, diseñarlo para la usabilidad. Haga que la API sea fácil para las personas que la usan. Si tiene algunas funciones básicas que se necesitan con frecuencia (como buscar a una persona por su nombre), proporciónelas directamente. Si los usuarios avanzados necesitan búsquedas avanzadas, aún pueden usar el
SearchCriteria
.fuente
¿Qué está haciendo el código detrás de la API? Si es algo flexible, entonces una API flexible es buena. Si el código detrás de la API es muy específico, ponerle una cara flexible significa que los usuarios de la API se sentirán frustrados y molestos por todo lo que la API pretende que es posible, pero que en realidad no se puede lograr.
Para su ejemplo de búsqueda de persona, ¿se requieren los tres campos? Si es así, la lista de criterios es mala porque permite una multitud de usos que simplemente no funcionan. Si no, entonces requerir que el usuario especifique entradas no requeridas es malo. ¿Qué posibilidades hay de que se agregue la búsqueda por dirección en V2? La interfaz flexible hace que sea más fácil de agregar que la inflexible.
No todos los sistemas deben ser ultra flexibles, tratando de hacer que todo lo sea, así es como Arquitectura Astronauta. Un arco flexible dispara flechas. Una espada flexible es tan útil como un pollo de goma.
fuente