flowchart LR
Cliente[Cliente]
Atendente[Atendente]
Gerente[Gerente]
UC1(["Realizar venda"])
UC2(["Consultar produto"])
UC3(["Aplicar desconto"])
UC4(["Emitir relatório"])
UC5(["Cadastrar produto"])
Cliente --- UC2
Atendente --- UC1
Atendente --- UC2
Atendente --- UC3
Gerente --- UC4
Gerente --- UC5
Gerente --- UC3
style Cliente fill:#cfe2ff,stroke:#084298
style Atendente fill:#cfe2ff,stroke:#084298
style Gerente fill:#cfe2ff,stroke:#084298
style UC1 fill:#d1e7dd,stroke:#0f5132
style UC2 fill:#d1e7dd,stroke:#0f5132
style UC3 fill:#fff3cd,stroke:#664d03
style UC4 fill:#d1e7dd,stroke:#0f5132
style UC5 fill:#d1e7dd,stroke:#0f5132
Módulo 08 — Modelagem de Requisitos: Casos de Uso e Histórias de Usuário
Onde estamos. No módulo anterior aprendemos a levantar e registrar requisitos: descobrir o que o sistema precisa fazer e sob quais restrições. Agora damos o passo seguinte. Ter uma lista de requisitos é uma coisa; organizá-los de modo que sejam compreensíveis, discutíveis e verificáveis é outra. Aqui você vai aprender duas linguagens para modelar requisitos de comportamento — casos de uso e histórias de usuário — e, tão importante quanto, entender quando cada uma serve melhor.
Deixe-me abrir com uma provocação honesta. Você já viu uma especificação escrita como uma lista interminável de frases começando com “o sistema deve”: cadastrar clientes, validar o CPF, emitir relatórios. Não são erradas, mas têm um problema silencioso: descrevem funções isoladas e perdem o fio da meada. Quem lê não enxerga quem usa o sistema, para quê, nem em que ordem. A modelagem de requisitos existe para devolver esse fio: em vez de funções soltas, ela conta histórias de uso — alguém, com um objetivo, interagindo com o sistema até obter um resultado que lhe interessa. É sobre essas duas maneiras de contar essas histórias que vamos conversar.
O papel da modelagem de requisitos
Um requisito bruto, isolado, é ambíguo — não diz quem o usa, em que contexto, nem o que se espera quando algo dá errado. Modelar é dar forma a esse material para que comunique intenção. Uma boa modelagem serve a três públicos: ao usuário e ao cliente, que reconhecem no modelo o próprio negócio e validam se entendemos corretamente; à equipe de desenvolvimento, que o usa como ponte para o projeto; e à equipe de teste, que precisa saber o que verificar. Guarde essa ideia dos três leitores — ela explica a diferença entre casos de uso e histórias adiante.
Bezerra, ao tratar de análise e projeto orientados a objetos, insiste que a modelagem de requisitos é a fronteira entre o problema e a solução: de um lado, as necessidades reais das pessoas; do outro, o software que construiremos. Se essa tradução falhar, todo o esforço posterior resolverá o problema errado com perfeição técnica — o pior dos desperdícios.
Casos de uso: a anatomia de uma interação
O caso de uso descreve o comportamento do sistema do ponto de vista de quem o utiliza. A ideia, popularizada na UML e discutida em profundidade por Larman, é simples e poderosa: em vez de descrever o que o sistema tem, descrevemos o que alguém consegue fazer com ele. Precisamos de dois conceitos fundamentais.
O primeiro é o ator: qualquer entidade externa ao sistema que interage com ele para atingir um objetivo. Atenção à palavra “externa”: o ator não faz parte do sistema, ele o usa. Pode ser uma pessoa em um papel — cliente, atendente, administrador —, mas também outro sistema ou um dispositivo. E há uma sutileza a reter: o ator é um papel, não uma pessoa concreta. A mesma pessoa pode ser, em momentos diferentes, o ator “atendente” e o “administrador”. Modelamos papéis, porque é o papel que define objetivos e permissões.
O segundo conceito é o caso de uso: uma sequência completa de interações entre um ou mais atores e o sistema, que produz um resultado de valor observável para algum ator. “Sequência completa” significa que ele vai do início ao fim de um objetivo, não é um passo isolado; “valor observável” significa que, ao final, o ator obteve algo que lhe interessava. “Realizar uma venda” é um caso de uso; “clicar no botão salvar” não é — é só um passo dentro de algo maior.
Iniciantes transformam cada tela ou botão em um caso de uso, produzindo dezenas de “casos” minúsculos que não contam história nenhuma. O teste é sempre o mesmo: qual objetivo do ator este caso satisfaz? Se a resposta for “nenhum, é só um passo intermediário”, você não tem um caso de uso, tem um fragmento de outro.
O diagrama de casos de uso
O diagrama de casos de uso é a visão panorâmica: mostra, de uma vez, quais atores existem e quais objetivos cada um pode alcançar. Não descreve como nada acontece — para isso serve a descrição textual —, mas oferece um mapa das fronteiras do sistema. É a primeira coisa que mostro a um cliente, porque ele reconhece os próprios papéis e valida o escopo.
A UML desenha atores como bonecos e casos de uso como elipses dentro de uma caixa que representa a fronteira do sistema. O Mermaid não tem tipo nativo para isso, então usaremos um flowchart: nós retangulares para atores, arredondados para casos de uso, ligações indicando quem participa de quê. Considere a venda em uma pequena loja.
Veja o que esse mapa comunica sem esforço: o cliente só consulta produtos; o atendente vende e aplica descontos no balcão; o gerente administra e também aplica descontos. “Aplicar desconto” é compartilhado por dois atores — pista de um comportamento reutilizável, exatamente o tipo de relação de que falaremos agora.
Fluxo principal, fluxos alternativos e exceções
O diagrama mostra que casos de uso existem; a riqueza está em descrever como cada um transcorre. E aqui aparece o conceito que separa o modelo ingênuo do maduro: raramente uma interação segue um único caminho. Todo caso de uso tem um fluxo principal, ou cenário de sucesso, que é a sequência ideal — tudo dá certo, o ator faz o esperado e obtém o resultado. Mas a realidade tem desvios, e por isso descrevemos também os fluxos alternativos e as exceções.
Um fluxo alternativo é um caminho válido diferente do principal: o cliente paga com cartão em vez de dinheiro — não é erro, é variação legítima que também leva ao sucesso. Já uma exceção é um desvio provocado por condição de erro: o pagamento é recusado, o produto não tem estoque, a conexão cai. Larman ressalta que a maior parte da complexidade real — e dos defeitos — mora nesses fluxos que não são o caminho feliz. Quem só modela o fluxo principal descreve o sistema fácil de construir e ignora o difícil de operar.
Ao terminar o fluxo principal, faça a pergunta incômoda em cada passo: “e se isto falhar?”. Cada resposta é um fluxo de exceção que, se não for modelado agora, virá cobrar seu preço como defeito em produção. Modelar exceções é o antídoto barato para o retrabalho caro.
As relações entre casos de uso: inclusão, extensão e generalização
Ao modelar vários casos de uso, percebemos que alguns comportamentos se repetem ou se relacionam. A UML oferece três relações, e não confundi-las importa, porque cada uma comunica uma intenção diferente.
A inclusão («include») representa um comportamento sempre executado como parte de outro. É a fatoração de um trecho comum: se “Realizar venda” e “Devolver produto” ambos precisam “Validar cliente”, extraímos esse caso como incluído por ambos. É obrigatória e incondicional.
A extensão («extend») representa um comportamento opcional, acionado sob certa condição, acoplado a um ponto do caso base. “Aplicar cupom” pode estender “Realizar venda”, mas só se o cliente apresentar um cupom. A diferença: inclusão é “sempre acontece”; extensão é “às vezes, sob condição”.
A generalização aplica-se a atores e a casos de uso e expressa “é um tipo de”. Um ator “Gerente” pode ser generalização de “Atendente”, herdando seus objetivos e acrescentando os próprios. Entre casos, “Pagar com cartão” e “Pagar com dinheiro” podem especializar um “Efetuar pagamento” genérico.
A tabela a seguir consolida as três relações, porque a confusão entre elas é uma das falhas mais frequentes em modelos de casos de uso.
| Relação | Notação | Intenção | Condicional? |
|---|---|---|---|
| Inclusão | «include» |
Fatorar comportamento comum e obrigatório | Não, sempre executa |
| Extensão | «extend» |
Acoplar comportamento opcional em um ponto | Sim, sob condição |
| Generalização | Seta de herança | Expressar “é um tipo de” entre atores ou casos | Não se aplica |
A descrição textual estruturada de um caso de uso
O diagrama é o índice; a descrição textual é o conteúdo, onde registramos passo a passo como a interação transcorre. Bezerra recomenda uma estrutura padronizada, e um gabarito fixo garante que ninguém esqueça as pré-condições, os fluxos alternativos ou as exceções. Um caso de uso bem descrito contém o nome e o objetivo, os atores, as pré-condições (o que precisa ser verdade para começar), o fluxo principal numerado, os alternativos, as exceções e as pós-condições (o que passa a ser verdade ao final).
Veja um esboço da descrição do caso de uso “Realizar venda”, em forma resumida para você reconhecer o gabarito.
Caso de uso: Realizar venda. Ator principal: Atendente. Pré-condição: atendente autenticado, caixa aberto. Fluxo principal: (1) informa os produtos; (2) o sistema calcula o total; (3) informa a forma de pagamento; (4) o sistema registra o pagamento e emite o comprovante. Fluxo alternativo (3a): pagamento com cartão exige autorização da operadora. Exceção (2a): produto sem estoque interrompe a venda com aviso. Pós-condição: venda registrada, estoque atualizado.
Repare como essa descrição revela requisitos que a frase “o sistema deve permitir vender” jamais tornaria explícitos: autenticação, controle de caixa, integração com a operadora e baixa de estoque. Modelar é, em grande parte, tornar visível o que estava implícito.
Histórias de usuário: o requisito enxuto do mundo ágil
Vamos à segunda linguagem. As histórias de usuário nasceram nos métodos ágeis e têm espírito bem diferente. Enquanto o caso de uso busca completude e detalhe antecipado, a história busca o mínimo para iniciar uma conversa: ela não é o requisito completo, é uma promessa de conversa sobre ele. Essa é a filosofia ágil: em vez de especificar tudo de uma vez, especificamos o suficiente para começar e detalhamos na hora de construir.
A forma mais difundida segue três partes: “Como <papel>, quero <objetivo> para <benefício>”. Por exemplo: “Como cliente, quero consultar o preço de um produto para decidir se vou comprá-lo”. A genialidade discreta está na terceira parte: ela obriga a justificar por que a funcionalidade importa, e muitas morrem — merecidamente — quando alguém tenta preencher o “para” e descobre que não há benefício real.
O “para <benefício>” não é decoração: é o filtro de valor da história. Se você não consegue completar essa parte com um benefício honesto para o papel citado, provavelmente está diante de uma funcionalidade que ninguém pediu. Escreva sempre as três partes, e desconfie das histórias que resistem à terceira.
Os critérios INVEST
Como saber se uma história está bem escrita? O acrônimo INVEST reúne seis qualidades e virou o padrão de fato. Uma boa história deve ser Independente — desenvolvível sem depender de outra; Negociável — ponto de partida para conversa, não contrato rígido; Valiosa — entrega valor perceptível; Estimável — a equipe avalia o esforço; Pequena (small) — cabe num ciclo curto; e Testável — há jeito objetivo de verificar se foi cumprida. A tabela detalha cada critério e o sintoma de sua violação.
| Critério | O que exige | Sintoma da violação |
|---|---|---|
| Independente | História autônoma, sem acoplamento forte | “Só dá para fazer depois daquela outra” |
| Negociável | Espaço para conversa, não contrato fechado | Detalhes de implementação travados cedo demais |
| Valiosa | Valor perceptível ao usuário ou ao negócio | Ninguém sabe dizer para que serve |
| Estimável | Clareza suficiente para dimensionar o esforço | “Não faço ideia de quanto tempo leva” |
| Pequena | Cabe em um ciclo curto de trabalho | História que “nunca termina” |
| Testável | Existe critério objetivo de aceitação | Impossível dizer se está pronta |
Critérios de aceitação e o backlog
Se a história é enxuta e negociável, o que a torna testável? Os critérios de aceitação: condições concretas e verificáveis que definem quando ela está pronta. Enquanto a história diz o que se quer, os critérios dizem como saberemos que foi entregue. Para a consulta de preço, poderiam ser: o preço inclui impostos; produtos sem estoque aparecem como indisponíveis; a busca por nome parcial retorna resultados. Cada critério pode ser confirmado ou negado — é essa objetividade que fecha a lacuna deixada pela concisão da história.
O conjunto das histórias ainda não desenvolvidas forma o backlog do produto: uma lista viva e priorizada de tudo que o produto pode vir a ter. Ele evolui continuamente. As do topo tendem a ser pequenas e detalhadas, prontas para desenvolver; as do fundo, grandes e vagas, aguardando refinamento. Essa priorização contínua garante que a equipe esteja sempre trabalhando no que tem mais valor agora.
O domínio em código Dart
Antes de comparar as abordagens, quero ancorar tudo em algo concreto. Toda modelagem aponta para um modelo de domínio — as entidades e regras do negócio que o software manipula. O diagrama de classes a seguir mostra o domínio da loja, com as entidades que emergiram da descrição dos casos de uso.
classDiagram
class Produto {
+String nome
+double preco
+int estoque
+bool disponivel()
}
class ItemVenda {
+int quantidade
+double subtotal()
}
class Venda {
+DateTime data
+double total()
+adicionar(Produto, int)
}
class Cliente {
+String nome
}
Venda "1" *-- "*" ItemVenda : contém
ItemVenda "*" --> "1" Produto : refere
Venda "*" --> "0..1" Cliente : associada a
Traduzindo o modelo para Dart, obtemos as entidades com suas regras encapsuladas. Note como cada classe carrega comportamento, não só dados — o subtotal de um item e o total de uma venda pertencem ao próprio objeto.
class Produto {
final String nome;
final double preco;
int estoque;
Produto(this.nome, this.preco, this.estoque);
bool disponivel(int quantidade) => estoque >= quantidade;
}
class ItemVenda {
final Produto produto;
final int quantidade;
ItemVenda(this.produto, this.quantidade);
double subtotal() => produto.preco * quantidade;
}
class Venda {
final DateTime data;
final Cliente? cliente;
final List<ItemVenda> itens = [];
Venda(this.data, {this.cliente});
void adicionar(Produto produto, int quantidade) {
if (!produto.disponivel(quantidade)) {
throw Exception('Estoque insuficiente para ${produto.nome}');
}
itens.add(ItemVenda(produto, quantidade));
produto.estoque -= quantidade;
}
double total() =>
itens.fold(0.0, (soma, item) => soma + item.subtotal());
}
class Cliente {
final String nome;
Cliente(this.nome);
}Perceba a ligação direta entre requisito e código: a exceção lançada em adicionar quando falta estoque é exatamente o fluxo de exceção do caso de uso “Realizar venda”. O requisito não morreu no documento; reapareceu, encarnado, como regra do domínio. Essa continuidade entre requisito e implementação é o que buscamos ao modelar com cuidado.
Casos de uso ou histórias de usuário: uma comparação honesta
Chegamos à pergunta que já deve ter se formado na sua cabeça: qual usar? A resposta honesta é que elas não competem tanto quanto parecem — servem a contextos diferentes, e escolher errado custa caro.
O caso de uso brilha quando o sistema tem interações complexas, muitos fluxos alternativos e exceções, e quando há necessidade de documentação detalhada e duradoura — sistemas críticos, contratos formais, domínios regulados. Sua força é a completude: um caso de uso bem escrito sobrevive à equipe que o criou. Sua fraqueza é o peso: escrever e manter descrições completas é caro, e em mudança rápida esse detalhamento envelhece antes de ser usado.
A história de usuário brilha em contextos ágeis e de descoberta incremental, onde os requisitos amadurecem com o uso do produto. Sua força é a leveza e o convite à conversa: barata de escrever, fácil de repriorizar, focada no valor. Sua fraqueza é depender da conversa presencial e da memória da equipe — uma história isolada comunica pouco, e funciona mal como documentação de longo prazo porque foi concebida para ser efêmera.
Quanto maior a necessidade de documentação formal, completa e durável, mais o caso de uso compensa. Quanto maior a incerteza e a velocidade de mudança, mais a história compensa. Muitas equipes maduras combinam as duas: histórias para conversar e priorizar no dia a dia, casos de uso para documentar os fluxos críticos que precisam sobreviver ao tempo.
Síntese
Retenha três ideias. A primeira: modelar requisitos é dar forma e história ao material bruto — em vez de funções soltas, contamos quem usa o sistema, para quê e o que acontece quando algo dá errado; é nos fluxos de exceção que mora a complexidade real. A segunda: casos de uso e histórias têm filosofias opostas — o caso de uso busca completude e permanência, com atores, fluxos e relações de inclusão, extensão e generalização; a história busca o mínimo para iniciar uma conversa, guiada pelo papel-objetivo-benefício e pelos critérios INVEST. A terceira: a escolha é contextual, não ideológica — documentação durável pede casos de uso, mudança rápida pede histórias, e nada impede combiná-las.
Para consolidar antes da aula: escolha um sistema que você usa todo dia e escreva, para uma mesma funcionalidade, um esboço de caso de uso (com fluxo principal e uma exceção) e uma história de usuário no formato completo com dois critérios de aceitação. Ao comparar os dois textos lado a lado, você sentirá na prática a diferença de filosofia que discutimos, e é essa percepção que quero que você traga.