Pular para conteúdo

Arquitetura

Organização das pastas

src/
  app/                 rotas (Expo Router, roteamento por arquivo)
    (tabs)/            Início, Receitas, Despensa
    pantry-add.tsx     modal de adicionar ingredientes
    recipe/[id].tsx    detalhe da receita
  components/
    ui/                biblioteca de componentes reutilizáveis
    illustrations/     ilustrações de estado vazio
  features/
    pantry/            despensa: catálogo, busca, store, ícones
    recipes/           receitas: catálogo, match, sugestões
    home/              lógica da tela inicial
  theme/               cores, tipografia, espaçamento, temas
  hooks/               hooks compartilhados
  __tests__/           suítes de teste

Organização por feature

Cada feature é uma pasta autocontida com seus dados, sua lógica e seus componentes. pantry/ e recipes/ seguem o mesmo molde, o que torna a segunda previsível para quem já conhece a primeira.

Uma regra de dependência

recipes depende de pantry, nunca o contrário

As receitas referenciam identificadores do catálogo de ingredientes, então essa direção é natural. O inverso tornaria a despensa dependente das receitas e ela deixaria de funcionar sozinha.

Telas podem importar as duas — tela é o ponto legítimo de junção.

Estado

O estado da despensa vive num store leve com persistência automática no armazenamento do aparelho. É o único estado global do app.

O resultado do match não é estado. É calculado a partir da despensa a cada render, por função pura. Não há nada para sincronizar, invalidar ou manter coerente:

despensa (store) ─┐
                  ├─► matchRecipes() ─► lista ordenada
catálogo ─────────┘

A consequência prática: mudar a despensa reordena as receitas sozinho, sem código de sincronização.

Funções puras onde a lógica erra em silêncio

Match e sugestões são funções puras, sem React e sem store. Elas recebem tudo por parâmetro.

A razão é concreta: uma ordenação instável ou um empate mal resolvido não quebram nada — só entregam uma lista pior, e ninguém percebe. Testar isso sem renderizar tela torna o erro visível.

Há um detalhe de desenho que vale citar: a função de match recebe apenas os identificadores da despensa, nunca as quantidades. Assim a regra "quantidade não conta" é garantida pelo tipo, não por convenção que alguém pode esquecer.

Contrato de fonte de receitas

O acesso às receitas passa por um contrato único, para que trocar a origem no futuro — uma API, receitas da comunidade — não encoste na regra de match nem nas telas.

O contrato é síncrono, e isso é uma escolha com custo assumido: torná-lo assíncrono hoje obrigaria estados de carregamento e erro em todas as telas para um caso que não existe. Quando a fonte virar remota, as telas mudam uma vez; as funções puras não mudam.