Kotlin

Projetando a API Kotlin do Couchbase 

Tenho o prazer de anunciar o lançamento do GA do Couchbase Kotlin SDK versão 1.0. Na verdade, estou muito feliz. Esse projeto tem sido um trabalho de amor. Depois de trabalhar com Java por décadas, tenho uma nova linguagem favorita.

Neste artigo, falarei algumas coisas boas sobre o Kotlin e, em seguida, mostrarei como usar o SDK do Couchbase Kotlin para se conectar ao banco de dados como serviço Capella. Por fim, compartilharei algumas decisões de design que moldaram a API pública do SDK. Espero que você fique atento a essa última parte, especialmente se estiver projetando a API de sua própria biblioteca Kotlin.

Por que Kotlin?

O Kotlin mudou a forma como pensamos sobre a programação assíncrona na JVM. As corrotinas e as funções de suspensão do Kotlin são evidências de que a programação reativa pode ser um trampolim para algo melhor, algo que não exige o sacrifício de um código legível no altar da escalabilidade. O Kotlin mostrou que há uma maneira melhor de escrever código assíncrono de alto desempenho, e não precisamos esperar pelas fibras e continuações do Project Loom.

Capella + Kotlin

Couchbase Capella é o banco de dados como serviço (DBaaS) para o Couchbase Server. É uma tecnologia sólida e, quando me inscrevi para uma avaliação gratuita há algumas semanas, o processo foi totalmente indolor.

Digamos que você tenha um cluster de avaliação do Capella e tenha adicionado seu IP à lista de permissões, além de ter criado um usuário de banco de dados que pode ler o arquivo amostra de viagem bucket. Veja como se conectar ao seu cluster usando o SDK do Kotlin: 

Quando você tiver um objeto Cluster, poderá executar uma consulta N1QL:

Ou obtenha uma referência a uma coleção e leia um documento específico:

A versão completa desse exemplo está incluída no arquivo Documentação do Kotlin SDKe vários outros.

Decisões de design da API do SDK

O restante deste artigo é dedicado a compartilhar algumas notas sobre as decisões que tomamos ao projetar a API pública do Couchbase Kotlin SDK. Em alguns casos, estarei comparando o Kotlin SDK com seu irmão mais velho, o Java SDK.

Extensão versus SDK independente

O SDK do Couchbase Kotlin depende do mesmo núcleo-io como o Java SDK, mas não depende do Java SDK.

Alternativas rejeitadas

Consideramos a possibilidade de oferecer suporte ao Kotlin fornecendo funções de extensão para classes do Java SDK. Infelizmente, algumas decisões de design que tomamos para o Java SDK não foram bem traduzidas para o Kotlin e não puderam ser compensadas apenas com funções de extensão.

Também consideramos a possibilidade de fornecer um wrapper completo da API nativa do Kotlin que simplesmente delegasse ao SDK do Java, mas estávamos preocupados com o fato de que ter duas versões de todas as classes (uma para o Kotlin e outra para o Java) seria confuso para os usuários.

Suspender ou falir!

O SDK do Kotlin não oferece uma API de bloqueio; os métodos que fazem E/S de rede são todos suspender funções.

Alternativas rejeitadas

Consideramos a possibilidade de adicionar variantes "bloqueadoras" de Cluster, Bucket, Scope, Collection etc., mas isso parece ser algo que os próprios usuários podem fazer com muito pouco esforço, bastando envolver as chamadas para as funções de suspensão com runBlocking.

Parâmetros opcionais

Como o Java não tem parâmetros opcionais, o Couchbase Java SDK os emula com um "bloco de opções" construído usando o padrão builder. 

No exemplo a seguir, com expiração é um parâmetro booleano opcional cujo padrão é false. Os trechos de código mostram um site de chamada em que o desenvolvedor deseja passar true em vez disso.

Java:

O SDK do Kotlin aproveita o suporte nativo do Kotlin para parâmetros padrão:

Kotlin:

Alternativas rejeitadas

Também consideramos a possibilidade de usar blocos de opções específicos de métodos para o Kotlin, que teriam algo parecido com:

Isso foi rejeitado porque era complicado para os usuários e difícil de manter para os desenvolvedores do SDK (considere o impacto de adicionar uma nova opção comum a todos os métodos).

Também consideramos a possibilidade de usar um lambda/mini-DSL para opções, o que seria algo parecido com:

Essa foi a mais tentadora das alternativas rejeitadas porque teria sido excelente para a compatibilidade binária (as assinaturas de métodos não mudariam à medida que novos parâmetros opcionais fossem adicionados). Ela também "parece Kotlin". Ela foi rejeitada porque:

    • O recurso de autocompletar código do IDE não forneceu o mesmo nível de orientação para DSLs e para parâmetros de método (embora os IDEs provavelmente melhorem com o tempo).
    • Queríamos reservar o parâmetro lambda final para outros fins.

Parâmetros comuns

Alguns parâmetros opcionais são comuns a muitos métodos na API do SDK do Couchbase. Os exemplos incluem a duração do tempo limite, a estratégia de nova tentativa e a extensão do rastreamento.

Em Java, essas opções comuns são propriedades de uma classe base CommonOptions que todos os blocos de opções específicas de métodos estendem; para o usuário, elas não são diferentes de outros parâmetros:

Java:

Em Kotlin, adotamos uma abordagem diferente que equilibra a conveniência dos parâmetros padrão com algumas concessões pragmáticas para manutenção e compatibilidade binária. Os parâmetros comuns são representados por um bloco de opções chamado CommonOptions. Os métodos aceitam um parâmetro opcional cujo valor padrão é um CommonOptions que representa as opções padrão. A substituição dos padrões tem a seguinte aparência:

Kotlin:

Alternativas rejeitadas

Consideramos a possibilidade de tratar as opções comuns como parâmetros normais, assim:

Embora isso certamente fosse agradável para os usuários, foi rejeitado porque adicionar ou remover um parâmetro comum exigiria a alteração da assinatura de quase todos os métodos públicos na base de código e tornaria a manutenção da compatibilidade binária uma tarefa árdua. Analisamos a possibilidade de automatizar esse processo usando a geração de código, mas a complexidade dessa abordagem parecia superar o valor.

No final, optamos por usar o CommonOptions como um tipo de anteparo de API para isolar problemas de manutenção relacionados a opções comuns.

Compatibilidade binária

Essas decisões sobre parâmetros comuns e opcionais têm as seguintes implicações para a compatibilidade binária:

A adição de um parâmetro opcional a um método quebra a compatibilidade binária somente para esse método. A compatibilidade pode ser restaurada adicionando-se um método com a assinatura antiga, anotada como Depreciado(level=HIDDEN). O resultado é que o impacto da manutenção é isolado em um único método, e as alterações no código para manter a compatibilidade também têm escopo restrito.

A adição de um parâmetro comum quebra a compatibilidade binária somente para o CommonOptions classe. A compatibilidade pode ser restaurada com a adição de um construtor com a assinatura antiga, anotada como Depreciado(level=HIDDEN). É significativo o fato de que não precisamos alterar a assinatura dos métodos que recebem CommonOptions como um parâmetro.

Parâmetros mutuamente exclusivos

Às vezes, um método pode ter duas formas diferentes de especificar um valor de parâmetro. Por exemplo, vários métodos recebem um expiração que pode ser especificado como um Duração ou um Instantâneo. Na API Java, não há nada que o impeça de escrever esse código:

Essas duas formas de especificar a expiração são mutuamente exclusivas, mas o Java permite que você escreva o código de qualquer forma. Se houver uma verificação de validade, ela deverá ocorrer em tempo de execução. (Neste exemplo específico, a segunda chamada para expiração abocanha o valor definido pela chamada anterior).

Em Kotlin, o método upsert tem um único parâmetro de expiração do tipo Expiração, onde Expiração é uma classe selada:

ou

Esse padrão é aplicado em toda a API; as opções mutuamente exclusivas são sempre representadas como um único parâmetro que recebe uma instância de uma classe selada cujas instâncias representam as diferentes formas de especificar o valor.

Resultados de streaming

Os serviços Couchbase Query, Analytics, View e Full-Text Search podem retornar conjuntos de resultados muito grandes. Para processar esses resultados com eficiência sem esgotar a pilha, os métodos de consulta desses serviços retornam seus resultados como um fluxo Kotlin.

Fornecemos dois executar métodos de extensão nesse fluxo. Um método armazena as linhas de resultados na memória antes de retornar todo o conjunto de resultados (a ser usado somente quando se sabe que o conjunto de resultados é pequeno). O outro método permite que o usuário forneça um lambda para aplicar a cada linha de resultado à medida que ela é recebida do servidor. Ambas as versões aproveitam o controle de contrapressão/fluxo fornecido pela biblioteca core-io.

Alternativas rejeitadas

Consideramos a possibilidade de expor os objetos Flux/Mono do Project Reactor usados pelo núcleo-io mas decidimos que, depois de experimentar as corrotinas, não sentimos nenhuma falta do Reactor, e acreditamos que a maioria dos usuários terá a mesma opinião.

DSL vs. construtor hierárquico

Os SDKs do Couchbase têm muitas opções de configuração agrupadas em categorias separadas. Para os SDKs da JVM, essas opções são propriedades da variável ClusterEnvironment. Em Java, essas opções são configuradas usando um ClusterEnvironment construtor. Aqui está um exemplo em Java que desativa a compactação, o DNS SRV e o disjuntor do serviço Key/Value:

A API do Kotlin aproveita o suporte a DSL do Kotlin, permitindo que a mesma configuração seja expressa como:

A API do Kotlin também permite configurar o ambiente em linha com o método de conexão:

Os usuários que preferem o construtor de ambiente de cluster tradicional em vez do DSL podem continuar a usar o construtor, se desejarem.

Alternativas rejeitadas

Poderíamos muito bem ter usado classes de dados em vez de uma DSL:

Isso teria sido bom, mas a DSL é mais concisa e parece mais que estamos jogando com a força do Kotlin.

Resumo

Pensamos muito no projeto da API pública do Couchbase Kotlin SDK. Não posso prometer que fizemos tudo certo, mas espero que o resultado seja algo que respeite os idiomas e as práticas recomendadas do Kotlin.

O SDK do Couchbase Kotlin está finalmente pronto para ser usado na produção, quer você esteja usando o Capella DBaaS ou gerenciando seu próprio cluster do Couchbase Server. Tudo o que não estiver anotado como volátil ou não comprometido agora faz parte oficialmente da API pública estável. Uma enorme Obrigado! agradece a todos da comunidade que compartilharam seus comentários ao longo do caminho.

Compartilhe este artigo
Receba atualizações do blog do Couchbase em sua caixa de entrada
Esse campo é obrigatório.

Autor

Postado por David Nault

David Nault escreve código no Couchbase, onde trabalha na equipe de SDK e conectores.

Deixe um comentário

Pronto para começar a usar o Couchbase Capella?

Iniciar a construção

Confira nosso portal do desenvolvedor para explorar o NoSQL, procurar recursos e começar a usar os tutoriais.

Use o Capella gratuitamente

Comece a trabalhar com o Couchbase em apenas alguns cliques. O Capella DBaaS é a maneira mais fácil e rápida de começar.

Entre em contato

Deseja saber mais sobre as ofertas do Couchbase? Deixe-nos ajudar.