Exemplo de diagrama de estados: o ciclo de vida de uma assinatura como máquina de estados

Um diagrama de máquina de estados UML resolvido para uma assinatura SaaS: estados, eventos, guardas, escolhas, um estado composto e os bugs que ele pega cedo.

· Fundador e desenvolvedor

Publicado em

Exemplo de diagrama de estados: o ciclo de vida de uma assinatura como máquina de estados

Bugs de assinatura são bugs de estado. Um cliente continua com acesso depois de cancelar. Uma conta com pagamento atrasado paga e continua bloqueada. Um teste termina para alguém sem cartão, e nenhuma regra diz o que acontece depois. Cada um desses casos é uma transição que ninguém desenhou.

Um diagrama de máquina de estados, normalmente chamado de diagrama de estados, lista cada estado em que um objeto pode estar e cada evento que o leva para outro. Transições que faltam aparecem na revisão, e não nos tickets de suporte. Este artigo constrói um para uma assinatura SaaS, com os status documentados pelo Stripe Billing para que você possa comparar o modelo com um sistema real, e explica a notação à medida que ela aparece.

Quando desenhar uma máquina de estados

Desenhe uma quando um único objeto tiver um ciclo de vida com regras: um pedido, uma assinatura, um pagamento, um ticket de suporte, um deploy. Os sinais de alerta são conhecidos:

  • Uma coluna status com mais de três valores e código que a verifica em muitos lugares.
  • Flags booleanas como is_canceled e is_paused que podem se contradizer.
  • Discussões sobre se algo "pode acontecer" a partir de um determinado status.

Um diagrama de estados é a imagem errada para uma conversa entre vários participantes, em que um diagrama de sequência mostra a ordem das mensagens, e para um processo de negócio com muitas pessoas e repasses, em que um diagrama BPMN se encaixa melhor.

A notação

Elemento Notação Significado
Estado Retângulo com cantos arredondados Uma condição em que o objeto fica até chegar um evento
Pseudoestado inicial Pequeno círculo preenchido Onde a máquina começa
Estado final Círculo preenchido dentro de um anel A máquina terminou
Transição Seta com o rótulo event [guard] / action Muda quando o evento acontece e a guarda é verdadeira
Escolha Losango As guardas nas setas de saída decidem o destino
Estado composto Um estado que contém estados Regras compartilhadas por tudo o que está dentro
Comportamento interno entry /, exit /, do / Executa ao entrar, ao sair ou enquanto está no estado

Cada parte do rótulo de uma transição é opcional. invoice paid é um evento sozinho, [setting: unpaid] é uma guarda sozinha e cancel / revoke access é um evento com uma ação. A sintaxe e os símbolos vêm da especificação UML 2.5.1.

Os status da assinatura

O exemplo usa os status da visão geral de assinaturas do Stripe, com pagamentos automáticos com cartão:

Status O que significa Acesso
incomplete O primeiro pagamento ainda não foi feito; o cliente tem 23 horas Não
incomplete_expired O primeiro pagamento nunca foi concluído; nada é cobrado Não
trialing Em período de teste Sim
active Em dia Sim
past_due O pagamento da última fatura falhou ou não foi tentado Você decide
unpaid As novas tentativas se esgotaram e suas configurações escolheram este resultado Não
paused O teste terminou sem forma de pagamento, e a pausa foi escolhida Não
canceled Encerrada; o Stripe a descreve como um estado terminal Não

Muitos produtos mantêm o acesso durante past_due enquanto as novas tentativas de pagamento acontecem, e este exemplo também. Escreva essa decisão no diagrama; é a que as pessoas esquecem.

Construindo as transições

Pegue cada estado e pergunte quais eventos podem acontecer nele.

  1. Início. subscribe [trial] leva a trialing; subscribe [no trial] leva a incomplete.
  2. Primeiro pagamento. De incomplete, invoice paid leva a active. Se 23 horas passarem sem pagamento, a assinatura vai para incomplete_expired e o Stripe anula a fatura. Esse estado é final: para tentar de novo, o cliente inicia uma nova assinatura.
  3. Fim do teste. trial ends vai para uma escolha. [payment succeeds] leva a active; [no payment method] leva a paused neste exemplo. O Stripe deixa você escolher o que acontece nesse caso: criar uma fatura (o padrão), cancelar ou pausar. Desenhe a configuração que você realmente usa, não as três.
  4. Renovações. De active, renewal payment fails leva a past_due, e invoice paid a traz de volta.
  5. Tentativas esgotadas. Outra escolha: dependendo das suas configurações, a assinatura vai para unpaid ou canceled, ou continua em past_due. De unpaid, pagar a fatura a devolve para active.
  6. Saída. cancel leva active a canceled. Cancelar no fim do período não é um estado à parte: a assinatura continua active com uma flag até o período acabar e então vai para canceled.

Um estado composto para o acesso

Aqui, três estados dão acesso: trialing, active e past_due. Desenhá-los dentro de um único estado composto, Access granted, permite escrever a regra uma vez só: entry / grant access e exit / revoke access. Qualquer transição que saia do composto revoga o acesso, seja qual for o estado de origem, então nenhum caminho pode esquecer disso. Mudar de estado dentro do composto, por exemplo de trialing para active, não sai dele e não executa essas ações.

Um ponto inicial leva a trialing ou a incomplete. Trialing, active e past due ficam dentro de uma moldura Access granted. O fim do teste passa por uma escolha para active ou paused. Uma renovação que falha leva a past due; sem tentativas, vai para unpaid ou canceled e depois ao fim.

15 componentes · 17 conexões

Abrir no Archboard
Conteúdo do diagrama
  • Access granted
  • trialing
  • active
  • past_due
  • paused
  • incomplete
  • incomplete_expired
  • unpaid
  • canceled
  • subscribe [trial]
  • subscribe [no trial]
  • invoice paid
  • after 23 h / void invoice
  • trial ends
  • [payment succeeds]
  • [no payment method]
  • resume [payment succeeds]
  • renewal payment fails
  • retries exhausted
  • [setting: unpaid]
  • [setting: cancel]
  • cancel
Uma máquina de estados para uma assinatura SaaS com os status documentados pelo Stripe Billing: o teste e o primeiro pagamento, um estado composto para todos os status que dão acesso, as renovações que falham e as duas formas de uma assinatura terminar.

Como ler:

  • No alto. O ponto inicial se divide conforme o teste: trialing dentro do composto ou incomplete fora dele. De incomplete, o pagamento leva a active, e 23 horas sem pagamento levam a incomplete_expired e ao estado final à direita.
  • No meio, Access granted. trial ends chega a uma escolha: active quando o pagamento é concluído, paused à esquerda quando não há forma de pagamento. Uma renovação que falha leva active a past_due, e o pagamento a traz de volta.
  • Embaixo. Quando as tentativas se esgotam, uma segunda escolha manda a assinatura para unpaid ou canceled. unpaid ainda pode voltar a active pagando; canceled termina no estado final.
  • As conexões que entram na moldura dão acesso, e as que saem o revogam.

Do diagrama ao código

O diagrama vira uma tabela de transições. Tudo o que não está na tabela é rejeitado:

type Status = 'incomplete' | 'incomplete_expired' | 'trialing' | 'active'
  | 'past_due' | 'unpaid' | 'paused' | 'canceled';

const transitions: Record<Status, Partial<Record<string, Status>>> = {
  incomplete: { invoice_paid: 'active', expired: 'incomplete_expired' },
  trialing: { trial_ended_paid: 'active', trial_ended_no_method: 'paused' },
  active: { payment_failed: 'past_due', cancel: 'canceled' },
  past_due: { invoice_paid: 'active', retries_exhausted: 'unpaid' },
  unpaid: { invoice_paid: 'active' },
  paused: { resumed: 'active' },
  incomplete_expired: {},
  canceled: {},
};

function next(status: Status, event: string): Status {
  const target = transitions[status][event];
  if (!target) throw new Error(`${event} is not allowed in ${status}`);
  return target;
}

Se os eventos vêm dos webhooks de um provedor de pagamentos, não conte com a ordem. A documentação de webhooks do Stripe diz que ele não garante que os eventos cheguem na ordem em que foram gerados. Trate cada evento como um motivo para buscar o status atual da assinatura e aplique a transição sobre ele.

Erros comuns

  • Eventos faltando. Para cada estado, verifique cada evento. "O que acontece se o cliente cancelar enquanto está em past_due?" é uma pergunta que o diagrama deve responder.
  • Flags desenhadas como estados. Cancelar no fim do período, um desconto ou um número de assentos são propriedades de um estado, não estados.
  • Ações desenhadas como estados. "Enviar e-mail de cobrança" é uma ação em uma transição ou um comportamento entry, não um lugar onde a assinatura espera.
  • Nenhum estado final. Mostre onde o ciclo de vida termina, para que ninguém escreva código que ressuscite uma assinatura cancelada.
  • Guardas que se sobrepõem ou deixam lacunas. As guardas que saem de uma escolha devem cobrir cada caso exatamente uma vez; adicione [else] se precisar.
  • Dois ciclos de vida em um diagrama. A assinatura e suas faturas têm máquinas de estados separadas. Ligue-as por eventos em vez de juntá-las.

Mantenha o diagrama ao lado do código que o implementa e revise os dois no mesmo pull request, como descrito em diagramas de arquitetura que não ficam desatualizados.

Monte no Archboard

  1. Entre com Google ou GitHub e crie um diagrama.
  2. Do pack UML, adicione um Estado para cada status.
  3. Adicione uma Moldura do pack Básico chamada Access granted em volta de trialing, active e past_due.
  4. Adicione do pack Básico uma pequena Elipse preenchida para o estado inicial, um Losango para cada escolha e um Ícone de círculo com ponto para cada estado final.
  5. Conecte os estados com conexões rotuladas event [guard] / action.
  6. Adicione uma Nota UML com as ações de entrada e saída do estado composto.

O exemplo usa 14 nós de diagrama e fica dentro do plano Free. Com um agente de IA conectado via MCP, você pode colar o enum de status e deixar que ele esboce os estados, e depois conferir cada transição você mesmo.

Perguntas frequentes

Qual a diferença entre um diagrama de estados e um fluxograma? Um fluxograma mostra os passos de um processo em ordem. Um diagrama de estados mostra as condições em que um objeto pode estar e quais eventos o movem entre elas, então ele responde o que é permitido, e não o que vem a seguir.

Cada valor da minha coluna de status deve ser um estado? Cada valor que muda o comportamento, sim. Se dois valores se comportam igual em todos os casos, junte-os; se um valor esconde dois comportamentos, divida-o.

Onde os webhooks entram numa máquina de estados? Eles são eventos. Verifique-os, busque o status atual no provedor e aplique a transição, porque os eventos podem chegar fora de ordem ou mais de uma vez.

Preciso de um estado composto? Use um quando vários estados compartilham uma regra, como acesso ou cobrança. Ele transforma uma regra que você repetiria em muitas setas em uma ação de entrada e uma de saída.

Quão detalhados devem ser os rótulos de transição? Nomeie o evento em termos do domínio e adicione uma guarda só quando duas setas saírem com o mesmo evento. Coloque no diagrama as ações que importam para a correção, como conceder acesso.

Comece grátis — um assento, sem cartão de crédito.

Artigos relacionados

← Voltar ao blog

Abra um canvas em um minuto

Entre com Google ou GitHub. Grátis para um assento. Faça upgrade quando a equipe entrar.