1. Introdução · O banco como camada ativa
Até agora você tratou o banco de dados como um lugar onde se guarda coisa. Você manda SELECT, ele devolve linhas. Você manda INSERT, ele guarda. Toda a lógica do sistema — quem pode fazer o quê, o que conta como uma operação válida, como combinar dados — vive no backend.
Esta aula muda essa perspectiva. O banco também pode executar lógica. Ele pode receber um pedido como "marque o usuário 42 como tendo assistido ao espetáculo 17 e atualize o contador de visualizações", verificar regras, abrir uma transação, fazer várias operações, fechar a transação e devolver o resultado em uma única chamada de rede.
Esse poder vem de duas construções: stored procedures e functions. Ambas vivem dentro do banco como objetos versionáveis, têm parâmetros, podem ler e escrever em tabelas, podem subir exceções. A diferença está em como elas se encaixam no resto do mundo — e é nessa diferença que reside boa parte das decisões arquiteturais que você vai tomar daqui em diante no projeto.
Objetivos de aprendizagem
- OA-1Diferenciar stored procedures de functions, escolhendo a forma certa para cada caso de uso do projeto.
- OA-2Escrever rotinas PL/pgSQL com variáveis, controle de fluxo (
IF,LOOP) e tratamento de exceção. - OA-3Implementar uma procedure que encapsule uma operação de domínio do projeto dentro de uma transação atômica.
- OA-4Justificar o trade-off entre colocar lógica no banco vs. na aplicação, com argumentos de manutenibilidade, performance e segurança.
- OA-5Conectar a procedure ao stack mobile MVVM com React Native, identificando onde ela aparece em cada camada (View, ViewModel, Model, API).
- OA-6Reconhecer riscos de segurança (SQL injection em SQL dinâmico) e técnicas de mitigação (
EXECUTE ... USING, revogação de privilégios).
Você já sabe escrever SQL: SELECT, INSERT, UPDATE, DELETE, JOIN e agregações. Se algum desses ainda dói, revise o material da aula 4 do Módulo 2 (Joins) antes de prosseguir — sem essa base, procedures parecem mágica em vez de ferramenta.
2. Por que descer lógica para o BD?
A pergunta certa não é "posso colocar essa regra no banco?" — quase sempre você pode. A pergunta é "devo?". Vamos olhar os quatro motivos clássicos a favor, e depois os três sinais de que você está empurrando coisa errada para o lugar errado.
2.1 · Quatro motivos a favor
⚡ Round-trips · reduzir idas e voltas
Suponha que o app precise executar uma lógica composta por 4 queries dependentes. Sem procedure, o backend faz 4 chamadas: pega o resultado da primeira, monta a segunda, espera a resposta, monta a terceira… Em uma rede com 10ms de latência, você gasta 40ms só em ida-e-volta, sem contar o tempo de execução em si. Com procedure, são 10ms de uma chamada só — o banco executa as 4 queries internamente.
🛡️ Atomicidade e contrato
Uma procedure expõe um contrato claro: "você me dá esses parâmetros, eu te devolvo essa saída". O que acontece entre os dois é problema do banco. Se a procedure abre uma transação, executa três UPDATEs e dá COMMIT, o app não precisa saber dessa orquestração — só recebe sucesso ou erro.
🔐 Segurança por encapsulamento
Você pode revogar permissões de leitura/escrita direta nas tabelas e dar permissão só de EXECUTE nas procedures. O app passa a só conhecer pontos de entrada controlados. SQL injection vira muito mais difícil porque o app nunca monta SQL livre; ele só chama procedures com parâmetros tipados.
♻️ Reuso entre serviços
Tem app web, app mobile, job batch noturno e relatório do BI. Se a regra "calcular top-N recomendações" vive como query SQL espalhada por 4 stacks diferentes, mudar essa regra significa mudar em 4 lugares. Se vive como procedure única, é uma migration só. O banco vira o ponto de centralização natural.
2.2 · Três cenários onde NÃO compensa
Lógica que muda toda sprint
Regra de marketing que troca a cada A/B test? Política de preços com 6 condições novas por mês? Deixe no app. Versionar SQL com rollback seguro é mais lento que versionar TypeScript — o ganho de performance da procedure não compensa o atrito de deploy.
Regra que precisa de chamadas externas
Se a operação depende de uma API REST, de um modelo de Machine Learning ou de processamento pesado, o banco não é o lugar. PL/pgSQL pode fazer HTTP em teoria (com extensão), mas isso é abrir mão da simplicidade da camada de dados.
Time sem skill em SQL avançado
Debug de procedure complexa requer ferramentas diferentes do debug de JS. Se o time não tem alguém confortável em ler plano de execução e analisar locks, procedure vira caixa-preta com bug difícil de achar. Avalie honestamente.
Procedure útil: encapsula consistência crítica, atomicidade transacional ou desempenho que importa medir. Procedure ruim: esconde regra de negócio em lugar onde nenhum desenvolvedor da aplicação vai pensar em procurar. Se um colega de outro projeto não consegue entender o que o sistema faz lendo só o código do backend, a procedure virou armadilha — não ferramenta.
3. Stored Procedures · Anatomia
Uma stored procedure é uma rotina nomeada, com parâmetros tipados, que executa um bloco de SQL. É chamada por CALL (PostgreSQL) ou EXEC (SQL Server). Não retorna valor como uma função matemática — ela faz coisas: insere, atualiza, deleta, eventualmente devolve um result set.
3.1 · Anatomia mínima
CREATE OR REPLACE PROCEDURE sp_top_recomendacoes_usuario(
p_user_id INTEGER,
p_top INTEGER DEFAULT 10
)
LANGUAGE plpgsql
AS $$
BEGIN
-- corpo da procedure
SELECT i.id, i.nome, r.score
FROM recomendacao r
JOIN item i ON i.id = r.item_id
WHERE r.user_id = p_user_id
ORDER BY r.score DESC
LIMIT p_top;
END;
$$;
-- chamada
CALL sp_top_recomendacoes_usuario(42, 5);
Quatro pontos importantes nessa estrutura:
CREATE OR REPLACEtorna o script idempotente — rodar duas vezes não quebra. Migrations dependem disso.- Os parâmetros são tipados. O motor rejeita uma chamada com tipo errado antes de executar.
DEFAULT 10permite omitir o parâmetro na chamada. Reduz acoplamento entre app e procedure.- O bloco entre
$$ ... $$é o corpo da rotina. Tudo que está aí dentro é PL/pgSQL — não é SQL puro.
3.2 · Procedure com transação e exceção
O ganho real aparece quando a procedure executa múltiplas operações que precisam acontecer juntas. Exemplo: registrar uma interação e atualizar o contador agregado.
CREATE OR REPLACE PROCEDURE sp_marcar_assistido(
p_user_id INTEGER,
p_espetaculo_id INTEGER,
INOUT p_interacao_id INTEGER
)
LANGUAGE plpgsql
AS $$
BEGIN
-- as duas operações abaixo são atômicas:
-- ou ambas commitam, ou nenhuma acontece
INSERT INTO interacao (user_id, espetaculo_id, tipo, data)
VALUES (p_user_id, p_espetaculo_id, 'ASSISTIU', NOW())
RETURNING id INTO p_interacao_id;
UPDATE estatistica_item
SET total_visualizacoes = total_visualizacoes + 1
WHERE item_id = p_espetaculo_id;
EXCEPTION
WHEN foreign_key_violation THEN
RAISE EXCEPTION 'Usuário ou espetáculo inexistente: % / %',
p_user_id, p_espetaculo_id;
WHEN OTHERS THEN
RAISE; -- propaga qualquer outro erro para o caller
END;
$$;
Note três coisas:
INOUTpermite que a procedure receba e devolva um valor pelo mesmo parâmetro. Útil para retornar IDs gerados.- O bloco
EXCEPTIONtrata erros específicos antes de subir genericamente.foreign_key_violationé só uma das condições nominadas que o PostgreSQL expõe. - Em PostgreSQL, cada chamada de procedure roda em sua própria transação implícita.
COMMITeROLLBACKsão automáticos se você não os controla manualmente.
O exemplo usa PostgreSQL. Em SQL Server (T-SQL), você usaria CREATE PROCEDURE com @parametros, BEGIN TRY / BEGIN CATCH e BEGIN TRANSACTION / COMMIT TRANSACTION explícitos. A ideia é a mesma — só a sintaxe muda. Os slides da aula mostram a versão T-SQL para você comparar lado a lado.
4. PL/pgSQL · A linguagem procedural do PostgreSQL
SQL puro é declarativo: você descreve o que quer, o motor decide como. PL/pgSQL adiciona controle procedural — você descreve passos. É a linguagem em que escrevemos o corpo das procedures e functions no PostgreSQL.
4.1 · Variáveis e atribuição
DECLARE
v_total INTEGER;
v_media NUMERIC(5,2);
v_nome TEXT;
v_encontrado BOOLEAN := FALSE; -- valor inicial
BEGIN
SELECT COUNT(*) INTO v_total FROM item;
v_media := v_total / 10.0;
END;
4.2 · Controle de fluxo
IF v_total > 100 THEN
RAISE NOTICE 'Catálogo grande: % itens', v_total;
ELSIF v_total > 10 THEN
RAISE NOTICE 'Catálogo médio';
ELSE
RAISE NOTICE 'Catálogo pequeno';
END IF;
-- LOOP com FOR sobre um SELECT
FOR r IN SELECT id, nome FROM item ORDER BY id LOOP
RAISE NOTICE 'Item %: %', r.id, r.nome;
END LOOP;
4.3 · Tratamento de exceção
O bloco EXCEPTION intercepta erros antes que eles propaguem para o caller. Você pode tratar condições específicas ou usar WHEN OTHERS como rede final. Quando você apenas faz RAISE dentro do handler, o erro original é re-disparado — útil para logar e propagar.
O PostgreSQL expõe nomes simbólicos para tipos de erro: unique_violation, foreign_key_violation, not_null_violation, division_by_zero, no_data_found, entre dezenas de outros. A lista completa está em Appendix A: PostgreSQL Error Codes. Capturar pelo nome é mais legível e estável do que comparar por código SQLSTATE — embora o código sirva quando você não tem o nome à mão.
5. Functions · Escalar e Tabular
Onde procedure faz coisa, function devolve coisa. A diferença operacional é simples: function pode ser usada em qualquer lugar onde uma expressão SQL caberia — em SELECT, WHERE, FROM, JOIN, dentro de outra function. Procedure não — ela exige chamada explícita por CALL.
5.1 · Function escalar — devolve 1 valor
CREATE OR REPLACE FUNCTION fn_media_item(p_item_id INTEGER)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
DECLARE
v_media NUMERIC;
BEGIN
SELECT AVG(rating) INTO v_media
FROM avaliacao
WHERE item_id = p_item_id;
RETURN COALESCE(v_media, 0);
END;
$$;
-- usa em qualquer expressão SQL:
SELECT id, nome, fn_media_item(id) AS media
FROM item
WHERE fn_media_item(id) > 4.0;
5.2 · Function tabular — devolve um SELECT
CREATE OR REPLACE FUNCTION fn_itens_similares(p_item_id INTEGER)
RETURNS TABLE (id INTEGER, nome TEXT, similaridade NUMERIC)
LANGUAGE sql -- pode ser SQL puro se for um único SELECT
AS $$
SELECT i.id, i.nome, s.similaridade
FROM similaridade s
JOIN item i ON i.id = s.item_b_id
WHERE s.item_a_id = p_item_id
ORDER BY s.similaridade DESC
LIMIT 5;
$$;
-- usa como se fosse uma tabela:
SELECT * FROM fn_itens_similares(17);
-- ou em JOIN:
SELECT u.nome AS usuario, s.nome AS sugestao
FROM usuario u
CROSS JOIN LATERAL fn_itens_similares(u.item_favorito_id) AS s
WHERE u.id = 42;
Quando a function é um único SELECT sem variáveis nem controle de fluxo, declare-a como LANGUAGE sql em vez de plpgsql. O motor consegue fazer inline da function diretamente na query externa — o plano de execução pula a chamada e otimiza tudo junto. Para SQL Server, o equivalente é Inline Table-Valued Function (sem BEGIN/END).
6. Procedure × Function · Tabela de decisão
| Critério | Procedure | Function |
|---|---|---|
| Side-effects (INSERT, UPDATE, DELETE) | ✅ Permitido — é para isso que existe | ❌ Bloqueado pelo motor (raras exceções com VOLATILE) |
| Retorno | Result set, parâmetros OUT/INOUT, código de saída |
1 valor (escalar) ou tabela (TVF) |
Uso em SELECT / WHERE / FROM |
❌ Não — chamada por CALL ou EXEC |
✅ Sim — composta como qualquer expressão |
| Controle de transação | ✅ BEGIN / COMMIT / ROLLBACK à vontade |
❌ Roda na transação do caller |
| Tratamento de erro | ✅ EXCEPTION com RAISE arbitrário |
⚠️ Limitado — função pura não levanta erro de negócio |
| Caso típico | Operação de escrita, fluxo com transação, batch | Cálculo derivado, lookup, métrica usada em outra query |
Se a operação muda dados, é procedure. Se calcula algo que vai entrar em outra query, é function. Em dúvida? Comece com procedure — é mais flexível e expõe um contrato explícito (você precisa chamá-la deliberadamente). Promover de procedure para function depois é trivial; o caminho inverso costuma exigir reescrita do chamador.
7. Performance · Plano de execução, cache e parameter sniffing
Procedures e functions não são mágicas de performance. Elas reduzem round-trips e dão um plano cacheado, mas se a query interna é ruim, a procedure só esconde o problema. Vamos ver os três conceitos que importam.
7.1 · Cache do plano
Na primeira execução, o motor compila a procedure e guarda o plano de execução no cache. Próximas chamadas pulam essa fase. Em uma procedure chamada milhares de vezes por minuto, isso vira ganho real.
7.2 · Parameter sniffing
O plano é otimizado para o primeiro valor que chegou. Se a primeira chamada foi p_user_id = 42 (usuário com 5 recomendações) e a segunda foi p_user_id = 7 (usuário com 5000 recomendações), o plano em cache foi feito para o caso de 5 linhas e vai performar mal no caso de 5000. Esse fenômeno é o famoso parameter sniffing.
-- PostgreSQL: discartar plano em cache
DISCARD PLANS;
-- SQL Server: forçar plano fresco a cada execução
CREATE PROCEDURE sp_top_recomendacoes_usuario
@userId INT
WITH RECOMPILE -- recompila toda vez · perde cache, ganha plano fresco
AS
BEGIN
SELECT ...
END
7.3 · Como ler o plano
- PostgreSQL:
EXPLAIN ANALYZE <query>mostra o plano e o tempo real de cada nó. - SQL Server:
SET SHOWPLAN_ALL ONou Ctrl+M no SSMS para plano gráfico. - Procure por Sequential Scan / Table Scan em tabelas grandes — quase sempre é índice faltando.
- Compare o custo total (
cost) antes e depois de adicionar um índice — esse é o feedback objetivo.
Se a query interna faz SELECT em tabela de 50 milhões de linhas sem índice, a procedure só vai esconder isso — não vai melhorar. Procedure boa precisa do índice certo embaixo dela. Antes de mover lógica para o banco achando que vai ficar rápido, rode EXPLAIN ANALYZE na query crua e veja se o problema é mesmo round-trip ou se é query ruim.
8. Segurança · SQL injection em procedures
Procedure não é imune a SQL injection. Se você monta SQL dinâmico concatenando strings dentro de uma procedure, você reintroduz o mesmo problema do backend mal escrito.
-- NÃO FAÇA ISSO
CREATE OR REPLACE FUNCTION fn_buscar_item(p_filtro TEXT)
RETURNS SETOF item
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM item WHERE nome LIKE ' || QUOTE_LITERAL(p_filtro);
-- mesmo com QUOTE_LITERAL acima, fica frágil
END;
$$;
CREATE OR REPLACE FUNCTION fn_buscar_item(p_filtro TEXT)
RETURNS SETOF item
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM item WHERE nome LIKE $1'
USING '%' || p_filtro || '%';
-- USING faz bind seguro · parâmetro nunca vira código
END;
$$;
Sempre que precisar de SQL dinâmico (nome de tabela ou coluna em parâmetro, por exemplo), use:
EXECUTE ... USINGpara valores → parametrização segura.format(...)com%I(identifier) ou%L(literal) → escapa corretamente para nomes/literais embutidos no SQL.
Para reduzir o blast radius de uma injection eventual, revogue privilégios diretos de SELECT/INSERT/UPDATE/DELETE para o usuário de aplicação e conceda apenas EXECUTE nas procedures. Mesmo que o app seja comprometido, o atacante só consegue executar o conjunto controlado de procedures — não consegue fazer DROP TABLE nem ler colunas sensíveis arbitrariamente.
9. Versionamento e CI/CD · Migrations idempotentes
SQL é código. Procedures e functions vivem no Git, passam por code review, têm CI próprio. Procedure que não está versionada não existe — você não tem como reproduzir o ambiente, fazer rollback ou rodar testes confiavelmente.
9.1 · Ferramentas mais comuns
- Flyway — arquivos versionados
V1__init.sql,V2__add_proc.sql. Aplica em ordem, mantém histórico em tabela de controle. Simples e robusto. - Liquibase — XML/YAML/SQL com changesets. Suporta rollback declarativo. Mais expressivo, mas exige aprendizado.
- node-pg-migrate — para stacks Node.js. Migrations em JS/TS com
up()edown(). - EF Core Migrations — para stacks .NET. Gera SQL a partir de modelo C#.
9.2 · Idempotência é lei
Pipeline de CI/CD precisa ser retentável. Se a migration falha no meio, você precisa poder rodar de novo sem quebrar. Por isso, use:
CREATE OR REPLACE PROCEDURE/FUNCTION— sempre substitui, nunca quebra.DROP X IF EXISTSantes de criar coisas que não suportam OR REPLACE.- Migrations devem ser aditivas sempre que possível: nunca modificar uma migration já aplicada em produção — sempre criar uma nova.
9.3 · Gates de CI sugeridos
db-lint— roda sqlfluff em tododb/*.sqlpara padronizar estilo.db-migrate-staging— aplica migration em ambiente espelho do prod.db-rollback-test— verifica se a migration tem rollback funcional.- Aprovação manual obrigatória antes de tocar em produção.
10. Conectando ao mundo · MVVM com React Native
Toda essa engenharia de procedures e functions só faz sentido quando alguém chama elas. No nosso projeto, esse alguém é o app mobile React Native — organizado em uma arquitetura MVVM (Model-View-ViewModel).
MVVM é um padrão de separação de responsabilidades. Pense nele como três camadas com papéis bem definidos:
10.1 · As três camadas
📱 View — a interface do usuário
São os componentes visuais do React Native: telas, botões, textos, listas. A View não processa dados. Ela apenas exibe o que a ViewModel manda e avisa quando o usuário interage (toca em algo, faz pull-to-refresh, digita).
🧠 ViewModel — o cérebro da tela
É quem busca dados na Model, transforma e prepara para a View consumir. No React Native, costumamos implementar a ViewModel como um Custom Hook que encapsula useState, useEffect e callbacks. Gerencia loading, error e data — esses três estados que toda tela precisa lidar.
🗄️ Model — dados e regras de negócio
É a camada dos dados. Define tipos/classes que descrevem a forma dos dados (TypeScript interfaces) e implementa as requisições para APIs ou bancos locais. É aqui que a chamada HTTP acontece — a chamada que, na ponta, ativa a nossa procedure.
10.2 · Exemplo concreto — Tela de perfil
Você tem uma tela de perfil que mostra foto, nome e estatísticas do usuário.
- A View renderiza a foto e o nome com
<Image>e<Text>. - A ViewModel (um hook
useProfile(userId)) tem a lógica de carregar esses dados, gerenciar oloadinge expor umrefetch. - A Model (um Repository
ProfileRepo) é a função que vai no servidor buscar o JSON com os dados — e o servidor, por baixo, executa a nossa procedure para calcular o ranking, contar avaliações, etc.
10.3 · Diagrama da arquitetura
Cada camada conhece apenas a vizinha. Trocar PostgreSQL por SQL Server: muda só a procedure. Trocar React Native por Flutter: muda só a View. Trocar o servidor Node por um servidor Python: muda só o controller da API. É isso que MVVM compra: capacidade de evoluir cada parte independentemente, sem reescrever o resto.
11. React Native · Como funciona por baixo
React Native permite que você escreva aplicativos para Android e iOS usando apenas JavaScript/TypeScript com a biblioteca React. O segredo dele é que ele não renderiza um site dentro do celular. Ele traduz o seu código JavaScript em componentes nativos de cada sistema operacional.
11.1 · O que acontece com um <Text>
Quando você escreve <Text>Olá</Text>:
- No Android: ele vira um
TextViewnativo. - No iOS: ele vira um
UITextViewnativo.
Isso garante que o app tenha a cara, a velocidade e a performance de um aplicativo feito do jeito tradicional — porque, na prática, é nativo. A diferença é só que a árvore de componentes e a lógica de interação ficam em JS.
11.2 · O motor por baixo · Hermes + Bridge
Hoje em dia, o React Native funciona principalmente através de:
- Hermes — um motor de JavaScript otimizado para mobile. Tem startup rápido, uso baixo de memória e bytecode pré-compilado. É o que executa seu código JS no celular.
- JSI (JavaScript Interface) / Bridge — a estrutura que comunica o JavaScript diretamente com o código nativo (C++, Java/Kotlin no Android, Objective-C/Swift no iOS). Sem essa ponte, JS não consegue tocar widget nativo.
- Fabric + TurboModules — a "Nova Arquitetura" do RN, que renderiza componentes e expõe módulos nativos de forma mais eficiente e tipada que o sistema antigo.
JavaScript é interpretado, mas Hermes faz ahead-of-time compilation para bytecode, então o app já abre rápido. Em paralelo, a maior parte do trabalho pesado (animação, scroll, rasterização) acontece em código nativo, não em JS. JS coordena, o nativo executa. Resultado: você programa com flexibilidade de web, mas entrega performance de mobile.
11.3 · O que isso significa pra nossa arquitetura MVVM
A camada View do MVVM (que renderiza JSX) vive dentro do mundo Hermes/Bridge. A ViewModel e a Model são JS/TS puros — não dependem de chamada nativa especial. Quando o Model faz fetch('/api/...'), essa chamada de rede sim usa a API nativa do SO via Bridge — mas isso é transparente para você.
12. Como e onde compilar um APK
O APK (Android Package Kit) é o arquivo instalável para dispositivos Android. A forma como você vai gerar esse arquivo depende do tipo de projeto React Native que você criou. Existem três caminhos principais — vamos do mais comum ao mais avançado.
12.1 · Android Studio · o caminho oficial (e pesado)
Android Studio é o IDE oficial do Google para desenvolvimento Android. Ele inclui o SDK do Android, ferramentas de build, e o AVD (Android Virtual Device) — um emulador que simula um celular Android no seu computador.
É excelente para debug profundo: dá logs nativos, profiler de memória, inspeção de layout. Mas tem um custo real:
- Download do SDK: ~3-6 GB.
- Imagens de AVD: ~3-8 GB cada (você normalmente tem 2-3 imagens diferentes).
- Precisa de HAXM (Intel) ou Hyper-V/WSA (Windows) ou Apple Hypervisor (Mac).
- RAM gorda — recomendado mínimo 16GB no host, com pelo menos 4GB livre para o emulador.
- Boot do AVD: 1 a 3 minutos. Em máquina modesta, pode travar.
Quando vale a pena: quando você precisa de debug nativo, quando vai testar comportamento em versões específicas do Android, ou quando seu app usa módulos nativos customizados.
12.2 · Expo · o caminho recomendado pra começar
O Expo é uma plataforma construída sobre React Native que tira de cima de você toda a complexidade do build nativo. Você não precisa do Android Studio para começar. Tem dois componentes principais que mudam a vida:
Expo Go · seu celular é o emulador
Expo Go é um app gratuito (Play Store / App Store) que você instala no seu celular real. Quando você roda npx expo start no seu projeto, ele gera um QR code. Você escaneia esse QR pelo Expo Go e o app abre — rodando seu código sem precisar instalar nada. Hot reload via Wi-Fi enquanto você edita o código no computador. Acessa câmera, GPS, notificações e sensores de forma nativa, porque é um app nativo de verdade rodando.
EAS Build · APK compilado na nuvem
Quando você precisa de um APK propriamente dito (para instalar fora do Expo Go, ou para distribuir), use o EAS Build. Os servidores do Expo compilam o APK na nuvem deles — você não precisa ter Android Studio nem JDK instalados.
# 1. instala o CLI do EAS
npm install -g eas-cli
# 2. faz login na sua conta Expo
eas login
# 3. configura o projeto de build (uma vez por projeto)
eas build:configure
# 4. gera um APK de teste pra instalar direto no celular
eas build --platform android --profile preview
# Quando terminar, o Expo dá um link pra baixar o .apk
12.3 · React Native CLI · sem Expo, controle total
Se você criou o projeto com o CLI puro do React Native (sem Expo), você compila localmente. Aqui precisa ter Android Studio + JDK instalados e configurados, com as variáveis de ambiente apontando para o SDK.
# 1. abra o terminal na pasta do projeto
# 2. entre na pasta android
cd android
# 3. rode o Gradle pra gerar o APK de debug
./gradlew assembleDebug
# O APK gerado fica em:
# meu-projeto/android/app/build/outputs/apk/debug/app-debug.apk
# Pra release (assinado), use assembleRelease
./gradlew assembleRelease
Comece com Expo + Expo Go. Você testa direto no seu celular via QR code, sem esperar emulador travar, e acessa câmera/GPS/notificações de forma nativa enquanto desenvolve. Quando precisar do APK final, eas build entrega na nuvem em poucos minutos. Android Studio só se você precisar mesmo — para debug profundo, módulos nativos customizados, ou requisitos específicos que o Expo não cobre.
13. Caminho de uma requisição · código completo
Agora vamos amarrar tudo: do toque do usuário na tela até a procedure rodando no banco. O exemplo é "top-N recomendações do usuário 42". Cada bloco abaixo representa uma camada do MVVM mais o backend.
13.1 · View · só renderiza
import { FlatList, RefreshControl } from 'react-native';
import { useRecommendations } from './useRecommendations';
import { RecoCard } from './RecoCard';
export function RecommendationsScreen({ userId }: { userId: number }) {
const { recos, loading, refetch } = useRecommendations(userId);
return (
<FlatList
data={recos}
keyExtractor={(item) => String(item.id)}
refreshControl={
<RefreshControl refreshing={loading} onRefresh={refetch} />
}
renderItem={({ item }) => <RecoCard item={item} />}
/>
);
}
Observe: zero lógica de dados. A View não sabe que existe API, que existe banco. Ela só usa o hook e renderiza. Se trocássemos a fonte de dados, essa tela continuaria a mesma.
13.2 · ViewModel · hook custom
import { useState, useEffect, useCallback } from 'react';
import { RecoRepo, Reco } from './RecoRepo';
export function useRecommendations(userId: number) {
const [recos, setRecos] = useState<Reco[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const refetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await RecoRepo.topN(userId, 10);
setRecos(data);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
}, [userId]);
useEffect(() => { refetch(); }, [refetch]);
return { recos, loading, error, refetch };
}
O hook é a ViewModel. Cuida do estado, expõe três coisas para a View (recos, loading, error) e um trigger (refetch). Não conhece JSX — é puramente lógica.
13.3 · Model · Repository + API
export type Reco = { id: number; nome: string; score: number };
const API = process.env.EXPO_PUBLIC_API_URL ?? 'http://localhost:3000';
export const RecoRepo = {
topN: async (userId: number, top: number): Promise<Reco[]> => {
const r = await fetch(`${API}/users/${userId}/recos?top=${top}`);
if (!r.ok) throw new Error('falha ao buscar recos');
return r.json();
}
};
import express from 'express';
import { Pool } from 'pg';
const app = express();
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
app.get('/users/:id/recos', async (req, res) => {
try {
const userId = Number(req.params.id);
const top = Number(req.query.top ?? 10);
// CALL da procedure · ponta da cadeia
const { rows } = await pool.query(
'SELECT * FROM sp_top_recomendacoes_usuario($1, $2)',
[userId, top]
);
res.json(rows);
} catch (e) {
res.status(500).json({ error: (e as Error).message });
}
});
app.listen(3000);
13.4 · Procedure · a ponta da cadeia
E no fim, a procedure que vimos lá no início do material — a mesma que você vai escrever na atividade prática:
CREATE OR REPLACE PROCEDURE sp_top_recomendacoes_usuario(
p_user_id INTEGER,
p_top INTEGER DEFAULT 10
)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT i.id, i.nome, r.score
FROM recomendacao r
JOIN item i ON i.id = r.item_id
WHERE r.user_id = p_user_id
ORDER BY r.score DESC
LIMIT p_top;
END;
$$;
Se trocássemos PostgreSQL por SQL Server amanhã, só uma linha mudaria: a do pool.query no server.ts (que passaria a usar a sintaxe T-SQL). A View, o hook, o Repository — tudo continua igual. Esse é o ganho do MVVM bem aplicado: cada camada protege a anterior das mudanças da seguinte.
14. Atividade prática · em grupo
Criar o arquivo db/procedures.sql no repositório do grupo com:
- Procedure 1 · Top-N recomendações — recebe
p_user_idep_top, retorna a lista ordenada. - Procedure 2 · Registrar interação — INSERT em
interacao+ UPDATE emestatistica_item, dentro de uma transação com tratamento de exceção. - Function escalar · Média de ratings — recebe
p_item_ide retorna oNUMERICda média. - Function tabular · Itens similares — recebe
p_item_ide retorna até 5 itens mais parecidos. - README curto em
db/README.mdexplicando cada um, com exemplo de chamada.
Entrega
Merge Request feat(db): procedures e functions no repositório do grupo até o fim do dia (18h00).
Excelência (opcional, mas conta)
- Tratamento de erro com
EXCEPTION WHENem PostgreSQL (ouTRY/CATCHem SQL Server). - 1 índice criado para suportar a procedure principal — incluir o
CREATE INDEXna migration e justificar no README. - 1 ADR (Architecture Decision Record) curto justificando a decisão de empurrar essas operações para o banco.
Sair às 18h sem 2 procedures + 2 functions rodando contra a base do projeto + migration versionada coloca o grupo em débito técnico para a aula 5 (Transactions e Triggers, dia 22/05). A aula 5 vai envolver suas procedures em transações de isolamento mais sofisticadas — sem elas prontas, fica complicado acompanhar.
15. Conexões com as próximas aulas
15.1 · Aula 5 (22/05) · Transactions e Triggers
Vamos aprofundar transações: níveis de isolamento (READ COMMITTED, REPEATABLE READ, SERIALIZABLE), deadlocks, locking. E entrar em triggers (BEFORE / AFTER / INSTEAD OF), que reagem automaticamente a eventos no banco — e que muitas vezes chamam procedures que você escreveu hoje.
15.2 · Aula 6 (25/05) · FrontEnd Mobile + MVVM com React Native
Vamos construir o lado mobile do stack que você viu na seção 13: View, ViewModel, Model. As suas procedures vão virar endpoints na API e, em última instância, dados na tela do app que o usuário do parceiro vai ver. Tudo que você implementou hoje é peça do quebra-cabeça que se monta na aula 6.
15.3 · Aula 7 (28/05) · Orientação a objetos com TDD
A camada de ViewModel/Model do MVVM vai começar a precisar de classes, interfaces e testes unitários. Vamos voltar ao código mobile com olhar de orientação a objetos disciplinada.
15.4 · Aula 8 (08/06) · Testes de integração
Como testar uma procedure? Como testar um endpoint que chama uma procedure? Como testar um hook que chama um endpoint que chama uma procedure? Tudo isso na aula 8.
16. Bibliografia e referências
16.1 · Bancos de dados
- PL/pgSQL — Procedural Language · PostgreSQL Official Docs.
- CREATE PROCEDURE (Transact-SQL) · Microsoft Docs.
- CREATE FUNCTION (Transact-SQL) · Microsoft Docs.
- Use The Index, Luke! · Markus Winand — referência canônica sobre índices e plano de execução.
- Brent Ozar — artigos sobre parameter sniffing e diagnóstico de planos em SQL Server.
16.2 · Migrations e CI/CD
- Flyway Documentation · Redgate.
- Liquibase Quickstart.
- node-pg-migrate · alternativa para stacks Node.
16.3 · React Native e MVVM
- React Native · Getting Started · Documentação oficial.
- React Native · Architecture Overview · Nova arquitetura (Fabric, Hermes, TurboModules).
- Expo Documentation · Inclui guia do Expo Go e EAS Build.
- Expo · EAS Build Setup — passo a passo para compilar APK na nuvem.
- Martin Fowler · Presentation Model · texto fundacional sobre MVVM.
16.4 · Android Studio
- Android Studio Overview · Google Developers.
- Run apps on the Android Emulator · documentação do AVD.