¿Cómo gestiona OpenAPI el polimorfismo? #
El polimorfismo es un concepto omnipresente en la ingeniería y el diseño de software, que permite que las interfaces y las clases adopten múltiples formas. Esto es fundamental para diseñar sistemas flexibles y fáciles de mantener. OpenAPI, una potente especificación para crear API, incluye un soporte sólido para el polimorfismo con el fin de lograr estas cualidades en el diseño de API. Este artículo analiza cómo gestiona OpenAPI el polimorfismo y aborda conceptos clave, ejemplos y buenas prácticas.
Comprender el polimorfismo #
Antes de explorar cómo gestiona OpenAPI el polimorfismo, es fundamental entender el concepto en sí. El polimorfismo permite utilizar una misma interfaz para una clase general de acciones, y la acción concreta se determina según la naturaleza exacta de la situación.
En el contexto de las API, el polimorfismo puede permitir que un endpoint de una API devuelva distintos tipos de objetos según la entrada u otras condiciones. Por ejemplo, un endpoint animal podría devolver datos de un objeto dog, cat o bird, cada uno con atributos diferentes.
Polimorfismo en OpenAPI #
OpenAPI admite el polimorfismo principalmente mediante las palabras clave oneOf, anyOf, allOf y discriminator. Estas palabras clave permiten a quienes diseñan API especificar esquemas que pueden representar varios tipos. Veamos cada una de ellas:
oneOf
#
La palabra clave oneOf permite especificar que la carga útil debe ser válida frente a exactamente uno de los esquemas indicados. Es útil cuando se espera un único tipo entre varios posibles.
Ejemplo #
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
name:
type: string
petType:
type: string
required:
- id
- name
- petType
Dog:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
bark:
type: boolean
required:
- bark
Cat:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
hunts:
type: boolean
required:
- hunts
Bird:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
wingspan:
type: integer
required:
- wingspan
NewPet:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Bird'
En este ejemplo, un NewPet debe ajustarse exactamente a uno de los esquemas Dog, Cat o Bird.
anyOf
#
La palabra clave anyOf permite especificar que la carga útil debe ser válida frente a cualquiera (uno o más) de los esquemas indicados. Es algo menos restrictiva que oneOf.
Ejemplo #
components:
schemas:
MixedPet:
anyOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Bird'
En este ejemplo, un MixedPet puede ajustarse a cualquiera (posiblemente a más de uno) de los esquemas Dog, Cat o Bird.
allOf
#
La palabra clave allOf se utiliza para indicar que la carga útil debe ser válida frente a todos los esquemas indicados. Es útil para componer esquemas a partir de varias partes.
Ejemplo #
components:
schemas:
BasePet:
type: object
properties:
id:
type: integer
name:
type: string
required:
- id
- name
Dog:
allOf:
- $ref: '#/components/schemas/BasePet'
- type: object
properties:
bark:
type: boolean
required:
- bark
En este ejemplo, un Dog debe ajustarse tanto a BasePet como a las propiedades adicionales específicas de Dog.
discriminator
#
La palabra clave discriminator ayuda a determinar qué esquema debe usarse cuando hay varios posibles. Es especialmente útil al implementar el polimorfismo con oneOf.
Ejemplo #
components:
schemas:
Pet:
type: object
required:
- petType
properties:
petType:
type: string
discriminator:
propertyName: petType
Dog:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
bark:
type: boolean
required:
- bark
Cat:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
hunts:
type: boolean
required:
- hunts
Bird:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
wingspan:
type: integer
required:
- wingspan
NewPet:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Bird'
discriminator:
propertyName: petType
En este ejemplo, el campo discriminator petType ayuda a determinar qué esquema usar al validar el NewPet.
Buenas prácticas para el polimorfismo en OpenAPI #
Usa discriminator con oneOf para mayor claridad
#
Al usar oneOf, especificar un discriminator puede mejorar notablemente la legibilidad y la facilidad de uso de tu API. Deja claro qué subtipo se espera y mejora la generación de clientes.
Ten cuidado con anyOf
#
Usar anyOf puede hacer que tu API sea menos predecible, ya que permite que sean válidos varios tipos. Asegúrate de que sea el comportamiento deseado antes de optar por anyOf.
Valida de forma exhaustiva #
La validación y las pruebas exhaustivas son fundamentales al implementar el polimorfismo en tu API. Las pruebas automatizadas pueden garantizar que tu API gestione correctamente todos los tipos posibles.
Documentación #
Documenta con claridad el comportamiento de los endpoints polimórficos. Herramientas como Swagger UI pueden generar automáticamente documentación útil para tu especificación OpenAPI.
Conclusión #
El polimorfismo es una característica sólida de OpenAPI que aporta flexibilidad y robustez al diseño de API. Al aprovechar oneOf, anyOf, allOf y discriminator, quienes diseñan API pueden construir sistemas flexibles que admitan distintos tipos de datos sin perder una documentación clara y concisa.
Para obtener información más detallada, la documentación oficial de OpenAPI es un excelente recurso, al igual que sus herramientas, como Swagger Editor, para probar y validar tus especificaciones OpenAPI.
Si sigues las buenas prácticas y comprendes estos conceptos clave, podrás utilizar el polimorfismo de forma eficaz en el diseño de tus API y crear sistemas más versátiles y fáciles de mantener.
Última actualización: 21 de mayo de 2024.