ETL — Um exemplo prático usando Scala

Marina Cavalari
4 min readJul 23, 2021

--

Eu nunca tinha lido uma única linha de código Scala até trabalhar no Nubank, e depois que entendi o racional por trás de cada função em Scala, soube que programação funcional era o meu santo graal.

Scala rainha, Python nadinha rs

Scala é uma linguagem de programação funcional (que também pode ser usada em orientação a objetos) que roda em cima de uma JVM, ou seja, uma linguagem compilada.

No artigo de hoje, vamos ver um exemplo de ETL simples, que realizará o tratamento de um determinado conjunto de dados (lidos a partir de uma planilha, apenas para demonstrar sua usabilidade, pois num ambiente real, leríamos dados vindos de um s3, ou algo parecido) e nos retornará um output (em csv para facilitar a demonstração), e para isso, usaremos um framework de big data, o Spark com o Scala. Por feito em Scala, o Spark tem funções específicas e customizadas para garantir maior performance e escalabilidade (tudo o que nós precisamos quando o assunto é big data).

Configurações iniciais

Para começarmos, é importante ter instalado o SBT, Scala e o Spark.

O SBT é o nosso gerenciador, assim como Maven para o Java, ele vai ser responsável por buildar, rodar o projeto, fazer o papel de lint e etc.

Além disso, precisamos criar o nosso arquivo de configuração do Spark, ele vai ser responsável por saber como iniciar as sessões, recursos de memória, etc. Esse arquivo deve se parecer com o código abaixo:

Além do arquivo de configuração do Spark, precisamos criar o nosso arquivo de build, que fazendo uma alusão ao Clojure, seria o project.clj, que deve conter as libs necessárias para rodarmos nosso projeto, a versão que usaremos do Scala, e do próprio Spark, como abaixo:

Começando nossa aplicação

Nossa classe Scala deve ser iniciada pelos imports necessários para a nossa aplicação funcionar, dessa forma, os que usaremos nesse exemplo são os responsáveis por lidar com: Excel (para ler a planilha), o próprio Spark para lidar com as sessões e funções built-in, e o por fim o hadoop, que é parte desse ecossistema de big-data.

Depois de importar tudo que é necessário, gosto de definir como serão as funções que usarei na minha aplicação. Como essa é uma aplicação simples, não há imports de outras classes, nem funções. Scala é até 10 vezes mais mais performático que Python, um exemplo dessa vantagem é por que Scala é uma linguagem estaticamente tipada, enquanto Python é dinamicamente tipada, sendo assim, muito mais rápida na hora de compilar e identificar possíveis problemas no código. Quando nós realizamos a declaração de funções que vamos usar, nós já criamos elas tipadas e dessa forma elas são extremamente mais ágeis no momento de processamento. Outro ponto positivo do Scala, é que ele consegue ter interface direta com a API do hadoop, que é em Java, por ser compilado em bitecode para rodar na JVM no fim das contas, enquanto python precisa usar algumas libs terceiras para que essa integração seja possível, por exemplo: hadoopy. Além de ser uma forma mais elegante e organizada de se estruturar o código, facilitando também a testagem do mesmo. Uma função criada para nossa aplicação pode ser compreendida no exemplo abaixo:

Na função acima, apenas recebemos como parâmetro o caminho do arquivo a ser lido, e dessa forma, nós podemos extrair o dado a ser processado (Extract).

A partir da leitura dos nossos dados, podemos executar qualquer tipo de transformação neles. Como dito anteriormente, nós precisamos declarar as funções que utilizaremos para assim manter a performance da aplicação. Um exemplo de função para definir o layout pode ser entendida conforme abaixo:

As funções de layout e transformações no DafaFrame de origem são sempre puras, pois elas nunca alteram o dado de entrada, apenas o transforma em algo novo, mantendo assim sua imutabilidade. (Transform)

Depois de todas as transformações necessárias, temos que carregar esse dado tratado, novo (Load). A função abaixo é responsável por essa parte da nossa ETL:

Pronto, as funções estão prontas, apenas precisamos fazer a chamada para elas serem utilizadas. Em Scala, nossas funções são chamadas como parâmetros dentro do método transform, que é responsável por fazer a iteração através da cauda do seu dataset, evitando assim que ele percorra um dataset que não conhece o tamanho final, e de a exceção de stackoverflow.

Nossa aplicação, no final, deve se parecer como esse exemplo:

Após todas essas pequenas partes, nós seremos capazes de ter nosso projeto, que pode ser exemplificado pelo repositório contendo os arquivos aqui demonstrados na estrutura necessária para que nossa aplicação rode.

Os comandos necessários para rodarmos a aplicação então são:

sbt clean assembly

Esse comando é responsável por gerar o .jar da nossa aplicação, em outras palavras, torná-la executável. Dessa forma, para finalizar, rodamos o comando abaixo para que ela seja executada:

spark-submit target/scala-2.12/etlScalaExample_2.12–1.0.jar-packages com.crealytics:spark-excel_2.12:0.13.7-class=etl.etlExampleScalaApp

E pronto, nossa aplicação pode ser rodada manualmente no nosso cli, ou pode ser dockerizada, e dessa forma escalada muito facilmente.

Mais informações sobre buildar e submeter aplicações em Spark nos links abaixo:

--

--