Calculadora de financiamento imobiliário da BuildSystems
| Data | 2024-07-30 |
|---|---|
| Autor | BuildSystems |
| Cliente | BuildSystems |
| Diretor | Martin Bittmann |
| Gerente | Julia Dorn |
| Equipe | Daniel Nunes Locatelli, Daniel Dieren |
| Local | Online |
| Link | https://app.buildsystems.de |
Recursos
- Estimativa de preço de um edifício: O aplicativo estima o custo de uma construção ou reforma com base em dados disponíveis publicamente da Arge e.V.
- Simulação de empréstimo: Simulação precisa de empréstimos com base na estimativa de preço e eficiência energética de um edifício.
- Métricas de energia: Influenciando as possibilidades de subsídio e empréstimo.
- Segurança de dados: Garantindo que todos os dados do usuário estejam seguros e sigam os regulamentos da União Europeia.
- Design responsivo: O aplicativo funciona perfeitamente em todos os dispositivos.
Stack de tecnologia
- GitHub: Para o repositório Git.
- Angular: Um framework JavaScript moderno apoiado pelo Google e usado para aplicações de grande escala.
- ng2-charts: Wrapper Angular para a biblioteca Chart.js. É usado para criar gráficos responsivos e interativos.
- Cloudflare: Provedor de hospedagem para garantir confiabilidade e escalabilidade, nenhum investimento inicial é necessário.
Por que desenvolvemos este app?
A Alemanha é conhecida por impulsionar tecnologias verdes, como painéis solares e turbinas eólicas, por meio de subsídios públicos. Mas você sabia que também existem muitos subsídios para construções energeticamente eficientes? Embora esses subsídios sejam atrativos, navegar pela burocracia pode ser incrivelmente desafiador. Este aplicativo desenvolvido pela BuildSystems facilita a simulação de um empréstimo do banco nacional KfW. Ele simplifica o processo ao oferecer uma interface amigável, permitindo que incorporadores imobiliários e proprietários de imóveis entendam suas opções financeiras de forma rápida e fácil.
Processo de Desenvolvimento
O desenvolvimento do aplicativo aconteceu em três fases principais: Planejamento e Design, Desenvolvimento Frontend, e Testes e Garantia de Qualidade.
Planejamento e Design
Para começar o projeto, toda a equipe definiu os requisitos e variáveis para a lógica do aplicativo. Uma vez que isso foi definido, esbocei a arquitetura do frontend e a UI/UX.
Lógica do aplicativo
Daniel Dieren desenvolveu a lógica do aplicativo no Excel. Minha função era revisá-la, fazer engenharia reversa das fórmulas para garantir que tudo estivesse correto e sugerir melhorias. Esta etapa se sobrepôs a todo o processo de desenvolvimento de software porque, conforme avançávamos, percebíamos que podíamos adicionar mais informações relevantes. Criei uma documentação simples no Notion a partir do arquivo do Excel para entender cada fórmula completamente e facilitar a transferência para o TypeScript posteriormente.
Arquitetura do Frontend
Como a equipe já usa o Figma, decidi permanecer dentro desse ecossistema. Então, usei o FigJam para esboçar um diagrama inicial de arquitetura de software, pensando em quais componentes seriam necessários e a relação entre eles.
%%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB
R(Routes) --> N & S & PO & PR & SE
subgraph S[Sanierung Component]
SF
SS
SOUT
SSV
end
subgraph SOUT[Output]
SD
SCH
end
SF(Sanierung Forms)-->SS(Service)
SS-->SD(Dashboard)
SS-->SCH(Charts)
SS-->SSV(Save)
subgraph N[Neubau Component]
NF
NS
NOUT
NSV
end
subgraph NOUT[Output]
ND
NCH
end
NF(Neubau Forms)-->NS(Service)
NS-->ND(Dashboard)
NS-->NCH(Charts)
NS-->NSV(Save)
subgraph PR[Profile Component]
CP(Change password)
DC(Delete Account)
end
subgraph SE[Settings Component]
CT(Change Theme)
CL(Change Language)
end
NL-->NLD
SL-->SLD
NSV-->DB[(Database)]
SSV-->DB
DB-->SL
DB-->NL
subgraph PO[Portfolio Component]
NL(Neubau List)
SL(Sanierung List)
SLD(Load)
NLD(Load)
end
Design de UI e UX
Conceitualmente, minha abordagem para o design foi criar um dashboard completo com todas as variáveis acessíveis pelo usuário, sem muita abstração. Em uma fase posterior, pretendemos ter outro fluxo de usuário onde os usuários tenham um passo a passo para simular os empréstimos. Para criar protótipos, usei o Figma, que foi uma experiência de design bem agradável. Simular comportamentos de mouse over, mouse in, mouse out é possível. Além disso, o plano pago facilita a cópia de estilos CSS e SVGs com o Modo Dev. Mas mesmo com o plano gratuito, é muito fácil exportar SVGs.

Construindo a Interface do Usuário
Este projeto marcou minha metamorfose em um desenvolvedor de software completo. Isso exigiu que eu aprendesse Angular, um framework JavaScript, e sua estrutura altamente opinativa, que era perfeita para o meu caso.
Por que escolhemos o Angular?
Muitas pessoas acreditam que o Angular é um dos frameworks mais difíceis para desenvolvimento web. Se compararmos com React ou Vue, por exemplo, parece mais difícil no começo. Mas a verdade é que, como o Angular tem muitos recursos integrados (o que significa que é “opinativo”), ele elimina a necessidade de tomar muitas decisões mais tarde. Então, eu pude pular a parte mais dolorosa para quem aprende sozinho: a exaustão mental que pode vir de fazer muitas escolhas. E acredite, fadiga de decisão é uma coisa real! Além disso, o Angular usa uma linguagem de programação chamada TypeScript. Pense nisso como JavaScript com um verificador de código que ajuda a detectar erros antes que eles se tornem problemas. Como eu era a única pessoa escrevendo o código, o TypeScript foi uma grande rede de segurança. Na verdade, eu só confiaria em mim mesmo para desenvolver este aplicativo com as proteções que o TypeScript cria. Lembre-se de que eu não tinha nenhum revisor de código; eu era uma equipe de um no lado do software. Outro motivo para escolher o Angular é sua reputação de confiabilidade e facilidade de manutenção, especialmente em aplicações de grande escala. Ele é apoiado pelo Google, que já construiu mais de 2.600 soluções com ele, então está claro que ele pode lidar com projetos complexos e será mantido por um longo tempo. Com minha formação em arquitetura e engenharia, entendo o quão rápido as coisas podem se tornar complexas neste campo também, e embora a Calculadora KfW pareça simples no início, ela inclui cerca de duas centenas de variáveis e mais de cem funções em sua primeira versão. Dado o objetivo da BuildSystems de criar um aplicativo escalável que evoluirá para uma ferramenta abrangente de planejamento inicial, os pontos fortes do Angular foram perfeitos para este projeto.
Ferramentas de IA como copiloto
Também vale a pena mencionar a importância das ferramentas de IA como um copiloto. O ChatGPT desempenhou um papel crucial na conversão de fórmulas do Excel em código TypeScript. Mas devo dizer que, para recursos de ponta, essas ferramentas de IA não deram boas respostas, porque obviamente elas ainda não têm os dados de treinamento disponíveis.
Processo
Durante o processo de desenvolvimento, tentei criar um único componente que acomodasse tanto a calculadora de Nova Construção (Neubau) quanto a de Renovação (Sanierung), tornando o código menos repetitivo seguindo o princípio de DRY (don’t repeat yourself). Isso, no entanto, tornou o componente muito complexo porque havia muitas variáveis e requisitos exclusivos para cada calculadora. Então, no final, decidi dividi-lo em dois componentes. Embora haja algum código redundante, isso adicionou velocidade ao desenvolvimento.
%%{ init: { 'flowchart': { 'curve': 'base' } } }%%
flowchart TB
NC(Neubau Component) --> NPFC(Projekt Form Component) & NDFC(Darlehen Form Component)
subgraph NPF[Projekt Form]
direction TB
NPFC --> NPFS(Projekt Form Service)
end
subgraph NDF[Darlehen Form]
direction TB
NDFC --> NDFS(Darlehen Form Service)
end
NDFS --> NS(Neubau Service)
NPFS --> NS
NS --> NPD(Projekt Dashboard)
NS-->NPCHC(Charts Component)
NPCHC-->NCHG(Gesamtkosten Chart)
NPCHC-->NCHG2(Gesamtkosten m² Chart)
NPCHC-->NCHE(Einheitskosten Chart)
subgraph NPCH[Charts]
direction TB
NPCHC
NCHG
NCHG2
NCHE
end
subgraph NPOUT[Output Projekt]
direction TB
NPD
NPCH
end
NS-->NDD(Darlehen Dashboard)
NS-->NDCHC(Charts Component)
NDCHC-->NCHA(Annuitäten Chart)
NDCHC-->NCHF(Finanzierungskosten Chart)
NDCHC-->NCHT(Tilgung Chart)
subgraph NDOUT[Output Darlehen]
direction TB
NDD
NDCH
end
subgraph NDCH[Charts]
direction TB
NDCHC
NCHA
NCHF
NCHT
end
NS-->NSV(Save Option)
NPF:::paddingNPF
NDF:::paddingNDF
NPCH:::paddingNPCH
NDCH:::paddingNDCH
NDOUT:::paddingNDOUT
classDef paddingNDCH padding-right:34em;
classDef paddingNPCH padding-right:37em;
classDef paddingNPF padding-right:9em;
classDef paddingNDF padding-right:9em;
As estratégias usadas para implementar os recursos também variaram ao longo do desenvolvimento porque, à medida que eu progredia, aprendi maneiras novas e aprimoradas de atingir o mesmo resultado. Por exemplo, a implementação dos formulários já mudou duas vezes. Na primeira vez, decidi refatorar todo o código para garantir que tudo fosse homogêneo e tivesse um código mais claro. Isso, no entanto, acabou sendo uma decisão ruim de gerenciamento de produto, porque os recursos já estavam funcionando e, embora o código fosse um pouco confuso, alterar os internos não afetaria o usuário final de forma alguma. Então, para a segunda mudança, que está acontecendo para a segunda versão do aplicativo, não refatorarei o resto do código. Se você quiser saber mais sobre como estou implementando os formulários atualmente, confira este artigo de Zoaib Khan:
Testes e Garantia de Qualidade
Eu também mergulhei fundo no tópico de testes unitários usando a ferramenta padrão Karma. Esse tipo de teste verifica pequenas partes (unidades) do software para garantir que cada uma funcione corretamente por si só. Infelizmente, porém, eu só consegui aprender isso em um estágio tardio, o que significou que tive que refatorar o código para permitir que os testes unitários funcionassem. Além disso, se eu tivesse que começar de novo, eu concentraria minhas energias em testes de ponta a ponta (E2E) usando Cypress. Este teste verifica todo o sistema simulando a interação do usuário, garantindo que os inputs e outputs correspondam à nossa devida diligência.
Implantação
Inicialmente, implantamos o aplicativo no Netlify porque eles têm uma experiência supersuave. O modelo de negócios deles é “pague conforme você escala”, sem custos iniciais. Além disso, é uma solução de implantação sem código; você apenas conecta seu repositório GitHub e eles fazem o resto! No entanto, alguns usuários do Netlify começaram a relatar como passaram do plano gratuito para serem cobrados dezenas de milhares de dólares, ou até mesmo US$ 104 mil em um mês. Tudo por causa de um ataque DDOS que poderia acontecer com qualquer um. Como o Netlify não tinha um mecanismo de prevenção de ataques DDOS, decidimos mudar para o Cloudflare. O Cloudflare é semelhante ao Netlify. Mesmo modelo de negócios e implantação automatizada usando o GitHub. No entanto, ele tem um sistema anti-bot mais robusto. A implantação é automática e bem simples:
%%{ init: { 'flowchart': { 'curve': 'base' } } }%%
flowchart LR
NG(Angular) --push--> MA(main branch) --> CP(CF pages production) --> PA(https://app.buildsystems.de)
NG --push--> DV(development branch) --> CD(CF pages development) --> DA(https://branchname.pages.dev)
subgraph VS[VS Code]
NG
end
subgraph GH[GitHub]
MA
DV
end
subgraph CF[Cloudflare]
CP
CD
end
Desenvolvimento do aplicativo: principais insights e estratégias
- Segurança em primeiro lugar: Priorize a segurança dos dados desde o início para evitar problemas de conformidade mais tarde.
- Planejamento antecipado: Invista tempo no planejamento e entenda os requisitos necessários antes de iniciar o desenvolvimento.
- Ferramentas de IA: Utilize ferramentas de IA para designs iniciais para gerar rapidamente interfaces de usuário; use copilotos de código, mesmo que seja apenas o ChatGPT.
- Flexibilidade: Esteja ciente de que o aplicativo evoluirá, portanto, não siga estritamente o princípio DRY.
- Seleção de framework: Escolha um framework que atenda às necessidades do seu projeto e continue com ele. Normalmente, o melhor framework é aquele que você já conhece.
- Teste contínuo: Aposte seus esforços de teste em testes de ponta a ponta. Um dos principais desafios era tornar o aplicativo “snappy”; em outras palavras, enquanto o usuário move um controle deslizante, todos os valores e gráficos são atualizados em tempo real. Além disso, fomos cautelosos com os dados dos usuários por causa das regulamentações super restritivas da União Europeia. A primeira decisão foi evitar cálculos no servidor completamente. O aplicativo inteiro funciona apenas do lado do cliente, o que significa que, uma vez carregado, ele não precisa enviar dados para lugar nenhum; o cálculo acontece diretamente no dispositivo. Isso significa que também não tivemos que nos preocupar com o armazenamento de dados para a primeira versão. Projetar o aplicativo do zero foi uma experiência valiosa, mas agora que entendo o processo, eu começaria o design da interface com um assistente de IA. Ferramentas como Galileo AI ou Rendition Create podem ajudar a começar com uma interface de aplicativo agradável gerada a partir de prompts (Texto para UI). Começar com um rascunho de UI é sempre mais rápido, mesmo que o rascunho mude drasticamente.
Próximos passos
No momento, estamos trabalhando na segunda versão do aplicativo. A ideia é trazer outra calculadora e alguns outros recursos, como salvar um projeto e comparar dois projetos. Usaremos Supabase para armazenar os dados.
erDiagram
auth_users {
uuid id
}
neubau_projects {
bigint id
text title
uuid created_by
timestamp created_at
uuid owned_by
uuid last_edited_by
timestamp last_edited_at
text other_project_values
}
sanierung_projects {
bigint id
text title
uuid created_by
timestamp created_at
uuid owned_by
uuid last_edited_by
timestamp last_edited_at
text other_project_values
}
einzelmassnahmen_projects {
bigint id
text title
uuid created_by
timestamp created_at
uuid owned_by
uuid last_edited_by
timestamp last_edited_at
float vollkosten
float bafa_foerderung
}
user_neubau_projects {
bigint id
uuid user_id
bigint project_id
}
user_sanierung_projects {
bigint id
uuid user_id
bigint project_id
}
user_einzelmassnahmen_projects {
bigint id
uuid user_id
bigint project_id
}
einzelmassnahmen_items {
bigint id
bigint project_id
text title
int position
}
einzelmassnahmen_values {
bigint id
bigint item_id
bigint project_id
text title
float value
text unit
int position
}
auth_users ||--o{ neubau_projects : "created by"
auth_users ||--o{ neubau_projects : "owned by"
auth_users ||--o{ neubau_projects : "last edited by"
auth_users ||--o{ sanierung_projects : "created by"
auth_users ||--o{ sanierung_projects : "owned by"
auth_users ||--o{ sanierung_projects : "last edited by"
auth_users ||--o{ einzelmassnahmen_projects : "created by"
auth_users ||--o{ einzelmassnahmen_projects : "owned by"
auth_users ||--o{ einzelmassnahmen_projects : "last edited by"
auth_users ||--o{ user_neubau_projects : "has many"
auth_users ||--o{ user_sanierung_projects : "has many"
auth_users ||--o{ user_einzelmassnahmen_projects : "has many"
neubau_projects ||--o{ user_neubau_projects : "has many"
sanierung_projects ||--o{ user_sanierung_projects : "has many"
einzelmassnahmen_projects ||--o{ user_einzelmassnahmen_projects : "has many"
einzelmassnahmen_projects ||--o{ einzelmassnahmen_items : "contains many"
einzelmassnahmen_projects ||--o{ einzelmassnahmen_values : "contains many"
einzelmassnahmen_items ||--o{ einzelmassnahmen_values : "contains many"