Me complace anunciar el lanzamiento de la versión 1.0 del SDK de Couchbase Kotlin. La verdad es que estoy encantado. Este proyecto ha sido un trabajo de amor. Después de trabajar con Java durante décadas, tengo un nuevo lenguaje favorito.

En este artículo, diré algunas cosas buenas sobre Kotlin, y luego mostraré cómo usar el SDK de Couchbase Kotlin para conectarse a la base de datos como servicio de Capella. Finalmente, compartiré algunas decisiones de diseño que dieron forma a la API pública del SDK. Espero que te quedes para esta última parte, especialmente si estás diseñando la API de tu propia librería Kotlin.

¿Por qué Kotlin?

Kotlin cambió nuestra forma de pensar sobre la programación asíncrona en la JVM. Las coroutines y las funciones de suspensión de Kotlin son la prueba de que la programación reactiva podría ser un peldaño hacia algo mejor, algo que no requiera sacrificar código legible en el altar de la escalabilidad. Kotlin ha demostrado que hay una forma mejor de escribir código asíncrono de alto rendimiento, y no necesitamos esperar a las fibras y continuaciones del Proyecto Loom.

Capella + Kotlin

Couchbase Capella es la base de datos como servicio (DBaaS) para Couchbase Server. Es una tecnología sólida, y cuando me registré para una prueba gratuita hace unas semanas, el proceso fue completamente indoloro.

Digamos que usted tiene un cluster de prueba Capella y ha añadido su IP a la lista de permitidos, y ha creado un usuario de base de datos que puede leer el pre-instalado viaje-muestra cubo. A continuación te explicamos cómo conectarte a tu clúster utilizando el SDK de Kotlin: 

Una vez que tenga un objeto Cluster, puede ejecutar una consulta N1QL:

O consiga una referencia a una Colección y lea un documento específico:

La versión completa de este ejemplo se incluye en el archivo Documentación del SDK de Kotlinjunto con varios otros.

Decisiones de diseño de la API del SDK

El resto de este artículo está dedicado a compartir algunas notas sobre las decisiones que tomamos al diseñar la API pública del SDK Kotlin de Couchbase. En algunos casos, compararé el SDK de Kotlin con su hermano mayor, el SDK de Java.

Extensión frente a SDK independiente

El SDK de Couchbase Kotlin depende del mismo core-io como el SDK de Java, pero no depende de éste.

Alternativas rechazadas

Nos planteamos dar soporte a Kotlin proporcionando funciones de extensión para las clases del SDK de Java. Por desgracia, algunas decisiones de diseño que tomamos para el SDK de Java no se trasladaron bien a Kotlin y no se pudieron compensar solo con funciones de extensión.

También consideramos la posibilidad de proporcionar una envoltura nativa completa de la API de Kotlin que simplemente delegara en el SDK de Java, pero nos preocupaba que tener dos versiones de todas las clases (una para Kotlin y otra para Java) resultara confuso para los usuarios.

¡Suspender o reventar!

El SDK de Kotlin no proporciona una API de bloqueo; los métodos que realizan E/S de red son todos suspender funciones.

Alternativas rechazadas

Consideramos la posibilidad de añadir variantes "bloqueantes" de Cluster, Bucket, Scope, Collection, etc., pero parece algo que los usuarios pueden hacer por sí mismos con muy poco esfuerzo, simplemente envolviendo las llamadas a las funciones de suspensión con runBlocking.

Parámetros opcionales

Como Java no tiene parámetros opcionales, el SDK Java de Couchbase los emula con un "bloque de opciones" construido usando el patrón constructor. 

En el siguiente ejemplo, withExpiry es un parámetro booleano opcional cuyo valor por defecto es false. Los fragmentos de código muestran un sitio de llamada en el que el desarrollador desea pasar true en su lugar.

Java:

El SDK de Kotlin aprovecha el soporte nativo de Kotlin para parámetros por defecto:

Kotlin:

Alternativas rechazadas

También consideramos la posibilidad de utilizar bloques de opciones específicos de método para Kotlin, que habrían tenido un aspecto similar:

Se rechazó porque era engorroso para los usuarios y difícil de mantener para los desarrolladores del SDK (considere el impacto de añadir una nueva opción común a todos los métodos).

También consideramos la posibilidad de utilizar un lambda/mini-DSL para las opciones, que habría sido algo así:

Esta era la más tentadora de las alternativas rechazadas porque habría sido excelente para la compatibilidad binaria (las firmas de los métodos no cambiarían al añadir nuevos parámetros opcionales). También "se siente como Kotlin". Fue rechazada porque:

    • La finalización de código IDE no ofrecía el mismo nivel de orientación para los DSL que para los parámetros de método (aunque es probable que los IDE mejoren con el tiempo).
    • Queríamos reservar el último parámetro lambda para otros fines.

Parámetros comunes

Algunos parámetros opcionales son comunes a muchos métodos de la API del SDK de Couchbase. Algunos ejemplos son la duración del tiempo de espera, la estrategia de reintento y el intervalo de rastreo.

En Java, estas opciones comunes son propiedades de una clase base CommonOptions a la que extienden todos los bloques de opciones específicos de los métodos; para el usuario, no se ven diferentes de otros parámetros:

Java:

En Kotlin, adoptamos un enfoque diferente que equilibra la comodidad de los parámetros por defecto con algunas concesiones pragmáticas para la mantenibilidad y la compatibilidad binaria. Los parámetros comunes están representados por un bloque de opciones llamado CommonOptions. Los métodos aceptan un parámetro opcional cuyo valor por defecto es un CommonOptions que representa las opciones por defecto. Anular las opciones predeterminadas tiene el siguiente aspecto:

Kotlin:

Alternativas rechazadas

Hemos considerado tratar las opciones comunes como parámetros normales, así:

Aunque esto sería agradable para los usuarios, se rechazó porque añadir o eliminar un parámetro común requeriría cambiar la firma de casi todos los métodos públicos de la base de código, lo que haría que mantener la compatibilidad binaria fuera una tarea ardua. Estudiamos la posibilidad de automatizar este proceso mediante la generación de código, pero la complejidad de este enfoque parecía superar su valor.

Al final, optamos por utilizar el CommonOptions como una especie de mamparo de la API para aislar los problemas de mantenimiento relacionados con las opciones comunes.

Compatibilidad binaria

Estas decisiones sobre los parámetros comunes y opcionales tienen las siguientes implicaciones para la compatibilidad binaria:

Añadir un parámetro opcional a un método rompe la compatibilidad binaria sólo para ese método. La compatibilidad puede restaurarse añadiendo un método con la firma antigua, anotado como Deprecated(level=HIDDEN). El resultado es que el impacto del mantenimiento se aísla en un único método, y los cambios en el código para mantener la compatibilidad tienen un alcance igualmente restringido.

La adición de un parámetro común rompe la compatibilidad binaria sólo para el CommonOptions clase. La compatibilidad puede restaurarse añadiendo un constructor con la firma antigua, anotado como Deprecated(level=HIDDEN). Significativamente, no tenemos que cambiar la firma de los métodos que toman CommonOptions como parámetro.

Parámetros mutuamente excluyentes

A veces, un método puede tener dos formas distintas de especificar el valor de un parámetro. Por ejemplo, varios métodos toman un caducidad que puede especificarse como Duración o un Instantánea. En la API de Java, nada te impide escribir este código:

Estas dos formas de especificar la caducidad son mutuamente excluyentes, pero Java le permite escribir el código de todos modos. Si hay una comprobación de validez, tiene que ocurrir en tiempo de ejecución. (En este ejemplo concreto, la segunda llamada a caducidad borra el valor establecido por la llamada anterior).

En Kotlin el método upsert tiene un único parámetro de caducidad de tipo Caducidaddonde Caducidad es una clase sellada:

o

Este patrón se aplica en toda la API; las opciones mutuamente excluyentes siempre se representan como un único parámetro que toma una instancia de una clase sellada cuyas instancias representan las diferentes formas de especificar el valor.

Resultados del streaming

Los servicios Couchbase Query, Analytics, View y Full-Text Search pueden devolver conjuntos de resultados muy grandes. Con el fin de procesar estos resultados de manera eficiente sin agotar el montón, los métodos de consulta para estos servicios devuelven sus resultados como un flujo Kotlin.

Ofrecemos dos ejecutar en este flujo. Uno de los métodos almacena las filas de resultados en memoria antes de devolver el conjunto de resultados completo (sólo se utiliza cuando se sabe que el conjunto de resultados es pequeño). El otro método permite al usuario proporcionar una lambda para aplicar a cada fila de resultados a medida que se recibe del servidor. Ambas versiones aprovechan el control de contrapresión/flujo proporcionado por la biblioteca core-io.

Alternativas rechazadas

Consideramos la posibilidad de exponer los objetos del Proyecto Reactor Flux/Mono utilizados por el core-io pero hemos decidido que después de probar las coroutines no echamos de menos Reactor en absoluto, y creemos que la mayoría de los usuarios opinarán lo mismo.

DSL frente a constructor jerárquico

Los SDKs de Couchbase tienen muchas opciones de configuración agrupadas en categorías separadas. Para los SDKs de JVM, estas opciones son propiedades del módulo ClusterEnvironment. En Java, estas opciones se configuran mediante una directiva ClusterEnvironment constructor. He aquí un ejemplo en Java que desactiva la compresión, DNS SRV y el interruptor de servicio Key/Value:

La API de Kotlin aprovecha el soporte DSL de Kotlin, permitiendo expresar la misma configuración como:

La API de Kotlin también permite configurar el entorno en línea con el método connect:

Los usuarios que prefieran el constructor de entornos de clúster tradicional al DSL pueden seguir utilizando el constructor si lo desean.

Alternativas rechazadas

También podríamos haber utilizado clases de datos en lugar de un DSL:

Eso habría estado bien, pero el DSL es más conciso y da la sensación de que estamos jugando con los puntos fuertes de Kotlin.

Resumen

Hemos puesto mucho cuidado en el diseño de la API pública del SDK Kotlin de Couchbase. No puedo prometer que lo hayamos hecho todo bien, pero espero que el resultado se sienta como algo que respeta los modismos y las mejores prácticas de Kotlin.

El SDK de Couchbase Kotlin está finalmente listo para su uso en producción, ya sea que esté utilizando el Capella DBaaS o la gestión de su propio clúster de Couchbase Server. Todo lo que no esté anotado como volátil o no comprometido ya forma parte oficialmente de la API pública estable. Un enorme Gracias. a todos los miembros de la comunidad que nos han hecho llegar sus comentarios.

Autor

Publicado por David Nault

David Nault escribe código en Couchbase, donde trabaja en el equipo SDK & Connectors.

Dejar una respuesta