Suponga que tiene un gran proyecto respaldado por una base API. El proyecto también incluye una API pública que los usuarios finales (ish) pueden usar.
En ocasiones, debe realizar cambios en la base de API que admite su proyecto. Por ejemplo, debe agregar una función que necesite un cambio de API, un nuevo método o requiera la alteración de uno de los objetos, o el formato de uno de esos objetos, pasado ao desde la API.
Suponiendo que también esté utilizando estos objetos en su API pública, los objetos públicos también cambiarán cada vez que lo haga, lo que es indeseable ya que sus clientes pueden confiar en que los objetos API permanecen idénticos para que su código de análisis funcione. (tos C ++ clientes WSDL ...)
Entonces, una posible solución es versionar la API. Pero cuando decimos "versionar" la API, parece que esto también debe significar versionar los objetos de la API, así como proporcionar llamadas a métodos duplicados para cada firma de método modificada. Entonces tendría un objeto clr viejo y simple para cada versión de mi api, lo que de nuevo parece indeseable. E incluso si hago esto, seguramente no construiré cada objeto desde cero, ya que eso terminaría con grandes cantidades de código duplicado. Más bien, es probable que la API extienda los objetos privados que estamos usando para nuestra API base, pero luego nos encontramos con el mismo problema porque las propiedades agregadas también estarían disponibles en la API pública cuando no se supone que lo estén.
Entonces, ¿qué es la cordura que generalmente se aplica a esta situación? Sé que muchos servicios públicos, como Git para Windows, mantienen una API versionada, pero tengo problemas para imaginar una arquitectura que soporte esto sin grandes cantidades de código duplicado que cubra los diversos métodos versionados y objetos de entrada / salida.
Soy consciente de que procesos como el control de versiones semántico intentan poner algo de cordura cuando se producen interrupciones de API públicas. El problema es más que parece que muchos o la mayoría de los cambios requieren romper la API pública si los objetos no están más separados, pero no veo una buena manera de hacerlo sin duplicar el código.
fuente
I don't see a good way to do that without duplicating code
- Su nueva API siempre puede llamar a métodos en su antigua API, o viceversa.Respuestas:
Al mantener una API utilizada por terceros, es inevitable que deba realizar cambios. El nivel de complejidad dependerá del tipo de cambio que se esté produciendo. Estos son los principales escenarios que surgen:
Nueva funcionalidad que se agrega a la API existente
Este es el escenario más fácil de soportar. Agregar nuevos métodos a una API no debería requerir ningún cambio en los clientes existentes. Es seguro implementarlo para los clientes que necesitan la nueva funcionalidad, ya que no tiene actualizaciones para ningún cliente existente.
Funcionalidad antigua obsoleta de API
En este escenario, debe comunicar a los consumidores existentes de su API que la funcionalidad no será compatible a largo plazo. Hasta que deje de admitir la funcionalidad anterior (o hasta que todos los clientes hayan actualizado a la nueva funcionalidad), debe mantener la funcionalidad de API antigua y nueva al mismo tiempo. Si se trata de una biblioteca que se proporciona, la mayoría de los idiomas tienen una forma de marcar los métodos antiguos como obsoletos / obsoletos. Si se trata de un servicio de terceros de algún tipo, generalmente es mejor tener diferentes puntos finales para la funcionalidad antigua / nueva.
Funcionalidad existente en API cambiando de alguna manera
Este escenario dependerá del tipo de cambio. Si ya no es necesario utilizar los parámetros de entrada, puede actualizar el servicio / biblioteca para ignorar los datos adicionales ahora. En una biblioteca sería hacer que el método sobrecargado llame internamente al nuevo método que requiere menos parámetros. En un servicio alojado, el punto final ignora los datos adicionales y puede dar servicio a ambos tipos de clientes y ejecutar la misma lógica.
Si la funcionalidad existente necesita agregar nuevos elementos requeridos, entonces debe tener dos puntos finales / métodos para su servicio / biblioteca. Hasta que los clientes se actualicen, debe admitir ambas versiones.
otros pensamientos
Rather, the API is likely to extend the private objects we are using for our base API, but then we run into the same problem because added properties would also be available in the public API when they are not supposed to be.
No exponga objetos privados internos a través de su biblioteca / servicio. Cree sus propios tipos y asigne la implementación interna. Esto le permitirá realizar cambios internos y minimizar la cantidad de actualizaciones que los clientes externos deben realizar.
The problem is more that it seems like many or most changes require breaking the public API if the objects aren't more separated, but I don't see a good way to do that without duplicating code.
La API, ya sea un servicio o una biblioteca, debe ser estable en el punto de integración con los clientes. Cuanto más tiempo tome para identificar cuáles deberían ser las entradas y salidas y mantenerlas como entidades separadas, le ahorrará muchos dolores de cabeza en el futuro. Haga que el contrato de API sea su propia entidad separada y asigne las clases que proporcionan el trabajo real. El tiempo ahorrado cuando cambian las implementaciones internas debería más que compensar el tiempo adicional que llevó definir la interfaz adicional.
No vea este paso como "código duplicado". Si bien son similares, son entidades separadas que vale la pena crear. Mientras que los cambios de API externos casi siempre requieren un cambio correspondiente a la implementación interna, los cambios de implementación internos no siempre deberían cambiar la API externa.
Ejemplo
Supongamos que está proporcionando una solución de procesamiento de pagos. Está utilizando PaymentProviderA para realizar transacciones con tarjeta de crédito. Más adelante, obtendrá una mejor tarifa a través del procesador de pagos de PaymentProviderB. Si su API expuso los campos de Tarjeta de crédito / Dirección de factura de su tipo en lugar de la representación de PaymentProviderA, entonces el cambio de API es 0 ya que la interfaz sigue siendo la misma (con suerte, de todos modos, si PaymentProviderB requiere datos que no fueron requeridos por PaymentProviderA, entonces debe elegir admite ambos o mantén la peor tasa con PaymentProviderA).
fuente