Dia 16 — Fechamento do trimestre: app completo
Estruturar o projeto e revisar a base
Estruturar o projeto e revisar a base
Estrutura é a decisão de onde cada arquivo mora, e ela vale mais quando é
simples de lembrar: um lugar para tela, um lugar para componente reaproveitado,
um lugar para o que fala com o servidor, e um lugar para o estilo.
src/ telas/ uma tela por arquivo, com o nome da tela componentes/ o que e usado por mais de uma tela servicos/ o que fala com o servidor estilos/ cores, espacamento e o StyleSheet compartilhado dados/ constantes, tipos e o que nao tem estado
O exemplo desta página imprime a estrutura com a responsabilidade de cada
pasta ao lado do nome, e é a segunda coluna que importa: src/telas é uma tela
por arquivo, src/servicos é o que fala com o servidor, e src/dados é o que
não tem estado. Cinco pastas, cinco responsabilidades, e nenhuma sobreposição.
A regra que separa telas de componentes é o número de usos: o que uma
tela usa só ela, fica na tela; o que a segunda tela também usa, vira componente.
Componente em pasta de tela não é erro grave, mas componente que ninguém
consegue achar é — e o sintoma é o mesmo arquivo copiado em três pastas.
O exemplo aplica a regra em três casos e o resultado é o critério inteiro:
ListaPedidos usada por 2 telas vira componente, CartaoPedido usada por 3
também, e TelaConfig usada por 1 só fica na tela. O console traduz a
regra em uma frase — um item em duas telas vira componente, em uma só fica na
tela.
servicos é a camada que fala com o servidor, e ela é o que permite trocar de
fonte de dado sem tocar em tela. A tela chama listarPedidos() e recebe o
resultado; onde o dado veio — requisição, cache, dado de exemplo — é problema do
serviço. Sem essa separação, a tela gruda a fetch no componente e a troca de
fonte passa a ser reescrita de tela.
O exemplo troca a fonte do serviço e a tela não muda: com "servidor" ela
mostra "2 pedidos", e com "cache" o serviço devolve `{ origem: 'cache', itens:
[...] }` sem que nada na tela tenha sido tocado. É a linha final do parágrafo
medida: a tela não sabe de onde o dado veio, e por isso trocar a fonte é uma
linha no serviço.
revisar a base antes de fechar
Revisar o trimestre é olhar o que foi escrito com o critério de quem vai ler
depois, e há quatro perguntas que resolvem a maior parte:
- Há nome repetido de constante? Duas telas com
#1f6febescrito à mão é uma
cor que vai divergir.
- Há
console.logque sobrou? Ele não quebra o aplicativo, e é o que impede o
teste de passar em silêncio.
- Há componente com nome de uma linha só? Nome de componente é uma frase sobre
o que ele faz: CartaoPedido, ListaPedidos, CampoTexto.
- Há função que faz duas coisas? Separar é o que permite testá-la.
O exemplo escreve os quatro critérios como pares de achado e correção, e cada
linha é uma regra aplicável: console.log no componente sai antes de fechar,
nome de uma palavra só vira frase, função que faz duas coisas é separada para
poder ser testada, e cor escrita à mão vai para o arquivo de estilos. A cor de
#1f6feb nomeada uma vez só resolve o primeiro critério, e é o que o exemplo
mostra no objeto de cores.
Refatorar com esses quatro critérios é diferente de reescrever. Reescrever
apaga o que funcionava; revisar com lista mantém o comportamento e tira o que
está atrapalhando.
Os nomes do exemplo fecham o critério 3 com os quatro nomes bons:
CartaoPedido, ListaPedidos, CampoTexto e CabecalhoTela. Cada nome diz o
que o componente é, e é o que permite a busca achar pelo nome — a mesma razão
que faz o eixo de termos do curso existir.
Exemplo
// Estrutura do projeto: cada pasta tem uma responsabilidade, e o // exemplo monta a arvore de arquivos e a regra que separa componente de // tela. Tambem mostra os quatro criterios da revisao. const { View, Text, StyleSheet } = require('react-native'); // (1) a estrutura, com a responsabilidade de cada pasta. const estrutura = { src: { telas: 'uma tela por arquivo, com o nome da tela', componentes: 'o que e usado por mais de uma tela', servicos: 'o que fala com o servidor', estilos: 'cores, espacamento e o StyleSheet compartilhado', dados: 'constantes, tipos e o que nao tem estado', }, }; console.log('estrutura do projeto:'); for (const pasta of Object.keys(estrutura.src)) { console.log(' src/' + pasta.padEnd(13) + estrutura.src[pasta]); } // (2) a regra que decide tela x componente: quantas telas usam. const usosPorArquivo = { 'ListaPedidos': ['Pedidos', 'DetalhePedido'], 'CartaoPedido': ['Pedidos', 'DetalhePedido', 'Painel'], 'TelaConfig': ['Config'], }; console.log('\nquem usa cada arquivo:'); for (const arquivo of Object.keys(usosPorArquivo)) { const usos = usosPorArquivo[arquivo]; console.log(' ' + arquivo.padEnd(15), usos.length + ' tela(s)', usos.length > 1 ? '-> componente' : '-> fica na tela'); } console.log('um item em duas telas vira componente; em uma so, fica na tela'); // (3) a camada de servico: a tela pede dado e nao sabe de onde veio. function servicoListarPedidos(fonte) { return { origem: fonte, itens: [{ id: 'p41' }, { id: 'p42' }] }; } function telaDePedidos() { // a tela chama o servico e nao conhece a fonte const resultado = servicoListarPedidos('servidor'); return <Text>{resultado.itens.length + ' pedidos'}</Text>; } const arvore = telaDePedidos(); console.log('\na tela:', arvore.type, '|', arvore.props.children); console.log('trocar a fonte e uma linha no servico:'); console.log(' ', JSON.stringify(servicoListarPedidos('cache'))); console.log('a tela nao muda, porque ela nao sabe de onde o dado veio'); // (4) revisao: cor repetida com o nome certo uma vez so. const cores = { primaria: '#1f6feb', texto: '#101418' }; const estilos = StyleSheet.create({ botao: { backgroundColor: cores.primaria } }); console.log('\ncor nomeada:', JSON.stringify(StyleSheet.flatten(estilos.botao))); console.log('duas telas com a cor escrita a mao e uma cor que vai divergir'); // (5) revisao: `console.log` que sobrou. const revisao = [ { achado: 'console.log no componente', correcao: 'remover antes de fechar' }, { achado: 'nome de componente com uma palavra so', correcao: 'nomear com a frase do que ele faz' }, { achado: 'funcao que faz duas coisas', correcao: 'separar para poder testar' }, { achado: 'cor escrita a mao em duas telas', correcao: 'nomear no arquivo de estilos' }, ]; console.log('\nos quatro criterios da revisao:'); for (const item of revisao) console.log(' ' + item.achado + ' -> ' + item.correcao); // (6) o exemplo: nome de componente e uma frase sobre o que ele faz. const nomeados = ['CartaoPedido', 'ListaPedidos', 'CampoTexto', 'CabecalhoTela']; console.log('\nnomes de componente:', nomeados); console.log('cada nome diz o que o componente e, e a busca acha pelo nome');
Saída real
estrutura do projeto:
src/telas uma tela por arquivo, com o nome da tela
src/componentes o que e usado por mais de uma tela
src/servicos o que fala com o servidor
src/estilos cores, espacamento e o StyleSheet compartilhado
src/dados constantes, tipos e o que nao tem estado
quem usa cada arquivo:
ListaPedidos 2 tela(s) -> componente
CartaoPedido 3 tela(s) -> componente
TelaConfig 1 tela(s) -> fica na tela
um item em duas telas vira componente; em uma so, fica na tela
a tela: Text | 2 pedidos
trocar a fonte e uma linha no servico:
{"origem":"cache","itens":[{"id":"p41"},{"id":"p42"}]}
a tela nao muda, porque ela nao sabe de onde o dado veio
cor nomeada: {"backgroundColor":"#1f6feb"}
duas telas com a cor escrita a mao e uma cor que vai divergir
os quatro criterios da revisao:
console.log no componente -> remover antes de fechar
nome de componente com uma palavra so -> nomear com a frase do que ele faz
funcao que faz duas coisas -> separar para poder testar
cor escrita a mao em duas telas -> nomear no arquivo de estilos
nomes de componente: [ 'CartaoPedido', 'ListaPedidos', 'CampoTexto', 'CabecalhoTela' ]
cada nome diz o que o componente e, e a busca acha pelo nome
Publicar o app e fechar o trimestre
Publicar o app e fechar o trimestre
Build de produção é outro pacote do que a versão de desenvolvimento. O
desenvolvimento entrega um servidor de código quase legível, para o
carregamento ser rápido e o erro aparecer com nome de arquivo. O build de
release entrega código comprimido, sem nada de console, e é esse que vai para
a loja.
A distinção que importa: console.log em produção mostra dado do usuário.
Publicar com registro ligado é a razão comum de vazamento em aplicativo real. O
build de release remove o registro por padrão, e essa é uma das razões para ele
existir separado do outro.
O exemplo desta página monta os dois builds e a diferença sai em três campos. O
de desenvolvimento tem 12 registros, código legível e tamanho 24800; o de
release tem 0 registros, código ilegível e tamanho 6400. A razão é a mesma
em todos: o release sai sem console, e é o que evita mostrar dado do usuário.
Versionar é o número que o usuário lê: 1.2.0 é maior, maior e correção. O
comprimento do número diz o tamanho da mudança — subir o terceiro porque
corrigiu um texto é exagero; subir o primeiro porque reescreveu a tela é o
mínimo. E o número só sobe depois que a entrega está pronta, não quando o
código começou.
O exemplo lista as três versões com data e mudança, e a comparação de números do
console devolve 1.1.1 como a maior. A linha seguinte diz a regra que os
números obedecem: a correção sobe o terceiro número e a tela nova sobe o
primeiro. É a diferença entre 1.0.0 para a primeira entrega e 1.1.1 para a
correção de um erro de salvamento.
o que o changelog registra
O changelog não é o histórico do git: é o que mudou para quem usa. "Corrigido
o travamento ao salvar um pedido" serve; "refatorado o serviço de pedido" não.
Quem lê o changelog quer saber se o defeito que ele afeta está
corrigido, e essa resposta vem em uma frase por versão.
As três frases do exemplo mostram o critério funcionando: a 1.0.0 diz o que
a primeira versão tem, a 1.1.0 diz que a lista parou de travar, e a 1.1.1
descreve o erro do salvamento sem cliente escolhido. Nenhuma das três menciona
refatoração, e é por isso que as três respondem a pergunta de quem lê.
o README é a primeira tela
O README é o que alguém abre antes de rodar o projeto, e ele responde a quatro
perguntas em ordem: o que é, o que precisa instalar, como se roda, e como se
testa. Um README que começa com a arquitetura é um README que ninguém lê até o
terceiro parágrafo.
O exemplo lista as quatro perguntas numeradas, na ordem que o parágrafo exige, e
a linha final traduz o erro de ordem: um README que começa pela arquitetura é um
README que ninguém lê até o terceiro parágrafo. A arquitetura tem lugar no
material — depois das quatro perguntas, e nunca antes delas.
limpar antes de publicar
Três coisas saem antes de publicar: código de teste que não tem mais uso, dado
de exemplo que ficou no lugar do real, e segredo nenhum no pacote — a base, o
token e a chave de assinatura não vão versionados. É a revisão que o trimestre
inteiro vinha preparando: os quatro critérios da revisão do dia anterior, com o
acréscimo de "isso é seguro mandar para fora?".
A função de conferência do exemplo roda sobre o pacote e devolve as três
linhas: código de teste sem uso em 1 arquivo, dado de exemplo no lugar do real
em 1 arquivo, e segredo no pacote em config/base.js. É o terceiro achado que
distingue esta revisão das quatro perguntas da aula anterior: as outras três são
sobre legibilidade, e esta é sobre segurança.
O fechamento do trimestre é essa revisão, e o que entra nele é o que o
trimestre seguinte vai assumir: componentes que se combinam, listas que não
travam, telas que trocam, e dado que chega de fora. O que falta aqui é o
guardar para depois — e ele fica para o trimestre seguinte, junto com o
SQLite.
O exemplo fecha o trimestre com as quatro entregas listadas e a linha do que fica
para depois: guardar o dado no aparelho. A árvore da tela inicial mostra a
versão como um Text com "versao 1.1.1", e o console explica por que esse
número aparece em um lugar só — o arquivo de versão é a fonte, e a tela apenas o
lê.
Exemplo
// Fechamento: build de release sem registro, versao, changelog e o // README. O exemplo monta a versao, o changelog e a conferencia do que // sai antes de publicar. const { View, Text, StyleSheet } = require('react-native'); // (1) versao: maior, maior, correcao. O numero sobe depois da entrega. const versoes = [ { versao: '1.0.0', data: '2026-05-01', mudanca: 'tela de pedidos e detalhe' }, { versao: '1.1.0', data: '2026-06-15', mudanca: 'lista com rolagem sem travar' }, { versao: '1.1.1', data: '2026-07-20', mudanca: 'corrigido o erro ao salvar sem cliente' }, ]; function maior(a, b) { const pa = a.split('.').map(Number); const pb = b.split('.').map(Number); for (let i = 0; i < 3; i += 1) { if (pa[i] !== pb[i]) return pa[i] > pb[i] ? a : b; } return a; } console.log('versoes publicadas:'); for (const v of versoes) console.log(' ' + v.versao, v.data, '-', v.mudanca); console.log('a maior versao:', maior(maior(versoes[0].versao, versoes[1].versao), versoes[2].versao)); console.log('a correcao sobe o terceiro numero; a tela nova sobe o primeiro'); // (2) o changelog responde o que mudou PARA QUEM USA. const changelog = { '1.1.1': 'Corrigido o erro que aparecia ao salvar um pedido sem cliente escolhido.', '1.1.0': 'A lista de pedidos parou de travar ao rolar com muitos itens.', '1.0.0': 'Primeira versao com tela de pedidos, detalhe e edicao.', }; console.log('\nchangelog:'); for (const v of versoes) console.log(' ' + v.versao + ': ' + changelog[v.versao]); console.log('o changelog nao e o historico do git: e o que mudou para quem usa'); // (3) build de release: sem registro, e sem dado de exemplo. function saidaDoBuild(desenvolvimento) { const registros = desenvolvimento ? 12 : 0; return { registros, legivel: desenvolvimento, tamanho: desenvolvimento ? 24800 : 6400, }; } console.log('\nbuild de desenvolvimento:', saidaDoBuild(true)); console.log('build de release: ', saidaDoBuild(false)); console.log('o release sai sem console: e o que evita mostrar dado do usuario'); // (4) o que sai antes de publicar. function revisarParaPublicar(arquivos) { const problemas = []; const testes = arquivos.filter((a) => a.temTeste === false); if (testes.length) problemas.push('codigo de teste sem uso: ' + testes.length + ' arquivo(s)'); const exemplos = arquivos.filter((a) => a.dadoDeExemplo === true); if (exemplos.length) problemas.push('dado de exemplo no lugar do real: ' + exemplos.length + ' arquivo(s)'); const segredos = arquivos.filter((a) => a.temSegredo === true); if (segredos.length) problemas.push('segredo no pacote: ' + segredos.map((a) => a.nome).join(', ')); return problemas.length ? problemas : ['nada a remover']; } const pacote = [ { nome: 'telas/Pedidos.js', temTeste: true, dadoDeExemplo: false, temSegredo: false }, { nome: 'servicos/pedidos.js', temTeste: true, dadoDeExemplo: false, temSegredo: false }, { nome: 'dados/exemplo.js', temTeste: true, dadoDeExemplo: true, temSegredo: false }, { nome: 'config/base.js', temTeste: true, dadoDeExemplo: false, temSegredo: true }, { nome: 'telas/TesteAntigo.js', temTeste: false, dadoDeExemplo: false, temSegredo: false }, ]; console.log('\no que sai antes de publicar:'); for (const linha of revisarParaPublicar(pacote)) console.log(' ' + linha); // (5) o README responde quatro perguntas, nessa ordem. const readme = [ 'o que e', 'o que precisa instalar', 'como se roda', 'como se testa', ]; console.log('\no README, em ordem:'); readme.forEach((pergunta, i) => console.log(' ' + (i + 1) + '. ' + pergunta)); console.log('um README que comeca pela arquitetura e um README que ninguem le ate o terceiro paragrafo'); // (6) o que o trimestre entregou. const entregue = [ 'componentes que se combinam', 'listas longas que nao travam', 'telas que trocam', 'dado que vem de fora', ]; console.log('\no que o trimestre entregou:'); for (const item of entregue) console.log(' ' + item); console.log('o que fica para o proximo trimestre: guardar o dado no aparelho'); // (7) a arvore da tela inicial, montada com a versao do app. const estilos = StyleSheet.create({ versao: { padding: 8, fontSize: 12, textAlign: 'center' } }); function TelaDeVersao() { return <Text style={estilos.versao}>versao 1.1.1</Text>; } const arvore = TelaDeVersao(); console.log('\ntipo:', arvore.type, '| texto:', arvore.props.children); console.log('a versao mostrada vem do arquivo de versao, em um lugar so');
Saída real
versoes publicadas:
1.0.0 2026-05-01 - tela de pedidos e detalhe
1.1.0 2026-06-15 - lista com rolagem sem travar
1.1.1 2026-07-20 - corrigido o erro ao salvar sem cliente
a maior versao: 1.1.1
a correcao sobe o terceiro numero; a tela nova sobe o primeiro
changelog:
1.0.0: Primeira versao com tela de pedidos, detalhe e edicao.
1.1.0: A lista de pedidos parou de travar ao rolar com muitos itens.
1.1.1: Corrigido o erro que aparecia ao salvar um pedido sem cliente escolhido.
o changelog nao e o historico do git: e o que mudou para quem usa
build de desenvolvimento: { registros: 12, legivel: true, tamanho: 24800 }
build de release: { registros: 0, legivel: false, tamanho: 6400 }
o release sai sem console: e o que evita mostrar dado do usuario
o que sai antes de publicar:
codigo de teste sem uso: 1 arquivo(s)
dado de exemplo no lugar do real: 1 arquivo(s)
segredo no pacote: config/base.js
o README, em ordem:
1. o que e
2. o que precisa instalar
3. como se roda
4. como se testa
um README que comeca pela arquitetura e um README que ninguem le ate o terceiro paragrafo
o que o trimestre entregou:
componentes que se combinam
listas longas que nao travam
telas que trocam
dado que vem de fora
o que fica para o proximo trimestre: guardar o dado no aparelho
tipo: Text | texto: versao 1.1.1
a versao mostrada vem do arquivo de versao, em um lugar so