██╗██████╗ ███████╗██████╗ ██╗ █████╗ ███████╗
     ██║██╔══██╗╚══███╔╝██╔══██╗██║██╔══██╗██╔════╝
     ██║██████╔╝  ███╔╝ ██║  ██║██║███████║███████╗
██   ██║██╔═══╝  ███╔╝  ██║  ██║██║██╔══██║╚════██║
╚█████╔╝██║     ███████╗██████╔╝██║██║  ██║███████║
 ╚════╝ ╚═╝     ╚══════╝╚═════╝ ╚═╝╚═╝  ╚═╝╚══════╝   
⠀⠀
    

Uma carta de amor ao Ruby on Rails

Há algumas semanas, resolvi sair da minha zona de conforto e aprender Ruby on Rails. Vindo de um background em Java, a experiência foi… interessante, para dizer o mínimo. Ruby é uma linguagem completamente diferente. Não é só sintaxe diferente, é uma forma diferente de pensar sobre programação.

A ideia era criar um projeto que me forçasse a pensar diferente. Algo que não fosse só mais um CRUD básico, mas que tivesse regras de negócio complexas e interessantes. Foi aí que pensei em um sistema de apuração tributária - um sistema para calcular o DAS (Documento de Arrecadação do Simples Nacional) mensalmente.

O que me atraiu nesse projeto foi justamente o fato de lidar com regras de negócio bem diferentes do que estou acostumado. Em vez de sistemas de e-commerce ou CRUDs administrativos, aqui eu teria que pensar em imutabilidade fiscal, cálculos determinísticos, e garantir que uma vez que uma nota fiscal fosse emitida, ela não pudesse mais ser alterada. É um tipo de problema que força você a pensar na modelagem de dados de forma diferente.

O Projeto

O Ruby Fiscal Project é um MVP de um sistema de apuração tributária. A ideia é simples: você cadastra notas fiscais, cria apurações mensais, e o sistema calcula automaticamente o DAS baseado nas notas de saída do período.

Mas o que realmente me interessou foi a oportunidade de trabalhar com regras de negócio completamente diferentes do que estou acostumado. Sistemas fiscais têm características únicas: dados imutáveis, cálculos que precisam ser determinísticos, e uma série de validações que não existem em outros tipos de sistemas.

É o tipo de projeto que te força a pensar em modelagem de dados de forma diferente. Não é só criar tabelas e relacionamentos. É pensar em como garantir que uma nota fiscal, uma vez criada, não possa ser alterada. É pensar em como garantir que os cálculos sejam sempre os mesmos, independente de quando são executados.

Ruby: Uma Forma Diferente de Pensar

Vindo do Java, a primeira coisa que me chamou atenção no Ruby foi a sintaxe. Tudo parece mais limpo, mais direto. Em Java, você escreve:

public class UserService {
    private UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException("User not found"));
    }
}

Em Ruby, a mesma coisa fica assim:

class UserService
  def initialize(user_repository)
    @user_repository = user_repository
  end

  def find_by_id(id)
    @user_repository.find_by_id(id) || raise UserNotFoundError, "User not found"
  end
end

É menos código, mas também é uma forma diferente de pensar. Ruby te força a ser mais expressivo, a escrever código que se lê quase como inglês. E isso, no começo, foi difícil. Eu estava acostumado a ser explícito sobre tudo - tipos, modificadores de acesso, exceções. Ruby confia mais em você, e isso foi um ajuste que precisei fazer.

Mas depois que você se acostuma, você percebe que Ruby é uma linguagem incrivelmente poderosa. A flexibilidade que ela oferece permite escrever código muito mais expressivo e legível.

Rails: As Facilidades Que Fazem Diferença

O que mais me impressionou no Rails foram as facilidades que ele traz. Coisas que em Java/Spring você precisa configurar manualmente, no Rails já vêm prontas ou são extremamente simples de implementar.

Active Record: ORM Que Faz Sentido

O Active Record do Rails é muito bom de se usar. Em vez de escrever queries SQL ou usar uma API complexa, você escreve código Ruby que se lê quase como português:

# Buscar todas as notas fiscais de saída de janeiro de 2024
notas = NotaFiscal.where(tipo: :saida)
                  .where("data_emissao >= ? AND data_emissao <= ?", 
                         Date.new(2024, 1, 1), 
                         Date.new(2024, 1, 31))

# Calcular a receita bruta
receita_bruta = notas.sum(:valor_base_calculo)

É intuitivo, é legível, e funciona. Não precisa de configuração complexa, não precisa de múltiplos arquivos XML ou anotações espalhadas. É só código Ruby.

Migrations: Versionamento de Banco de Dados

As migrations do Rails são geniais. Em vez de escrever SQL manualmente e ter que gerenciar scripts de atualização, você escreve código Ruby que descreve as mudanças:

class CreateNotasFiscais < ActiveRecord::Migration[7.0]
  def change
    create_table :notas_fiscais do |t|
      t.string :numero, null: false
      t.string :serie, null: false
      t.date :data_emissao, null: false
      t.decimal :valor_total, precision: 10, scale: 2, null: false
      t.integer :tipo, null: false
      t.string :cnpj_emitente, null: false
      t.string :cnpj_destinatario

      t.timestamps
    end

    add_index :notas_fiscais, [:numero, :serie], unique: true
  end
end

E o Rails gerencia tudo. Você pode fazer rollback, pode ver o histórico, pode aplicar em qualquer ambiente. É simples e funciona.

Concerns: Reutilização de Código

Uma das coisas que mais usei no projeto foram concerns. Em vez de herança múltipla ou composição complexa, você cria módulos que podem ser incluídos onde necessário:

module Imutavel
  extend ActiveSupport::Concern

  included do
    before_update :prevenir_alteracao, if: :imutavel?
  end

  def imutavel?
    # Lógica específica de cada modelo
  end

  private

  def prevenir_alteracao
    errors.add(:base, "Este registro não pode ser alterado")
    throw(:abort)
  end
end

E então você simplesmente inclui no modelo:

class NotaFiscal < ApplicationRecord
  include Imutavel
  
  def imutavel?
    true # Notas fiscais são sempre imutáveis
  end
end

É elegante, é simples, e funciona perfeitamente para o que eu precisava.

Service Objects: Organizando Lógica Complexa

Para organizar a lógica de negócio, usei service objects. Em Rails, isso é simples - você cria classes em app/services e pronto:

module ApuracaoService
  class Processar
    def self.call(apuracao)
      new(apuracao).call
    end

    def initialize(apuracao)
      @apuracao = apuracao
    end

    def call
      notas = buscar_notas_do_periodo
      receita_bruta = calcular_receita_bruta(notas)
      valor_das = calcular_das(receita_bruta)
      
      @apuracao.update!(
        receita_bruta: receita_bruta,
        valor_das: valor_das
      )
      
      associar_notas(notas)
    end

    private

    def buscar_notas_do_periodo
      NotaFiscal.where(tipo: :saida)
                .where("data_emissao >= ? AND data_emissao <= ?",
                       @apuracao.periodo.inicio,
                       @apuracao.periodo.fim)
    end

    def calcular_receita_bruta(notas)
      notas.sum(:valor_base_calculo)
    end

    def calcular_das(receita_bruta)
      DasCalculator.calcular(receita_bruta: receita_bruta)[:valor_das]
    end

    def associar_notas(notas)
      notas.each { |nota| @apuracao.notas_fiscais << nota }
    end
  end
end

E para usar, é só chamar:

ApuracaoService::Processar.call(apuracao)

É limpo, é testável, e não precisa de injeção de dependência complexa. Ruby permite isso de forma natural.

Value Objects: Conceitos do Domínio

Para encapsular conceitos fiscais, criei value objects. Em Ruby, isso é simples - você cria classes normais:

module Fiscal
  class Aliquota
    FAIXAS = [
      { limite: 180_000, aliquota: 0.06 },
      { limite: 360_000, aliquota: 0.075 },
      { limite: 720_000, aliquota: 0.095 },
      { limite: Float::INFINITY, aliquota: 0.11 }
    ].freeze

    attr_reader :valor

    def initialize(valor)
      @valor = valor
    end

    def self.para_receita(receita_bruta)
      faixa = FAIXAS.find { |f| receita_bruta <= f[:limite] }
      new(faixa[:aliquota])
    end

    def percentual
      (valor * 100).round(2)
    end
  end
end

E usa assim:

aliquota = Fiscal::Aliquota.para_receita(200_000)
puts aliquota.percentual # => 7.5

É simples, é direto, e encapsula a lógica de negócio de forma clara.

O Projeto: Regras de Negócio Diferentes

O que mais me interessou nesse projeto foi trabalhar com regras de negócio completamente diferentes do que estou acostumado. Em sistemas fiscais, você precisa pensar em:

Imutabilidade: Uma nota fiscal, uma vez criada, não pode ser alterada. Não é uma questão de “seria legal”, é uma questão de “alterar isso pode gerar problemas com a Receita Federal”. No projeto, implementei isso através de um concern que previne updates:

class NotaFiscal < ApplicationRecord
  include Imutavel
  
  def imutavel?
    true
  end
end

Cálculos Determinísticos: O cálculo do DAS precisa ser sempre o mesmo para os mesmos inputs. Não pode depender de estado externo, não pode ter efeitos colaterais. Implementei isso através de um service puro:

class DasCalculator
  def self.calcular(receita_bruta:)
    aliquota = Fiscal::Aliquota.para_receita(receita_bruta)
    valor_das = receita_bruta * aliquota.valor

    {
      receita_bruta: receita_bruta,
      aliquota_percentual: aliquota.percentual,
      valor_das: valor_das
    }
  end
end

É uma função pura - sempre retorna o mesmo resultado para os mesmos inputs. Isso facilita testes e garante consistência.

Validações Complexas: Uma apuração não pode ser criada para um período que já tem uma apuração finalizada. Essas validações precisam estar no modelo, e Rails facilita isso:

class Apuracao < ApplicationRecord
  validates :mes, :ano, presence: true
  validate :periodo_unico_se_finalizada

  def periodo_unico_se_finalizada
    if finalizada? && existe_apuracao_para_periodo?
      errors.add(:base, "Já existe uma apuração finalizada para este período")
    end
  end
end

É simples, é direto, e funciona perfeitamente.

O Que Aprendi Sobre Ruby

  1. Ruby é expressivo: A sintaxe permite escrever código que se lê quase como português. Isso torna o código mais legível e mais fácil de entender.

  2. Ruby confia em você: Em Java, você precisa ser explícito sobre tudo - tipos, modificadores de acesso, exceções. Ruby confia que você sabe o que está fazendo, e isso permite escrever código mais conciso.

  3. Ruby é flexível: A flexibilidade da linguagem permite implementar padrões de forma mais natural. Service objects, value objects, concerns - tudo isso flui de forma mais natural em Ruby.

  4. Ruby tem uma comunidade incrível: A quantidade de gems disponíveis, a qualidade da documentação, e a comunidade em geral são impressionantes.

O Que Aprendi Sobre Rails

  1. Rails facilita muito o desenvolvimento: Coisas que em Java/Spring você precisa configurar manualmente, no Rails já vêm prontas ou são extremamente simples.

  2. Convention over Configuration funciona: Seguir as convenções do Rails acelera muito o desenvolvimento. Você não precisa decidir onde colocar cada arquivo, como nomear cada classe - o Rails já tem uma resposta.

  3. Active Record é poderoso: O ORM do Rails é intuitivo e poderoso. Você escreve código Ruby e ele gera SQL otimizado. É simples e funciona.

  4. Rails tem ferramentas para tudo: Migrations, concerns, service objects, value objects - tudo pode ser implementado de forma elegante sem precisar de gems externas.

Os Desafios de Aprender Ruby Vindo do Java

  • Pensar diferente: Java te força a ser explícito sobre tudo. Ruby te permite ser mais flexível, e isso foi um ajuste mental grande.

  • Sintaxe diferente: A sintaxe do Ruby é completamente diferente do Java. Blocos, símbolos, métodos sem parênteses - tudo isso foi novo.

  • Falta de tipos: Em Java, você sempre sabe o tipo de uma variável. Em Ruby, você precisa confiar na documentação ou no código. Isso foi difícil no começo.

  • Debugging diferente: As ferramentas de debugging são diferentes, e a forma de pensar sobre erros também é diferente.

Mas no final, valeu a pena. Ruby é uma linguagem incrível, e Rails é um framework que realmente facilita o desenvolvimento.

O Fim da História

Aprender Ruby vindo do Java foi uma experiência interessante. No começo, foi difícil pensar diferente. Eu estava acostumado a ser explícito sobre tudo, a ter tipos em todo lugar, a ter uma estrutura rígida. Ruby me forçou a pensar de forma mais flexível, e isso foi um ajuste.

Mas depois que você se acostuma, você percebe que Ruby é uma linguagem incrivelmente poderosa. A sintaxe é limpa, o código é expressivo, e a flexibilidade permite implementar padrões de forma mais natural.

E Rails… Rails é impressionante. As facilidades que ele traz, a forma como ele acelera o desenvolvimento, a quantidade de coisas que já vêm prontas - tudo isso faz diferença. É um framework que realmente facilita o desenvolvimento, sem sacrificar flexibilidade.

O projeto em si também foi interessante. Trabalhar com regras de negócio fiscais me forçou a pensar em modelagem de dados de forma diferente. Imutabilidade, cálculos determinísticos, validações complexas - tudo isso são conceitos que você não encontra em CRUDs básicos normalmente.

E no final das contas, foi uma experiência de aprendizado valiosa. Não só aprendi Ruby e Rails, mas também aprendi a pensar sobre problemas de forma diferente. E isso, talvez, seja o mais importante.

O projeto está disponível no GitHub: ruby-fiscal-project.

Fora isso… Obrgiado pela atenção, um abraço e até mais!

j