Músicas — Decisões de Design
Gerado pelo Writer (Reversa) em 2026-05-11
doc_level: detalhado
DECISION-01 — FTS5 com tabela virtual musica_busca para busca textual
Status: Implementado (desde o início do projeto)
Contexto: O catálogo pode conter milhares de músicas. A busca precisa ser rápida, offline e tolerante a acentos.
Decisão: Tabela virtual FTS5 musica_busca espelhando musica.titulo (peso BM25 = 50) e musica.letra (peso BM25 = 5). Tokenizer unicode61 para suporte a acentos nativo + normalização NFKD aplicada no código antes do MATCH.
Alternativas descartadas:
- LIKE com índice — não suporta ranking, lento em textos longos
- Busca em memória — não escalável
- API de busca remota — viola requisito de uso offline
Consequências:
- 🟢 Busca sub-100ms em catálogos de 5k+ músicas
- 🟢 Ranking automático por relevância via BM25
- 🟡 Tabela FTS5 precisa ser mantida sincronizada com
musica(via triggers ou re-insert)
Rastreabilidade: docs/reversa/adrs/004-sqlite-fts5-banco-principal.md | src/repository/musicas/MusicasRepository.ts
DECISION-02 — Estratégia tripla de busca (semTermo / porIndice / porTermo)
Status: Implementado
Contexto: A busca precisa lidar com três intenções distintas do usuário: navegar o catálogo, buscar pelo número do hinário físico, e buscar por texto livre.
Decisão: Padrão Strategy com 3 implementações selecionadas por getDetalhesTermoBusca:
- Entrada vazia →
semTermo(ORDER BY titulo) parseInt(termo)válido →porIndice(WHERE cantico_livro.indice = ?)- Qualquer outro texto →
porTermo(FTS5 MATCH)
Consequências:
- 🟢 UX fluida: usuário com hinário físico em mãos digita o número e acha a música
- 🟡 Índices alfanuméricos (ex.: "23a") não são reconhecidos como índice — fallback para FTS5 pode não encontrar
Rastreabilidade: src/repository/musicas/MusicasRepository.ts — BuscaStrategies
DECISION-03 — LIMIT n+1 para detecção de hasNextPage sem COUNT(*)
Status: Implementado
Contexto: Paginação infinite scroll precisa saber se há mais resultados, sem executar um COUNT(*) separado.
Decisão: Consultar LIMIT pageSize + 1. Se retornar pageSize + 1 registros, hasNextPage = true e o último item é descartado. Caso contrário, hasNextPage = false.
Consequências:
- 🟢 Evita dupla query (data + count)
- 🟢 Performático em listas grandes
- 🟢 Padrão amplamente conhecido, sem surpresas
Rastreabilidade: src/repository/musicas/MusicasRepository.ts — findAllBy
DECISION-04 — Redux EntityAdapter para cache de músicas por id
Status: Implementado
Contexto: A tela de letra e a tela de lista são navegadas frequentemente de forma alternada. Recarregar do banco a cada navegação é desnecessário.
Decisão: createEntityAdapter do Redux Toolkit mantém um mapa {id_musica → InformacoesDetalhadasMusica} em memória. carregarMusica verifica esse mapa antes de ir ao banco.
Consequências:
- 🟢 Zero latência ao retornar para uma música já carregada
- 🟡 Cache não é invalidado automaticamente — músicas atualizadas via obras da Biblioteca podem ficar desatualizadas até reiniciar o app
Rastreabilidade: src/store/Musicas/Musicas.reducer.ts
DECISION-05 — Campo YouTube (singular) mantido como deprecated
Status: Deprecated, mantido por compatibilidade
Contexto: O backend evoluiu para suportar múltiplos vídeos YouTube por música. O campo singular existia antes dessa mudança.
Decisão: Manter midias.YouTube?: string no modelo mas usar midias.YouTubes?: string[] como fonte de verdade. O campo singular não deve ser removido até que todas as músicas antigas sejam migradas.
Consequências:
- 🟢 Compatibilidade retroativa com músicas do catálogo mais antigo
- 🟡 Pontos de consumo na UI devem tratar o fallback explicitamente
Rastreabilidade: src/model/Musica.ts | commit feat(youtube): support to api with multiple youtube ids
DECISION-06 — Configuração adaptativa phone/tablet via MusicaConfiguration
Status: Implementado
Contexto: Tablets têm telas maiores e podem exibir trechos maiores da letra na listagem sem comprometer o layout.
Decisão: Arquivo de configuração retorna constantes diferentes com base em isTablet():
- Phone:
TOTAL_CARACTERES=100,TOTAL_TOKENS=12 - Tablet:
TOTAL_CARACTERES=250,TOTAL_TOKENS=24
Consequências:
- 🟢 UX aprimorada no tablet sem código condicional espalhado
- 🟡 A detecção
isTablet()pode ser imprecisa em alguns dispositivos Android com tela grande
Rastreabilidade: src/repository/musicas/MusicaConfiguration.ts