Visão Geral da Arquitetura

Este é o mapa do sistema como ele roda em produção: cada superfície de cliente, a API por trás delas, o banco de dados, como código novo chega à produção e como descobrimos quando algo quebra. O diagrama acima mostra o quadro completo; as seções abaixo percorrem cada peça.

Stack tecnológica

Camada Tecnologias
Web app React 18, TypeScript, Vite, Redux Toolkit, Axios, react-hook-form + Zod, i18next (en/pt), Tailwind CSS 3. O caderno de estudos acrescenta o BlockNote sobre o Mantine 8 para o editor e o React Flow para o quadro de roadmap. O quadro de Ideias também é React Flow
App mobile React Native + Expo (Android primeiro), NativeWind, TypeScript. Divide os pacotes de estado, cliente de API e i18n com o web app pelo monorepo
Backend Spring Boot 4.1, Java 25 (virtual threads), Spring Security, JWT (auth0 java-jwt), Spring AOP, Spring AI para o chat do agente, as sugestões de onboarding e a IA do caderno de estudos (cadeia de fallback de LLMs), PDFBox e jsoup para as fontes do caderno
Banco de dados PostgreSQL 17 em produção (um cluster CloudNativePG; os testes e as stacks locais rodam o 15), schema controlado pelo Flyway (o Hibernate valida, nunca escreve), chaves primárias UUID, cache Caffeine na frente das leituras quentes
Entrega GitHub Actions constrói a imagem do backend para o GHCR; o Flux (GitOps) faz o rollout dela num cluster k3s de um nó; o CI faz o deploy dos builds do web e da documentação no Cloudflare Workers Static Assets com o wrangler; Docker Compose para desenvolvimento local e e2e
Monitoramento kube-prometheus-stack (Prometheus, Grafana), Loki + Alloy, GlitchTip (compatível com Sentry)

Topologia de produção

A produção roda num cluster k3s de um nó cujo estado inteiro vive no Git e é aplicado pelo Flux, com a Cloudflare servindo os dois sites estáticos. O lado da aplicação são três peças, todas construídas pelo CI:

O banco é o beyou, num cluster PostgreSQL 17 compartilhado, operado pelo CloudNativePG no namespace data. Fazer merge na main é o deploy. No backend, o CI publica uma imagem :latest nova, a automação de imagens do Flux registra o digest dela no Git e faz o rollout, e voltar atrás é um git revert. Os dois sites estáticos vão do próprio CI para a Cloudflare com wrangler deploy, e é lá também que voltam atrás. Nada no servidor escuta numa porta pública. Um Cloudflare Tunnel disca para fora a partir do servidor e entrega os hostnames públicos do cluster ao Traefik, o único ingress, que roteia por host para cada Ingress; todo namespace fica atrás de uma network policy default-deny. O tópico de infraestrutura cobre a máquina, o cluster e os backups. Mais duas peças vivem fora do cluster: o site de marketing em beyouweb.com (HTML estático no Cloudflare Pages) e o app mobile, que é instalado no celular e fala com a mesma API do web app.

O conteúdo deste site de documentação tem seu próprio pipeline: o markdown e as specs OpenAPI vivem no repositório beyou-arch-design, o backend os importa para o PostgreSQL, e uma mudança de conteúdo reconstrói o site de documentação pré-renderizado e faz o deploy dele na Cloudflare.

Monitoramento

Em produção a stack de observabilidade roda dentro do cluster como kube-prometheus-stack mais Loki, Alloy e GlitchTip; localmente um overlay do Compose responde as mesmas perguntas. Cada componente responde uma pergunta diferente:

O Grafana fica em cima dos três, com dashboards carregados do Git: saúde do serviço do backend, o agente de IA, uso do produto, logs, o host e o ingress. Ele também manda por e-mail os alertas sobre a máquina e o cluster (memória, disco, pods reiniciando ou travados, alvos fora do ar), enquanto o GlitchTip fica com os da aplicação. Ele vive em obs.beyouweb.com, atrás do próprio login. Esses dois são as únicas superfícies públicas de monitoramento; Prometheus, Loki e os exporters não têm hostname e só são alcançáveis pelo Grafana.

Modelo de domínio

erDiagram
  USER ||--o{ CATEGORY : possui
  USER ||--o{ HABIT : possui
  USER ||--o{ TASK : possui
  USER ||--o{ GOAL : possui
  USER ||--o{ ROUTINE : possui
  USER ||--o{ NOTEBOOK_PAGE : possui
  USER ||--o{ CALENDAR_TASK : possui
  USER ||--o{ QUICK_NOTE : possui

  CATEGORY }o--o{ HABIT : marcado
  CATEGORY }o--o{ TASK : marcado
  CATEGORY }o--o{ GOAL : marcado

  ROUTINE ||--o{ ROUTINE_SECTION : contem
  ROUTINE ||--|| SCHEDULE : "agendada por"

  ROUTINE_SECTION ||--o{ HABIT_GROUP : agrupa
  ROUTINE_SECTION ||--o{ TASK_GROUP : agrupa

  HABIT_GROUP }o--|| HABIT : referencia
  TASK_GROUP }o--|| TASK : referencia

  HABIT_GROUP ||--o{ HABIT_GROUP_CHECK : registra
  TASK_GROUP ||--o{ TASK_GROUP_CHECK : registra

  NOTEBOOK_PAGE ||--o{ NOTEBOOK_PAGE : "pai de"
  NOTEBOOK_PAGE ||--o{ BOARD_NODE : "quadro de"
  BOARD_NODE }o--|| NOTEBOOK_PAGE : abre
  NOTEBOOK_PAGE ||--o{ FLASHCARD : guarda
  NOTEBOOK_PAGE ||--o{ SOURCE : lê

  QUICK_NOTE |o--o| TASK : "virou"

Destaques das entidades

Fluxo de autenticação

sequenceDiagram
  participant U as Usuário
  participant FE as Frontend
  participant BE as Backend
  participant GO as Google

  rect rgba(59, 130, 246, 0.25)
  U->>FE: 🔑 Login com email + senha
  FE->>BE: POST /auth/login
  BE-->>FE: JWT (header) + Refresh Token (cookie HttpOnly)
  FE->>FE: Guarda o JWT em memória
  end

  rect rgba(234, 88, 12, 0.25)
  U->>FE: 🔐 Login com Google OAuth
  FE->>GO: Redirecionamento de autorização
  GO-->>FE: Código de autorização
  FE->>BE: GET /auth/google?code=...
  BE->>GO: Troca o código por access token
  GO-->>BE: Perfil do usuário
  BE-->>FE: JWT + Refresh Token
  end

  rect rgba(16, 185, 129, 0.25)
  FE->>BE: 🔄 Refresh: requisição com JWT expirado
  BE-->>FE: 401 Unauthorized
  FE->>BE: POST /auth/refresh (cookie)
  BE-->>FE: Novo JWT + novo Refresh Token
  FE->>BE: Repete a requisição original
  end

Detalhes dos tokens

Camada de API

37 controllers REST organizados por domínio, todos sob /api/v1:

Grupo Controllers Caminhos base
Auth Authentication, AuthVerification /auth/*
Entidades principais Category, Habit, Task, Goal /category, /habit, /task, /goal
Rotinas Routine, Schedule, Snapshot /routine, /schedule, /routine/snapshot
Histórico CheckHistory, XpHistory /check-history, /xp
Modo Foco Focus /focus/cycles, /focus/micro-tasks, /focus/day
Diário Mood /mood
Calendário Calendar /calendar, /calendar/tasks
Quadro de Ideias QuickNote /quick-notes
Caderno de estudos Notebook, NotebookBoard, NotebookCard, NotebookSource, NotebookStudy, NotebookAi, StudyCycle /notebook/*
Resumo do Dia DailyBriefing /daily-briefing
Usuário User, UserPhoto, UserExport /user, /user/photo
IA AiAgent, Onboarding /ai/agent, /onboarding
Feedback Feedback, FeedbackAdmin /feedback, /feedback/admin
Notificações NotificationPreferences /notification/preferences, /notification/unsubscribe
Docs Architecture, Blog, Api, Project, Search, Import /docs/*

Padrão de requisição/resposta

flowchart LR
  REQ["📥 Requisição"] --> FILT["🛡️ Filtro de segurança<br/>Validação do JWT"]
  FILT --> CTRL["🎯 Controller<br/>Validação de DTO"]
  CTRL --> SVC["⚙️ Service<br/>Lógica de negócio"]
  SVC --> REPO["💾 Repository<br/>Consultas JPA"]
  REPO --> DB[("🐘 PostgreSQL")]
  DB --> REPO
  REPO --> SVC
  SVC --> MAP["🔄 Mapper<br/>Entidade → DTO"]
  MAP --> CTRL
  CTRL --> RES["📤 Resposta"]

Gerenciamento de estado (frontend)

flowchart TD
  AX["Axios + Interceptor"]
  ST["Redux Store<br/>packages/state compartilhado"]
  PS["redux-persist<br/>localStorage (web)"]
  UI["Componentes<br/>React / React Native"]

  UI -->|"dispatch de actions"| ST
  ST -->|"selectors"| UI
  ST <-->|"hidrata / persiste"| PS
  UI -->|"chamadas de API"| AX
  AX -->|"dispatch no sucesso"| ST
  AX -->|"401 → refresh automático"| AX

Os slices do Redux vivem em um pacote compartilhado do workspace (packages/state, 22 slices), então o web e o mobile rodam a mesma lógica de estado. O web app o envolve com redux-persist e exclui de propósito os slices de perfil, snapshot, celebration, mood, notebook, studyCycles e quickNotes, para que nenhum dado pessoal, texto de diário, anotação de estudo ou ideia caia no localStorage. O app mobile guarda a store em memória e rebusca ao abrir.

Sistema de gamificação

flowchart LR
  ACT["✅ Check de hábito/tarefa<br/>na rotina"] --> XP["🎮 Calculadora de XP"]
  XP --> UXP["👤 XP do usuário<br/>+ level up"]
  XP --> HXP["💪 XP do hábito<br/>+ level up"]
  XP --> CXP["📂 XP da categoria<br/>+ level up"]
  XP --> RXP["📋 XP da rotina<br/>+ level up"]
  ACT --> STR["🔥 Constância<br/>rastreio de streak"]