Mostrando postagens com marcador Programação. Mostrar todas as postagens
Mostrando postagens com marcador Programação. Mostrar todas as postagens

sábado, 3 de janeiro de 2026

Padrão Brasileiro para Cálculos Financeiros - Bancos, Fintechs e afins em Java

        Em sistemas financeiros, precisão é uma exigência legal, contábil e auditável. Os tipos primitivos como double e float utilizam representação binária, o que gera erros de precisão. Então o padrão para se trabalhar com valores monetários é o BigDecimal, pois ele traz: precisão decimal exata, controle explícito de escala e controle explícito de arredondamento.

        O padrão para lidar com valores é:

  • Uso de BigDecimal
  • Escala fixa de 4 casas decimais (mesmo que só exiba duas usa 4 nos cálculos)
  • Arredondamento HALF_EVEN (arredondamento bancário)
  • Usar numeric no Banco de Dados (Numeric(19,4))

        Porém para se trabalhar corretamente com BigDecimal é preciso obedecer algumas regras:

  • Nunca iniciar o construtor a partir de um double ou float:

new BigDecimal(10.25);  // errado, faz o valor entrar nos erros de cálculo de repres. binária

  • Existem duas formas para inicializar:

new BigDecimal("10.25");     // A partir de String

BigDecimal.valueOf(10.25);  // Usando valueOf

// criando valores corretamente

BigDecimal valor = new BigDecimal("123.456789")

        .setScale(4, RoundingMode.HALF_EVEN);


  • O arredondamento é o RoundingMode.HALF_EVEN

Exemplo:

new BigDecimal("2.345").setScale(2, RoundingMode.HALF_EVEN); // 2.34 new BigDecimal("2.355").setScale(2, RoundingMode.HALF_EVEN); // 2.36

  • Não usar o equals para fazer comparações entre os objetos. O equals vai comparar as instancias.
    Use o "compareTo".
new BigDecimal("10.0").equals(new BigDecimal("10.00")); // false

new BigDecimal("10.0").compareTo(new BigDecimal("10.0"))

Retorna:         -1 → menor que         0 → igual         1 → maior que

        E a partir daí é usar o que a classe traz de métodos:

Soma:

BigDecimal resultado = valor1.add(valor2).setScale(4, RoundingMode.HALF_EVEN);

Subtração:

BigDecimal resultado = valor1.subtract(valor2).setScale(4, RoundingMode.HALF_EVEN);

Multiplicação:

BigDecimal resultado = valor1.multiply(valor2).setScale(4, RoundingMode.HALF_EVEN);

Divisão:

BigDecimal parcela = valor1.divide( valor2, 4, // escala RoundingMode.HALF_EVEN );


        Um exemplo de uma classe para trabalhar com dinheiro:


public class Money { private final BigDecimal value; public Money(BigDecimal value) { this.value = value.setScale(4, RoundingMode.HALF_EVEN); } public static Money of(String value) { return new Money(new BigDecimal(value)); } public Money add(Money other) { return new Money(this.value.add(other.value)); } public Money subtract(Money other) { return new Money(this.value.subtract(other.value)); } public Money multiply(BigDecimal factor) { return new Money(this.value.multiply(factor)); } public BigDecimal getValue() { return value; } }







domingo, 4 de agosto de 2024

Metodologia Twelve-Factor

         A metodologia Twelve-Factor App é um conjunto de 12 regras para manter uma aplicação de forma eficaz, sendo elas:


  1. Codebase (Código-fonte único): Uma aplicação deve ser armazenada em um único repositório de código, com múltiplas implantações derivadas do mesmo código-base.

  2. Dependencies (Dependências explícitas): Todas as dependências da aplicação, incluindo bibliotecas e ferramentas de sistema, devem ser declaradas explicitamente e gerenciadas de forma isolada.

  3. Config (Configurações): As configurações da aplicação devem ser armazenadas em variáveis de ambiente, não no código-fonte, para permitir a configuração flexível em diferentes ambientes.

  4. Backing Services (Serviços de Back-end): Os serviços de back-end, como bancos de dados e filas, devem ser tratados como recursos externos, acessíveis por meio de interfaces padrão.

  5. Build, Release, Run (Construir, entregar, executar): O processo de build, entrega e execução da aplicação deve ser separado em etapas distintas, com cada etapa tendo suas próprias responsabilidades e garantindo a consistência entre ambientes.

  6. Processes (Processos): As aplicações devem ser executadas como processos independentes, leves e sem estado (Stateless), para facilitar a escalabilidade e a resiliência.

  7. Port Binding (Ligação de porta): As aplicações devem ser autocontidas e expor serviços por meio de portas, para que possam ser facilmente conectados a outras aplicações e serviços.

  8. Concurrency (Concorrência): As aplicações devem escalar horizontalmente, adicionando instâncias concorrentes para lidar com cargas de trabalho aumentadas.

  9. Disposability (Descartabilidade): As aplicações devem ser fáceis de iniciar e parar rapidamente, sem impacto para outras partes do sistema, para facilitar o deploy e a atualização contínua.

  10. Dev/Prod Parity (Paridade dev/prod): Os ambientes de desenvolvimento, testes e produção devem ser o mais semelhantes possível, para minimizar diferenças e evitar problemas de compatibilidade.

  11. Logs (Registros): As aplicações devem produzir logs estruturados e acessíveis por meio de interfaces padronizadas, para facilitar a depuração e o monitoramento.

  12. Admin Processes (Processos de administração): As tarefas administrativas, como migrações de banco de dados e limpeza de caches, devem ser executadas como processos únicos e rastreáveis.

quinta-feira, 2 de junho de 2022

Matriz de Rastreabilidade - Mapeando os impactos das alterações do sistema

 

Nesse artigo eu vou falar sobre Matriz de Rastreabilidade. Essa matriz é um gráfico que mostra a dependência entre partes do sistema.

 

         O objetivo é ter uma ferramenta visual de rápida identificação de áreas que precisam ser testadas para evitar erros em produção devido à falta de testes suficientes após alterações que parecem pontuais, mas na verdade tem um impacto maior.

 

         A matriz possui 2 eixos: Manutenção e Impacto; e é construída colocando o nome dos módulos do sistema (ou partes importantes que se deseja controlar, como por exemplo, um método que é muito usado) em ambos os eixos, e depois marcando os pontos onde há dependências.

 

         Aqui abaixo tem um exemplo:

 


 

         A matriz de rastreabilidade pode ser feita por qualquer pessoa da equipe, mas um DEV tem maior conhecimento dessas dependências e pode dar uma grande ajuda.

 

Essa matriz pode ser bem grande dependendo do tamanho do sistema e do nível de detalhes, que pode mudar de acordo com a necessidade, por exemplo, pode ser mapeado módulos mas também classes ou métodos muito utilizados.

 

Deve ser atualizada sempre que novas dependências forem encontradas e estar disponível para a equipe de testes e homologação calcular o que testar e ter uma ideia do tempo dos testes.

terça-feira, 1 de junho de 2021

Substituido Entidades em um projeto Java

Este post é um pouco diferente dos demais já que vou tratar mais de um passo-a-passo do que fazer ao invés de trazer código.

 

Não sei se você já passou pela necessidade de precisar trocar uma entidade por outra em todo o sistema, mas quando isso acontece o trabalho é enorme. 

 

Entidades são as classes javas que são mapeadas para o banco de dados. Normalmente, aplicando as boas práticas e padrões de projeto em um determinado sistema, temos uma entidade para uma tabela, claro que podemos ter VOs (ou DTOs como tbm são conhecidos), mas falo de uma classe que vai ser a responsável pelo objeto inserido no banco, atualizado, apagado, enfim, o bean persistente. Em um projeto que trabalhei, não sei porque fizeram isso (falta de um arquiteto ou diria mesmo de uma pessoa com boa noção de O.O.), essa regra básica não foi seguida, e tinhamos 3, 4 ou até 6 classes diferentes apontando para a mesma tabela, com basicamente as mesmas propriedades, mudava apenas uma ou outra, e que ainda tinham sua própria classe de negócio e seu próprio controller. Ou seja, duplicação (ou melhor, multiplacação) de código enorme, regras espalhadas, bagunça total.

Nesse cenário caótico, eu precisei juntar as várias cópias de duas entidades principais, uma que tinha 6 classes e outra que tinha 4, e deixar apenas uma de cada. Não foi feito a junção do controller nem das classes de negócio devido ao tamanho, complexidade e tempo, mas ter apenas um objeto trafegando entre elas facilitou bastante as coisas. E pra fazer isso nesse sistema que é grande, eu usei o seguinte passo a passo:

1.     Juntar todas as propriedades em uma unica entidade removendo as repetidas mas não apagar nesse momento as classes duplicadas.

 

2.     Para cada uma das classes que serão eliminadas faça o seguinte: use o Search do eclipse na opção File Search seguindo os padrões abaixo, ao retornar resultados, clique com botão direito na aba que abriu de mesmo nome da funcionalidade (Search) e escolha a opção substituir todos, e vá substituindo pela nova classe. 

 

1.     Marque a opção Case Sensitive pra todas as buscas

 

2.     Procure os imports e substitua pelo import da classe que vai ficar

 

3.     Procure o nome da classe com um parêntese no início – com ou sem espaço dependendo do padrão de codificação usado no projeto entre o nome e os parênteses. Ex: (Usuario  

 

4.     Procure o nome da classe com o parentese no final – com ou sem espaço dependendo do padrão de codificação usado no projeto entre o nome e os parênteses. Ex: Usuario( 

 

5.     Procure o nome da classe com entre parênteses – com ou sem espaço dependendo do padrão do projeto entre o nome e os parênteses. Ex: (Usuario)

 

6.    Procure o nome da classe entre os sinais de menor que ‘<’  e maior que ‘>’. Ex: <Usuario>

 

7.    Procure o nome da classe entre espaços. Ex: Usuario

 

8.    Procure o nome da classe seguido do .class. Ex: Usuario.class

 

Nesse ponto, podem haver alguns erros apontados na aba Problems do Eclipse, geralmente são alguns imports que são necessários corrigir na mão Pronto, agora apague a(s) classe(s) que não deseja mais.

quarta-feira, 19 de maio de 2021

Threads - Exemplo de uso para tarefas pesadas: synchronized, wait, notify e mais...

      Olá, nesse post vou mostrar um estudo de caso de como separar uma tarefa pesada – como um processamento de dados que envolva várias entidades e regras de negócio – e dar algumas dicas de como trabalhar essa questão com threads.

 

      Primeiro vamos ao caso:

 

     Temos uma tarefa que consiste em processar um grande volume de dados, (poderia ser uma folha de pagamento, contas de serviços de clientes, etc). Essa atividade hoje é feita da seguinte forma:

 

1.    Em um laço for uma consulta paginada retorna de 1000 em 1000 registros usados no início do processo (poderia ser o id dos colaboradores no caso da folha de pagamento, dos clientes no caso das contas de clientes, etc).

2.    Para cada página, temos um outro laço for que percorre cada um dos 1000 registros e inicia o processo de consultar os demais dados necessários, realizar processamento (todos os descontos, acréscimos, multas, juros, etc...), e gravar o resultado.

3.    Vai para próxima página, busca mais 1000 registros e continua.

 

Veja o código (esse código não compila, é apenas um exemplo didático. Mais à frente o código que uso threads está OK, compila e roda perfeitamente):

 

/*representa a classe que da início ao nosso processo*/

public class SincronizacaoThreads {

 

    public static void main( String[] args ) {

 

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        classeDeNegocio.executaTarefaPesada();

    }

 

}

 

public class ClasseDeNegocio {

 

    public void executaTarefaPesada() {

 

        /*

         * Digamos que eu tivesse um grande processamento de dados muito pesado para fazer,

         * Nesse ponto eu prepararia uma consulta paginada para buscar os dados, 1k por pagina.

         * Esse primeiro for representa essa paginação, aqui simulo 3 páginas.

         */

        for ( int j = 0; j < 3; j++ ) {

 

            // aqui buscaríamos os 1000 registros no banco e preencheríamos nossa lista

            System.out.println( "Tarefa pesada rodada número " + j );

 

            // Aqui percorreríamos todos os 1000 registros

            for ( int i = 0; i < lista.lenght; i++ ) {

 

                /*

                 * executando todo processo pesado, item a item...

                 * chamando outras classes de negócio necessárias etc...

                 */

            }

        }

    }

}

 

Vamos otimizar esse processo usando threads e ver algumas questões importantes na construção de soluções usando essa abordagem. A imagem abaixo mostra um rascunho de como era e como ficou o código:

 


Agora vamos ao código. São duas classes apenas, você pode copiar e colocar o código para rodar na sua máquina para facilitar o entendimento (elas estão funcionando rsrsrs):

 

/*representa a classe que da início ao nosso processo*/

public class SincronizacaoThreads {

 

    public static void main( String[] args ) {

 

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        classeDeNegocio.executaTarefaPesada();

    }

 

}

 

public class ClasseDeNegocio {

 

    // esse contador serve para validar se posso mudar de página

    private int contadorThreadsFilhasAtivas = 0;

 

    public void executaTarefaPesada() {

 

        /*

         * Digamos que eu tivesse um grande processamento de dados muito pesado para fazer,

         * Nesse ponto eu prepararia uma consulta paginada para buscar os dados, 1k por página.

         * Esse primeiro for representa essa paginação, aqui simulo 3 páginas.

         */

        for ( int j = 0; j < 3; j++ ) {

 

            // mostro o múmero da pagina (0 a 2)

            System.out.println( "Tarefa pesada rodada número " + j );

 

            // Começando o trabalho - Dividindo a tarefa em 10 threads.

            for ( int i = 0; i < 10; i++ ) {

 

                // aqui eu criaria a sublista com 100 elementos

 

                // incremento o contador de threads filhas

                contadorThreadsFilhasAtivas++;

 

                new Thread( new Runnable() {

 

                    @Override

                    public void run() {

 

                        // aqui passaria a sublista com 100 elementos (1k / 10) para o processaDados

                        processaDados();

                    }

                }, "thread " + i ).start();

            }

 

            /*

             * só posso continuar a paginação e buscar mais 1k de dados após todas as 10 threads

             * auxiliares acaberem seu serviço, para evitar sobrecarga no sistema e no banco, já

             * estamos falando de uma tarefa bastante pesada de processamento de dados.

             */

            synchronized ( this ) {

                try {

                    System.out.println( "Esperando a execução das threads filhas para continuar" );

                    while ( contadorThreadsFilhasAtivas != 0 ) {

                        this.wait();

                    }

                } catch ( InterruptedException e ) {

                    e.printStackTrace();

                }

            }

 

            System.out.println( "Fim das threads filhas da página " + j + " e agora posso continuar" );

            System.out.println();

        }

    }

 

    public void processaDados() {

 

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no processaDados" );

 

        try {

            /*

             * simula o tempo de execução do método.

             * o tempo dos sleep simula o processamento dos 100 dados que o método receberia

             * executando todo processo pesado, item a item...

             * chamando outras classes de negócio necessárias etc...

             */

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

 

        System.out.println( nome + " terminando execução do processaDados" );

        System.out.println( nome + " saindo do processaDados" );

 

        synchronized ( this ) {

            // decremento o contador de threads filhas antes de sair

            contadorThreadsFilhasAtivas--;

            this.notify();

        }

    }

}

 

Vamos analisar o código e algumas dicas.

 

A classe ClasseDeNegocio tem apenas 2 métodos: executaTarefaPesada() e processaDados(). Se fosse um código real, o método processaDados() receberia um lista com 100 objetos para ele processar (já que minha paginação pega 1.000 registros e divido em 10 threads no laço).

 

Antes de usarmos threads, o método executaTarefaPesada fazia tudo sozinho. Agora, ele apenas busca os dados e divide para as threads fazerem o trabalho. Você pode se perguntar: por que cada thread não busca seus próprios dados? Porque facilmente as threads poderiam buscar os mesmos dados e isso poderia ocasionar sérios problemas de duplicação de dados entre outras coisas.

 

Fizemos uso de wait() após disparamos as threads que vão fazer o trabalho pesado e deixamos ela em espera até todas as threads terminarem, e então seguimos para a próxima página. Esse controle é feito através da variável contadorThreadsFilhasAtivas que criamos na ClasseDeNegocio e começa com valor 0, porém a cada thread que lançamos esse valor é acrescido de 1, e no final do método processaDados() ela é decrescida em 1.

 

Logo que decrescemos o valor, usamos o notify() método processaDados() que serve para dizer a thread que deixamos parada com o wait() que ela pode continuar seu trabalho. Como o código que chama o wait() está dentro de um laço, ele vai executar o laço mais uma vez e checar se o valor da variável contadorThreadsFilhasAtivas já chegou a 0, caso contrário, ele executa outra chamada a wait() e entra em espera novamente. Isso ocorre até que todas as threads tenha terminado seu trabalho, e o código segue seu fluxo.

 

Quando usamos o wait(), notify() ou notifyAll(), nem sempre ele vai ser chamado a partir do “this”, mas pode ser de um parâmetro passado no método, etc. O ponto de aplicação do wait(), notify() ou notifyAll() é o objeto ou classe de negócio, enfim, aquilo que é de fato acessado de forma paralela. Ao dar um wait() em um objeto, faz-se necessário um notify() ou notifyAll() para o mesmo objeto, afim de que ele volte a execução, caso contrário ficará fadado a espera eterna. Wait(), notify() ou notifyAll() só podem ser usados dentro de um bloco synchronized.

 

Usamos também o synchronized. Ele pode ser usado tanto em blocos específicos de código, como foi o nosso caso, quanto na declaração do método. Qual a diferença? Bem, quando declaramos algo como sincronizado, isso significa que será permitido apenas um acesso por vez aquele trecho de código. Isso é muito ruim, pois cria gargalos... pra quê usar várias threads se eu deixar os métodos com 1 acesso por vez? Não faz sentido! Mas, as vezes é necessário. E isso vai depender de cada regra de negócio. No nosso caso, apenas o trecho que controla a variável contadorThreadsFilhasAtivas  teve essa necessidade.

 

Então, onde usar synchronized? Onde houver chance de duplicação de dados ou erro de calculo em variáveis compartilhadas entre threads como era o caso do nosso contadorThreadsFilhasAtivas ou coisas do tipo. Sincronizar o método ou um trecho? Sempre o menor possível, mantendo integridade e consistência para não perder muita performance.

 

O controle da execução de blocos sincronizados é válido para cada instância da classe. Ou seja, mesmo que anote um método com synchronized, se eu chama-lo a partir de duas instancias distintas, ele será executado ao mesmo tempo.

 

Acredito que para esse post era isso. Espero que tenha ajudado.