Modelo 3C — Abordagens Preliminares
O modelo 3C (Cartão, Conversa e Confirmação) foi utilizado para documentar as histórias de usuário do Blabry. Cada requisito passou por uma conversa entre o P.O e o desenvolvedor antes de ser implementado, garantindo clareza nos critérios de aceitação.
Esta seção registra as abordagens preliminares sobre requisitos não funcionais relacionados ao frontend — performance, responsividade, acessibilidade, segurança e manutenibilidade.
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F01, RNF-F02, RNF-F03, RNF-F04 |
| Como | usuário do Blabry |
| Quero | que a aplicação carregue rapidamente e responda de forma fluida |
| Para | ter uma experiência agradável sem esperas perceptíveis |
Conversa
P.OQuando o usuário abre o Blabry pela primeira vez, quanto tempo ele pode esperar antes de ver a tela de login? E quando navega entre o feed e o chat, essa transição pode ter algum delay perceptível ou precisa ser instantânea?
DevEle não deve esperar mais que 2 segundos em condições ideais. De acordo com o tempo de resposta da API, até que esse retorno chegue, será mostrada uma animação de carregamento para que o usuário tenha um feedback sobre o status do sistema, segundo a primeira heurística de Nielsen: Visibilidade do Status do Sistema. Após essa espera, o usuário já estará pronto a realizar as primeiras ações: cadastro e/ou login.
P.OE sobre as transições entre seções — essa animação de slide que aparece no design — ela pode ter um delay de até quantos milissegundos antes de parecer travada para o usuário?
DevA animação não tem um delay, mas a sua duração fica em torno de 200 a 300ms, para ser fluida. No entanto, dependendo do desempenho do computador do cliente, um delay pode aparecer, mas não por decisão de design da aplicação. A regra é: usuário clicou, animação executa imediatamente.
Confirmação
Print da tela de carregamento a ser adicionado após implementação.
Responsividade
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F05 |
| Como | usuário do Blabry |
| Quero | acessar a aplicação em qualquer dispositivo |
| Para | usar o Blabry no celular, tablet ou computador sem perda de funcionalidade |
Conversa
P.OVocê me mostrou o design tanto para desktop quanto para mobile. Existe algum tamanho de tela abaixo do qual o Blabry pode deixar de funcionar bem? Por exemplo, smartwatches ou telas muito pequenas estão fora do escopo?
DevSim. A aplicação foi pensada para computadores, tablets e celulares. Pode começar a ter problemas em dispositivos cujo viewport resulta em uma largura interna menor que 320 pixels.
Confirmação
Print das telas mobile a ser adicionado após implementação.
Acessibilidade
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F09, RNF-F10, RNF-F11 |
| Como | usuário do Blabry |
| Quero | que a interface seja minimamente acessível |
| Para | que leitores de tela consigam interpretar os elementos visuais |
Conversa
P.OO Blabry precisa funcionar para usuários que navegam apenas pelo teclado, sem mouse? E existe alguma preocupação com leitores de tela — usuários com deficiência visual que usam ferramentas como o NVDA ou VoiceOver?
DevNão, ainda não foram implementadas questões de acessibilidade senão alt text nos componentes para leitores de tela. A navegação com teclado ainda não é um recurso que faz parte da aplicação.
Confirmação
A ser validado após implementação.
Segurança
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F12, RNF-F13, RNF-F14 |
| Como | usuário do Blabry |
| Quero | que minhas credenciais sejam armazenadas e tratadas com segurança |
| Para | não ter minha conta comprometida por falhas de segurança no frontend |
Conversa
P.OO token JWT que o backend retorna após o login — onde ele deve ser armazenado no browser? E o que acontece quando o usuário fecha o browser e abre novamente — ele deve continuar logado ou precisa fazer login de novo?
DevInicialmente, será armazenado no localStorage. Contudo, haverá uma validação no back-end, onde o token tem uma duração que, por sua vez, ainda não foi definida. Mas após esse período, o token será invalidado e o usuário precisará fazer login novamente. Futuramente, quando houver a versão mobile em React Native, haverá outra verificação para saber o caráter da aplicação, e se for nativa, esse token não terá limites. Já se for na web, continua tendo.
P.OQuando o token expirar, o que o usuário vê?
DevO frontend detecta o token expirado e redireciona automaticamente para a tela de login.
Confirmação
A ser validado após implementação do fluxo de autenticação.
Feedback de Erros
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F08 |
| Como | usuário do Blabry |
| Quero | receber feedback claro quando algo der errado |
| Para | entender o que aconteceu e saber o que fazer a seguir |
Conversa
P.OQuando o usuário digita a senha errada ou um email que não existe, o que ele deve ver? Uma mensagem abaixo do campo específico, um toast no canto da tela, ou um alerta genérico no formulário?
DevUsaremos um toast com cor e mensagem de acordo com o erro.
Confirmação
Print do componente de toast a ser adicionado após implementação.
Manutenibilidade
Cartão
| Campo | Descrição |
|---|
| ID | RNF-F15, RNF-F16, RNF-F17 |
| Como | desenvolvedor do Blabry |
| Quero | que o código frontend seja organizado e previsível |
| Para | facilitar a manutenção e adição de novas funcionalidades sem quebrar o que já existe |
Conversa
P.OExiste alguma regra sobre como os componentes devem ser organizados? Por exemplo, um componente pode ter mais de uma responsabilidade, ou cada um deve fazer apenas uma coisa?
DevCada componente tem uma responsabilidade, uma vez que a aplicação está sendo construída seguindo princípios de SOLID. Portanto, um serviço dedicado a autenticação realiza autenticação. O serviço de cadastro realiza cadastro; nova postagem, novo comentário, tudo seguirá este padrão para evitar quebras no caso de uma nova funcionalidade surgir.
Confirmação
Backend
As seções a seguir registram as abordagens sobre requisitos do backend — integridade dos dados e proteção das credenciais. Diferentemente das anteriores, a Confirmação destas já é verificável por testes automatizados.
Fonte única das nacionalidades
Cartão
| Campo | Descrição |
|---|
| ID | RF17 |
| Como | usuário do Blabry |
| Quero | escolher minha nacionalidade em uma lista confiável durante o cadastro |
| Para | concluir o cadastro sem receber erro por ter escolhido uma opção inválida |
Conversa
P.OA lista de países é enorme e praticamente nunca muda. Por que não deixar essas opções fixas no próprio formulário do frontend? Seria uma requisição a menos, e a tela de cadastro carregaria mais rápido.
DevSeria, mas o banco não aceitaria. A coluna de nacionalidade do usuário é chave estrangeira para a tabela de países, então o banco só grava um código que exista lá. Se o formulário oferecesse uma opção que não está cadastrada, o usuário preencheria tudo, clicaria em criar conta e receberia um erro sem entender o motivo. A lista precisa sair do mesmo lugar que valida o dado.
P.OEntão o banco vira a fonte da verdade. Mas e se alguém enviar um código inválido direto para a API, sem passar pela tela?
DevAí a própria chave estrangeira recusa a inserção. É essa a vantagem de manter a restrição no banco em vez de só no formulário: o formulário é conveniência para o usuário, a chave estrangeira é a garantia. Duas camadas, com a mais interna sendo a que não dá para burlar.
P.OE a rota que devolve essa lista precisa de login?
DevNão pode precisar. Quem está se cadastrando ainda não tem token. Ela fica registrada antes do middleware de autenticação, junto com as rotas de login e cadastro.
Confirmação
| Critério de aceitação | Verificação | Resultado |
|---|
| A lista vem do banco, não do cliente | GET /countries consulta a tabela countries | Atendido |
| A rota é acessível sem autenticação | Requisição sem cabeçalho Authorization | 200 |
O formato devolvido é { country, name } | Comparação da resposta com o formato esperado | Atendido |
| Nenhum campo interno é exposto | Ausência de id na resposta | Atendido |
Verificado em tests/routes/countries.test.js.
Proteção das credenciais de acesso
Cartão
| Campo | Descrição |
|---|
| ID | RNF-B01, RNF-B02, RNF-B04 |
| Como | usuário do Blabry |
| Quero | que minha senha permaneça inacessível, mesmo para quem tenha acesso ao banco de dados |
| Para | não ter minha conta comprometida caso os dados da aplicação vazem |
Conversa
P.OA senha é guardada criptografada, certo? Se alguém conseguisse acesso ao banco, conseguiria descobrir as senhas dos usuários?
DevEla não é criptografada, é transformada em hash com bcrypt, o que é diferente. Criptografia é reversível com a chave; hash não tem volta. Nem eu consigo descobrir a senha de um usuário. Na hora do login eu aplico o mesmo processo na senha digitada e comparo os resultados.
P.OE existe risco de esse hash acabar aparecendo em algum lugar por engano? Numa resposta da API, num log?
DevEsse era o risco real, e por isso o hash deixou de ser um campo comum do objeto de usuário. Ele foi definido como propriedade não enumerável, então não aparece ao converter o objeto para JSON, nem ao copiar o objeto, nem ao imprimir no console. Continua acessível para quem pedir por ele explicitamente, que é o que a verificação de senha faz. A ideia é que vazar exija uma ação deliberada, em vez de depender de eu lembrar de remover o campo em cada resposta.
P.OQuando alguém erra o login, a mensagem deveria dizer se o problema foi o e-mail ou a senha? Ajudaria o usuário a se corrigir.
DevAjudaria o usuário e também ajudaria um atacante. Se a mensagem distingue os dois casos, qualquer pessoa consegue descobrir quais e-mails têm conta na plataforma, testando um por um. A mensagem é a mesma nos dois casos: credenciais inválidas.
Confirmação
| Critério de aceitação | Verificação | Resultado |
|---|
| A senha é armazenada como hash bcrypt | Comparação de senha correta e incorreta contra o hash | Atendido |
| O hash não aparece ao serializar o usuário | JSON.stringify do objeto e da resposta da API | Ausente |
| O hash não aparece ao copiar o objeto | Espalhamento do objeto de usuário | Ausente |
| O hash permanece acessível internamente | Acesso explícito ao campo | Atendido |
| A resposta de erro não revela se o e-mail existe | Comparação entre resposta de senha incorreta e de e-mail inexistente | Idênticas |
Verificado em tests/models/user.test.js e tests/routes/auth.test.js.
Visibilidade de dados pessoais no perfil
Cartão
| Campo | Descrição |
|---|
| ID | RF13, RF14, RNF-B17 |
| Como | usuário do Blabry |
| Quero | que meus dados pessoais fiquem visíveis apenas para mim |
| Para | manter meu e-mail e minha data de nascimento fora do alcance de outros usuários |
Conversa
P.OO perfil mostra e-mail, nascimento e nacionalidade. Quando outro usuário visita esse perfil, ele vê tudo isso também?
DevNão. E-mail e data de nascimento aparecem apenas para o dono do perfil. Nacionalidade permanece pública, porque é informação de apresentação, não de contato.
P.OE como você garante que isso não vai escapar em alguma tela nova no futuro?
DevA regra não fica na tela nem em cada rota, fica no próprio modelo de usuário. Existe um único ponto que monta o perfil, e ele só inclui os dados pessoais quando recebe explicitamente a indicação de que é o próprio dono. Qualquer rota nova que devolva um perfil passa por ali; o padrão é esconder.
P.OE se eu abrir o meu próprio perfil pelo endereço público, com o meu @ na URL?
DevContinua sendo o seu perfil, então os dados aparecem. A comparação é feita entre o dono do perfil e o usuário autenticado, não entre endereços.
Confirmação
| Critério de aceitação | Verificação | Resultado |
|---|
| E-mail e nascimento ficam ocultos no perfil público | Serialização de perfil de terceiro | null |
| E-mail e nascimento aparecem no próprio perfil | Serialização do perfil do dono | Atendido |
| Nacionalidade permanece pública | Presença do campo em ambos os casos | Atendido |
| O identificador do usuário vem do token, nunca da requisição | Rota /users/me sem parâmetro de identificação | Atendido |
| A data de nascimento não sofre conversão de fuso | Serialização de data armazenada em UTC | Data preservada |
Verificado em tests/models/user.test.js e tests/repositories/users.repository.test.js.