Metas
Dashboards financeiros de várias empresas, cada uma com um acesso diferente pra dar: API, banco, ou nada.
AnonimizadoUma plataforma de consultoria que acompanha indicadores financeiros e operacionais de várias empresas-cliente, de porte médio a grandes multinacionais, em setores diferentes. O nome fica de fora porque autorização nunca foi pedida. Toda restrição, decisão e número neste case reflete o que de fato foi construído.
- rails
- react
- postgresql
- oracle
- devise
O problema e as restrições
O Metas existe pra transformar os próprios números financeiros e operacionais de uma empresa-cliente numa nota única e comparável, mês a mês, em toda área do negócio e em toda empresa de um grupo. O cliente define uma meta e um peso pra cada indicador; uma pessoa ou um sistema reporta o que de fato aconteceu; a plataforma transforma isso numa nota que a gestão consegue comparar entre dezenas de áreas e, num cliente com várias empresas, entre toda subsidiária ao mesmo tempo.
A primeira restrição era que “pegar o número” significa uma coisa diferente pra cada cliente, porque nenhum cliente roda os mesmos sistemas nem concede o mesmo acesso. Alguns clientes têm uma API moderna e não têm problema em entregar uma chave. Alguns rodam software antigo, on-premises, sem API nenhuma, mas permitem uma conta de banco somente leitura atrás de uma VPN. Alguns, por razão regulatória ou de infraestrutura, não abrem porta nenhuma além do que uma pessoa já vê na própria tela. Uma plataforma que só soubesse chamar API simplesmente não conseguiria atender uma fatia relevante dos clientes pra quem esse produto existe.
A segunda restrição era que a organização de um cliente raramente cabe numa lista plana de “empresas”. Uma holding é dona de subsidiárias, uma subsidiária tem as próprias unidades de negócio, e cada nível precisa dos próprios indicadores, da própria meta e do próprio fechamento pro nível acima, enquanto uma única pessoa gerenciando o grupo inteiro precisa ver tudo isso de uma vez, sem trocar de login pra cada entidade.
A terceira restrição era que uma nota alimenta decisão real que a própria gestão do cliente toma sobre desempenho de time e, em alguns clientes, sobre a remuneração desse time, bem além de um número cosmético de dashboard. Errar uma fórmula sutilmente, ou deixar passar sem perceber um número velho ou manualmente maquiado, não só fica feio num gráfico, produz uma decisão real errada lá na frente.
A última restrição era o próprio acesso, no sentido mais literal. Chegar aos números reais de um cliente às vezes significa trabalho de software normal (chamar um endpoint), às vezes significa agir como engenheiro de rede (subir e manter um túnel VPN pra dentro de uma infraestrutura que ninguém do lado do cliente consegue explicar por completo), e às vezes significa agir como arqueólogo de dado (reconstituir o que as próprias telas de um sistema legado de fato mostram, quando o sistema em si não conta)
Arquitetura
A organização de um cliente vive como uma árvore autorreferente de linhas Company: uma holding, suas subsidiárias e as unidades de negócio delas são todas o mesmo tipo de registro, distinguidas só por um campo de categoria e um ponteiro de pai, então o mesmo schema atende um cliente com uma entidade legal só e um cliente com um grupo de quatro níveis, sem nenhum caso especial. Usuários se ligam a uma ou mais empresas por uma tabela de junção, e o acesso de um usuário comum fica estritamente restrito às empresas às quais ele de fato está ligado. Um papel fica inteiramente fora desse escopo: um administrador cross-company que vê a estrutura de todo cliente de uma vez, o papel que um consultor gerenciando várias relações de cliente de fato usa no dia a dia.
Debaixo de toda empresa fica um conjunto de Areas, e debaixo de toda área um conjunto de Indicators, cada um com um peso e uma de cinco fórmulas de pontuação (uma razão crescente simples, uma decrescente, uma variante de economia em despesas, e duas fórmulas de desvio pra estourar ou ficar abaixo de uma meta). O valor mensal de um indicador pode chegar de um de dois jeitos, rastreado por uma única flag is_manual: alguém digita no dashboard na mão, ou é escrito por qualquer mecanismo de integração que aquele cliente específico usa. Os dois caminhos alimentam exatamente o mesmo motor de fórmula lá na frente, então a plataforma nunca precisa saber nem se importar, na hora de pontuar, de onde um número veio originalmente.
Esse segundo caminho, a própria integração, é onde mora a maior parte do custo real de engenharia, e são três mecanismos separados, escolhidos por cliente conforme o que os próprios sistemas daquele cliente de fato permitem que um terceiro faça. Onde um cliente expõe uma API, a integração é uma chamada agendada direta. Onde um cliente permite acesso ao banco mas não tem API, a integração é uma consulta somente leitura rodando por um túnel VPN construído especificamente pra aquele cliente. Onde nenhum dos dois existe, e a própria equipe do cliente só vê número pelas telas do próprio software, a integração é RPA: operando aquele software do jeito que uma pessoa operaria, pra extrair os mesmos números que um operador humano teria digitado na mão de qualquer forma.
Assim que um valor chega, a própria nota de uma área segue uma regra deliberadamente direta: o resultado ponderado só conta alguma coisa se o resultado agregado da área bater 80% da meta; abaixo disso, a nota da área é exatamente zero, em vez de uma fração de um. Esse zero, ou essa nota, sobe pela árvore de empresas do mesmo jeito que a própria estrutura de empresa sobe, então o dashboard de uma holding reflete desempenho real de toda subsidiária debaixo dela, além do que por acaso foi digitado mais recentemente.
Uma meta perdida também não fica só num número seco. Quem é dono de um indicador que ficou abaixo anexa uma justificativa escrita pra aquele mês específico, e pode sustentar com evidência de apoio, um documento, uma captura de tela, uma planilha, enviada e ligada àquele mesmo indicador e mês. Nem a justificativa nem a evidência mudam a nota em si; a fórmula e o piso de 80% rodam exatamente igual dos dois jeitos. O que mudam é o que um revisor vê ao lado de um número vermelho: não só que uma meta foi perdida, mas a explicação específica e datada que o próprio dono da área deu pro motivo, registrada no mesmo mês em que aconteceu em vez de reconstituída de memória depois.
Arquitetura
Decisões
DECISION 01/04 · Um de três caminhos pra entrar, escolhido por cliente
A maturidade e a política de TI de cada cliente varia demais na prática: alguns expõem uma API limpa, alguns só permitem VPN mais uma conta de banco somente leitura, e alguns (um hospital, no caso mais difícil até agora) mal conseguem sustentar isso. Recusar um cliente por não ter o tipo certo de acesso significaria perder exatamente o negócio que esse produto existe pra atender
Custo aceitoTrês caminhos de integração inteiramente separados pra construir, testar e manter funcionando, cada um com seu próprio jeito de quebrar: limite de taxa e mudança de schema no caminho de API, manutenção de túnel e credencial no caminho de VPN, fragilidade a qualquer mudança de tela no caminho de RPA
DECISION 02/04 · Hierarquia de empresa como árvore autorreferente
Organogramas de cliente de verdade não compartilham um formato só: alguns clientes são uma entidade legal só, outros são estruturas de holding com três ou quatro níveis de profundidade. Um número fixo de níveis embutido no código quebra na primeira vez que a estrutura de um cliente não couber nele
Custo aceitoQualquer consulta que precise de 'toda empresa abaixo dessa' tem que percorrer a árvore recursivamente em vez de rodar um join simples, e nada no nível do banco impede uma empresa de acidentalmente virar sua própria ancestral
DECISION 03/04 · Nota abaixo do piso da meta é zero
Essa nota alimenta decisão de avaliação e de remuneração que a própria gestão do cliente toma sobre o time daquela área. Uma escala graduada borra exatamente a linha que essa decisão precisa; o cliente escolheu um piso explícito em vez disso
Custo aceitoUma área a 79% da meta pontua igual a uma a 10%, o que pode parecer implacável pra um time que errou por muito pouco. Esse é um trade-off deliberado do próprio cliente
DECISION 04/04 · Valor manual e valor integrado dividem um pipeline só
Do ponto de vista da fórmula de pontuação, o valor de um indicador é o valor de um indicador, não importa de onde veio. Uma tabela só significa um motor de fórmula só, uma validação de peso só, e um fechamento mensal só, em vez de manter a mesma lógica duas vezes e as duas cópias sincronizadas pra sempre
Custo aceitoUma escrita ruim de integração parece idêntica a uma entrada manual ruim no nível do banco. Perceber que o feed automatizado de um cliente parou de atualizar depende inteiramente de alguém de fato revisar os números; o schema sozinho não tem como impor isso
Invariantes
Um grupo de indicadores irmãos cujos pesos não somam exatamente 100 é detectado e exposto, nunca aceito em silêncio
Garantido por
User#have_goals_incompleteds? agrupa um conjunto de indicadores por nível, soma o peso de cada grupo e sinaliza qualquer grupo que não feche em exatamente 100O status de admin ou gerente de um papel é calculado pela posição dele na hierarquia, nunca guardado como uma flag independente que poderia dessincronizar
Garantido por
Role#admin? e Role#manager? leem parent_id e label no momento da chamada (um papel raiz que não é 'analista' é gerente; um papel cujo pai é 'admin' é ele mesmo admin) em vez de guardar a resposta em cache em algum lugarUm usuário comum, com escopo de empresa, nunca vê uma empresa pra qual não tem uma linha ativa em company_users
Garantido por
User#company e User#companies resolvem estritamente pelo join com company_users, filtrado por active: true; a única exceção deliberada é o escopo separado saga_admin, construído especificamente pra cruzar essa fronteiraA nota de uma área é o resultado ponderado completo ou exatamente zero, nunca um valor intermediário quando a área fica abaixo da meta
Garantido por
Area#calcular_indicators só calcula total_nota quando total_real bate o piso de premissa de 0.8; caso contrário devolve 0 tanto pra total_nota quanto pra total_geralUm indicador que é filho de outro indicador nunca é obrigado a declarar o próprio peso
Garantido por
validates_presence_of :weight, unless: :child? no modelo Indicator, app/models/indicator.rb
O que quebrou
- Sintoma
- A integração com o hospital, a mais difícil construída até agora, precisou de uma conexão direta com o banco por um túnel VPN IPsec em vez da chamada de API de costume: a própria equipe de rede do hospital nunca conseguiu abrir uma porta pra expor uma API, então um caminho de leitura direto no banco Oracle deles foi a única opção que sobrou
- Causa raiz
- O hospital não tinha DBA nem engenheiro de rede próprio, então ninguém lá conseguia de fato documentar como a rede ou o banco estavam montados; os dois precisaram ser mapeados de fora, do zero, sem ajuda de quem originalmente construiu os sistemas deles. Pior, assim que a conexão subiu os números não batiam: o software do próprio hospital calculava vários valores na hora, dentro de views do banco, e nunca persistia o resultado calculado como uma coluna comum. Consultar as tabelas de baixo direto, o único caminho que uma conexão externa somente leitura tem, devolvia as entradas brutas desses cálculos em vez dos valores que as telas do próprio hospital de fato mostravam
- Correção
- O cálculo de cada view afetada foi reconstituído na mão e reescrito como uma query equivalente, conferida campo a campo contra o que o sistema do hospital mostrava pro mesmo período, uma query reescrita e reconferida por dia durante cerca de um mês até toda saída bater exatamente
- Prevenção
- A topologia de rede e a estrutura do banco, as duas sem nenhuma documentação do lado do hospital no início, agora estão completamente mapeadas e registradas deste lado, pra essa mesma integração não precisar ser reconstituída do zero de novo se algum dia precisar mudar
Resultados
Stack completa
Backend
- Ruby on Rails
- Devise (autenticação)
- Pundit (políticas de autorização)
Frontend
- React
- Chakra UI
- Redux Toolkit
- ApexCharts / Highcharts
Dados
- PostgreSQL (banco da aplicação)
- Oracle (somente leitura, via integração com cliente)
Infra
- Túneis VPN IPsec (por cliente, sob demanda)
- RPA contra o software do próprio cliente
Tem interesse em um projeto como esse? Entre em contato.
Entrar em contato