Nexus — Fase 1 6 episódios

Sequência da Fase 1 do Nexus — o MVP funcionando ponta a ponta. Auth (registro/login/JWT), CRUD de notas com ownership, frontend SvelteKit com runes, e a jornada do cliente amarrando tudo. 6 episódios, cada um com áudio + resumo + material completo.

01 - Visão GeralVISÃO GERAL

Panorama da Fase 1 em primeira pessoa — o que foi entregue, decisões e onde estamos

0:00 / 0:00

Resumo

A Fase 1 entregou o MVP do Nexus rodando local: autenticação completa (registro, login, JWT), CRUD de notas com isolamento entre usuários, e frontend SvelteKit consumindo a API. 4 passos concluídos em 23/04, passo 5 (deploy) pausado pra esperar o servidor próprio chegar.

Stack do MVP: Spring Boot 3.5.13 + Java 25 + Spring Security + JWT (HS256) + JPA/Hibernate + Flyway no backend; SvelteKit + Svelte 5 (runes) + TypeScript no frontend; PostgreSQL 16 local.

Decisões guiando: auth stateless com JWT (não sessão server-side — funciona com mobile e SPA), ownership na query SQL (segurança no banco, não só no controller), frontend separado do backend (REST puro), e BCrypt pro hash de senha (cost 10).

MVPJWTSvelteKitstateless
CÓDIGO COMPLETO
02 - User e MigrationJPA

Entidade JPA, anotações, V2 e a regra ddl-auto: validate

0:00 / 0:00

Resumo

User é a primeira entidade. Anotada com @Entity, @Table(name="users"), @Id @GeneratedValue(IDENTITY). Campos email (unique not null) e passwordHash (not null, guarda o BCrypt — nunca a senha). createdAt com @CreationTimestamp (Hibernate-specific, preenche no insert).

Migration V2__create_users.sql cria a tabela com índice único em email. Flyway aplica no startup, antes do Hibernate validar.

Configuração crítica: spring.jpa.hibernate.ddl-auto: validate. Diferente do create, update ou create-dropvalidate só valida que as classes batem com o schema, nunca cria nem altera. Schema é responsabilidade exclusiva do Flyway. Princípio: uma única fonte de verdade pra DDL.

@EntityFlyway V2ddl-auto: validate
CÓDIGO COMPLETO
03 - Auth e JWTAUTH

BCrypt, filter chain stateless, dois endpoints densos — registro e login

0:00 / 0:00

Resumo

Dois endpoints públicos: POST /auth/register e POST /auth/login. Ambos retornam { "token": "<jwt>" }. Senha vai como BCrypt (cost 10) — hash + salt embutido, jamais plaintext no banco.

JWT: HS256, segredo no application.yml (overridable via env NEXUS_AUTH_JWT_SECRET), expiração 7 dias. Claim principal é sub = email. Não guarda nada sensível no token (token vai pro localStorage do browser).

Filter chain stateless: JwtAuthenticationFilter roda em cada request, lê o header Authorization: Bearer ..., valida o JWT, popula o SecurityContext com o User. Spring Security configurado com SessionCreationPolicy.STATELESS — nada de cookie de sessão. GlobalExceptionHandler normaliza erros de validação (400) e JSON malformado.

BCryptJWT HS256statelessfilter chain
CÓDIGO COMPLETO
04 - Note e CRUDCRUD

@ManyToOne LAZY, ownership na query SQL, DTOs, saveAndFlush no update

0:00 / 0:00

Resumo

Entidade Note com @ManyToOne(fetch = LAZY) pra User. Migration V3 cria tabela notes com FK pra users e ON DELETE CASCADE — apagar usuário apaga notas dele. Title obrigatório, content nullable.

Cinco endpoints REST: POST /notes, GET /notes, GET /notes/{id}, PUT /notes/{id}, DELETE /notes/{id}. Ownership na query SQL: findByIdAndUser e findByUserOrderByUpdatedAtDesc filtram por user_id direto no banco. Tentar acessar nota de outro user retorna 404 (não 403 — não revela existência).

DTOs: NoteRequest (entrada com Bean Validation: title @NotBlank @Size, content nullable) e NoteResponse (saída — não vaza User aninhado, só os campos da nota). Mapeamento via static factory NoteResponse.from(note).

saveAndFlush no update: força flush imediato pro @UpdateTimestamp popular o updatedAt antes de montar o DTO de resposta. Sem flush, o response volta com updatedAt antigo. (Em 28/04 isso foi refatorado pra DTO com OffsetDateTime.now().)

@ManyToOne LAZYownership SQLDTOsFlyway V3
CÓDIGO COMPLETO
05 - Frontend SvelteKitFRONTEND

Svelte 5 runes ($state/$effect/$derived), .svelte.ts, fetch wrapper, auth guard

0:00 / 0:00

Resumo

SvelteKit com Svelte 5 runes: $state (reativo simples), $effect (efeitos colaterais), $derived (computado), $props. Estado global de autenticação em auth.svelte.ts — extensão .svelte.ts habilita runes em arquivos TypeScript. Token persiste no localStorage, hidratado no boot.

Fetch wrapper em lib/api.ts: anexa Authorization: Bearer <token> automaticamente, trata 401/403 limpando token (e o usuário cai no login), parseia JSON ou retorna undefined em 204. Erros viram ApiException com status + message + fields de validação.

Rotas: /login, /register, / (lista), /notes/new, /notes/[id]. Auth guard no +layout.svelte via $effect: rotas privadas redirecionam pra /login se não autenticado. Textarea simples pro conteúdo (rich-text vem na Fase 2).

Svelte 5runesfetch wrapperauth guard
CÓDIGO COMPLETO
06 - Jornada do ClienteJORNADA

5 cenários end-to-end: primeiro acesso, retorno, expiração, multi-usuário, logout

0:00 / 0:00

Resumo

Episódio que amarra o circuito. Os 5 cenários do agente:

1. Primeiro acesso — usuário abre /, sem token, redireciona pra /register, registra, recebe JWT, volta pra lista vazia.

2. Retorno (token válido) — token no localStorage ainda dentro dos 7 dias, fetch da lista de notas vai com Authorization: Bearer, backend valida JWT no filter, retorna notas do user.

3. Token expirado — fetch retorna 401, wrapper de api limpa token do localStorage, usuário cai no login.

4. Dois usuários simultâneos — User A e User B logados em browsers diferentes. findByUserOrderByUpdatedAtDesc filtra por user_id — A nunca vê notas do B mesmo que tente GET /notes/<id-do-B> (404).

5. Logout — clica em logout, frontend só apaga o token do localStorage. Backend stateless não precisa "saber" — JWT abandonado expira sozinho. Sem revogação de token nesta fase (rolaria com blacklist em Redis na Fase 4+).

jornadastatelessmulti-usercenários
CÓDIGO COMPLETO