Repository navigation
Visao Geral do Negocio
Para quem é este artigo: para qualquer pessoa que vá ler, mudar ou estudar o PolicyDesk, inclusive quem nunca trabalhou com seguros. Não é preciso saber programar para acompanhar a primeira metade.
O que você vai aprender: o vocabulário de seguros que aparece no código, as regras que o sistema protege e por que cada uma existe.
Todo software existe para resolver um problema de alguém. Quando você abre um arquivo como Claim.cs sem saber o que é um sinistro, cada if parece arbitrário: "por que um sinistro negado não pode ser pago?". Quando você entende o negócio, o código passa a se explicar sozinho: é claro que não se paga uma indenização recusada.
Essa ideia tem nome: Domain-Driven Design (DDD), ou "design guiado pelo domínio". Domínio é a área de conhecimento que o software modela, aqui, seguros. O DDD defende que desenvolvedores e especialistas do negócio usem a mesma linguagem, chamada de linguagem ubíqua. Se o corretor fala "sinistro", o código tem uma classe Claim (sinistro em inglês), e não Request, Ticket ou Item.
Por isso este artigo vem primeiro. Ele é o "dicionário" que você vai consultar ao ler o resto.
Imagine que você tem um carro que vale R$ 50.000. Se ele for roubado, você perde tudo de uma vez. A chance de isso acontecer em um ano é pequena, mas o prejuízo seria enorme.
Uma seguradora propõe um acordo: você paga um valor pequeno e conhecido por ano (o prêmio), e ela se compromete a pagar o prejuízo grande e incerto caso ele aconteça (a indenização), até um limite combinado (a cobertura). Ela consegue fazer isso porque recebe prêmios de milhares de clientes, e só alguns têm prejuízo no mesmo ano.
Esse acordo é registrado em um contrato chamado apólice. Quando o evento ruim acontece, você abre um pedido de indenização, chamado sinistro. A seguradora analisa o pedido, aprova ou nega, e, se aprovar, paga.
flowchart LR
S["👤 Segurado<br/>a pessoa protegida"] -- "contrata e paga o prêmio" --> A["📄 Apólice<br/>o contrato"]
A -- "quando algo acontece" --> SI["🚨 Sinistro<br/>o pedido de indenização"]
SI -- "analisado e, se aprovado" --> P["💰 Pagamento"]
Esses três conceitos (segurado, apólice e sinistro) são as três peças centrais do PolicyDesk.
| Termo | No código | Explicação simples | Exemplo |
|---|---|---|---|
| Segurado | Policyholder |
A pessoa protegida pelo seguro e dona da apólice | Maria, CPF 529.982.247-25 |
| Apólice | Policy |
O contrato de seguro | "Seguro auto do Corolla de Maria" |
| Prêmio | PremiumAmount |
Quanto o segurado paga pela proteção. Não tem nada a ver com "ganhar um prêmio" | R$ 2.400 por ano |
| Cobertura | CoverageAmount |
O máximo que a seguradora paga por aquela apólice, somando todos os sinistros | R$ 50.000 |
| Vigência |
StartDate, EndDate
|
O período em que a apólice protege | 01/01/2026 a 31/12/2026 |
| Em vigor | IsInForce |
Uma apólice ativa cuja vigência inclui a data considerada | Em 15/10/2026, sim. Em 15/01/2027, não |
| Sinistro | Claim |
O pedido de indenização por um evento que aconteceu | "Colisão traseira em 15/10, R$ 8.000" |
| Indenização | — | O dinheiro pago ao segurado por um sinistro aprovado | R$ 8.000 transferidos para Maria |
| Negação | Deny |
A recusa de um sinistro, sempre com um motivo | "Evento não coberto pela apólice" |
| Cancelamento | Cancel |
O encerramento antecipado da apólice | Maria vendeu o carro em julho |
| Inativação | Deactivate |
Tirar um segurado do cadastro ativo, sem apagar o histórico | Maria deixou de ser cliente |
💡 Por que o código está em inglês e a API em português? O código segue a convenção da maior parte do mercado (nomes em inglês). A API, que é o que um cliente brasileiro consome, usa português (
valorCobertura,dataOcorrencia). A tradução acontece em um único lugar, nos objetos de entrada e saída. Veja Arquitetura.
O segurado é uma pessoa física, identificada pelo CPF. O sistema guarda nome, CPF, data de nascimento, e-mail e telefone.
Dois comportamentos merecem atenção, porque não são óbvios:
1. Ninguém é apagado de verdade. Quando um segurado deixa de ser cliente, ele é inativado: some das listagens, mas o registro continua no banco. Isso se chama exclusão lógica (soft delete). O motivo é o histórico: se Maria teve uma apólice e um sinistro pago em 2025, apagar o cadastro dela deixaria esses registros "órfãos", sem dono. Auditorias e a legislação de seguros exigem que esse histórico exista.
2. Voltar é reativar, não duplicar. Se Maria voltar a ser cliente e alguém cadastrar o CPF dela de novo, o sistema não cria uma segunda Maria. Ele reativa o cadastro antigo com os dados novos, e o identificador continua o mesmo. Assim, todo o histórico dela fica ligado a um único registro.
A apólice liga um segurado a uma proteção. Ela tem uma descrição, o valor da cobertura, o valor do prêmio e a vigência.
Uma apólice nasce Ativa e pode ser Cancelada. Mas "ativa" não quer dizer "protegendo hoje". Uma apólice ativa com vigência de 2027 ainda não começou, e uma com vigência de 2024 já terminou. O conceito que importa para os sinistros é em vigor: ativa e com a data dentro da vigência.
O sinistro é o pedido de indenização. Ele pertence a uma apólice e tem uma descrição, um valor e a data de ocorrência (quando o evento aconteceu, que pode ser diferente de quando o pedido foi aberto).
Um sinistro não é aprovado na hora. Ele passa por um fluxo, descrito mais abaixo.
Uma regra de negócio é uma condição que o sistema precisa garantir, não importa por onde a informação chegue. As regras abaixo são a especificação do PolicyDesk. Cada uma tem um código (S1, P2, C3...) para facilitar referências, e a coluna Onde aponta o arquivo que a garante, para quem quiser ver o código.
| # | Regra | Por que existe | Onde |
|---|---|---|---|
| S1 | Nome obrigatório, até 150 caracteres | Um cadastro sem nome não identifica ninguém; o limite evita textos absurdos |
Policyholder, PolicyholderDataValidator
|
| S2 | CPF obrigatório e válido; aceito com ou sem máscara e guardado só com dígitos | O CPF tem dígitos verificadores, então dá para recusar erros de digitação. Guardar só dígitos faz 529.982.247-25 e 52998224725 serem o mesmo CPF |
CpfValidator |
| S3 | E-mail válido, guardado em minúsculas |
Maria@Exemplo.com e maria@exemplo.com são o mesmo endereço |
EmailValidator |
| S4 | Telefone válido no país de destino; sem +, é tratado como brasileiro; guardado no formato E.164 |
Recusa DDDs que não existem e celulares sem o 9. Guardar em um formato único (+5511987654321) evita duplicidades por máscara |
PhoneValidator |
| S5 | Data de nascimento obrigatória e não futura | Ninguém nasce amanhã | Policyholder |
| S6 | O CPF é único em toda a base, entre ativos e inativos | Uma pessoa, um cadastro | Índice único em cpf
|
| S7 | Não pode haver dois segurados ativos com o mesmo e-mail | O e-mail é canal de contato; dois clientes ativos com o mesmo endereço indicam erro | Índice único filtrado |
| S8 | Cadastrar o CPF de um segurado inativo reativa o registro, mantendo o identificador | Preserva o histórico (veja acima) | PolicyholdersRepository |
| S9 | Não é possível inativar um segurado com apólice ativa em vigor hoje ou com vigência futura | Inativar alguém que ainda está protegido deixaria uma apólice válida sem titular ativo | DeactivatePolicyholderUseCase |
| S10 | O CPF não muda depois do cadastro | O CPF é a identidade da pessoa; trocá-lo seria trocar de pessoa |
Policyholder.Update não recebe CPF |
| # | Regra | Por que existe | Onde |
|---|---|---|---|
| P1 | Só pode ser emitida para um segurado existente e ativo | Não se vende seguro para quem não é cliente | RegisterPolicyUseCase |
| P2 | Descrição obrigatória, até 500 caracteres | Identifica o que está segurado | Policy |
| P3 | Cobertura e prêmio maiores que zero, com no máximo 2 casas decimais | Não existe seguro de valor zero nem centavo fracionado |
Policy, MoneyLimits
|
| P4 | A data de fim vem depois da data de início | Uma vigência ao contrário não faz sentido | Policy |
| P5 | Uma apólice cancelada não pode ser cancelada de novo | Cancelar duas vezes indica um erro de quem chamou, e o sistema avisa em vez de ignorar | Policy.Cancel |
| # | Regra | Por que existe | Onde |
|---|---|---|---|
| C1 | Só pode ser aberto em uma apólice existente e não cancelada | Uma apólice cancelada não protege mais | RegisterClaimUseCase |
| C2 | A data de ocorrência precisa estar dentro da vigência | O seguro só cobre eventos que aconteceram enquanto ele valia | Policy.IsInForce |
| C3 | A data de ocorrência não pode ser futura | Não se pede indenização por algo que ainda não aconteceu | RegisterClaimUseCase |
| C4 | O valor é maior que zero, com no máximo 2 casas decimais | Mesma lógica dos valores da apólice |
Claim, MoneyLimits
|
| C4.1 | A soma dos sinistros não negados da apólice, mais o novo valor, não pode passar da cobertura. Negar um sinistro libera o valor dele | A cobertura é o teto da apólice inteira, não de cada sinistro | Policy.RegisterClaim |
| C5 | O sinistro só segue o fluxo de status definido; qualquer outro caminho é recusado | Garante que nada seja pago sem passar por análise e aprovação | Claim |
| C6 | Negar exige um motivo, de até 1000 caracteres | O segurado tem direito de saber por que o pedido foi recusado | Claim.Deny |
A regra do saldo da cobertura costuma confundir, então vale um exemplo. A apólice de Maria tem cobertura de R$ 10.000.
| Passo | Ação | Sinistros que contam | Saldo disponível | Resultado |
|---|---|---|---|---|
| 1 | Abre sinistro A de R$ 8.000 | A (8.000) | 2.000 | ✅ Aceito |
| 2 | Abre sinistro B de R$ 8.000 | A (8.000) | 2.000 | ❌ Recusado: 8.000 + 8.000 > 10.000 |
| 3 | O sinistro A é analisado e negado | nenhum | 10.000 | O valor de A foi liberado |
| 4 | Abre sinistro B de R$ 8.000 de novo | B (8.000) | 2.000 | ✅ Aceito |
Repare que no passo 2 o sinistro A ainda nem tinha sido aprovado. Mesmo assim ele conta, porque pode vir a ser pago. Só um sinistro negado deixa de contar.
Várias regras falam de "hoje": a data de nascimento não pode ser futura (S5), a data de ocorrência não pode ser futura (C3), e uma apólice "em vigor hoje" impede a inativação (S9). Parece simples, mas tem uma armadilha.
Servidores costumam trabalhar em UTC, o horário de referência mundial. O horário de Brasília é UTC−3: quando são 22h do dia 10 em Brasília, já é 1h do dia 11 em UTC. Se o sistema perguntasse "que dia é hoje?" em UTC, entre 21h e meia-noite ele acharia que já é amanhã. Nesse intervalo, aceitaria um sinistro com a data de amanhã, o que a regra C3 proíbe.
Por isso o PolicyDesk tem o conceito de fuso horário de negócio: todas as regras perguntam o dia de hoje no fuso America/Sao_Paulo, que é configurável. Às 22h do dia 10 em Brasília, para o sistema, hoje é dia 10. O artigo Modelo de Domínio mostra como isso é feito no código.
Algumas coisas no sistema mudam de "estado" ao longo do tempo, e só podem mudar de certas formas. Um jeito claro de mostrar isso é um diagrama de estados: cada caixa é um estado possível, e cada seta é uma mudança permitida. Se não existe seta entre dois estados, aquela mudança é proibida.
stateDiagram-v2
direction LR
[*] --> Pendente: abrir
Pendente --> EmAnalise: iniciar análise
EmAnalise --> Aprovado: aprovar
EmAnalise --> Negado: negar (com motivo)
Aprovado --> Pago: registrar pagamento
Negado --> [*]
Pago --> [*]
| Estado na API | No código | Significado | O que pode acontecer depois |
|---|---|---|---|
Pendente |
Pending |
Acabou de ser aberto, ninguém olhou ainda | Iniciar a análise |
EmAnalise |
InAnalysis |
Um analista está avaliando | Aprovar ou negar |
Aprovado |
Approved |
A indenização foi aprovada e espera pagamento | Registrar o pagamento |
Negado |
Denied |
Foi recusado, com motivo registrado | Nada: é um estado final |
Pago |
Paid |
A indenização foi paga | Nada: é um estado final |
Leia o diagrama e tente responder: dá para pagar um sinistro pendente? Não, porque não existe seta de Pendente para Pago. Dá para aprovar um sinistro já negado? Também não. Essas perguntas viram regras no código, e a resposta "não" vira uma mensagem de erro para quem chamou a API.
Cada mudança também registra uma informação: a aprovação grava a data de aprovação, a negação grava o motivo, e o pagamento grava a data de pagamento.
stateDiagram-v2
direction LR
[*] --> Ativa: emitir
Ativa --> Cancelada: cancelar
Cancelada --> [*]
Simples: uma apólice nasce ativa e pode ser cancelada uma vez.
stateDiagram-v2
direction LR
[*] --> Ativo: cadastrar
Ativo --> Ativo: atualizar dados
Ativo --> Inativo: inativar (sem apólice em vigor ou futura)
Inativo --> Ativo: cadastrar o mesmo CPF (reativação)
Com a API rodando (veja Primeiros Passos), tente quebrar as regras de propósito. Cada tentativa deve ser recusada com uma mensagem clara:
- Cadastre um segurado com o CPF
111.111.111-11. (Regra S2: todos os dígitos iguais passam no cálculo, mas não são emitidos pela Receita.) - Emita uma apólice com cobertura de R$ 10.000 e abra dois sinistros de R$ 8.000. (Regra C4.1.)
- Tente pagar um sinistro que acabou de ser aberto. (Regra C5.)
- Tente inativar um segurado que tem uma apólice em vigor. (Regra S9.)
- Envie o mesmo pedido de cadastro com
Accept-Language: en. As mensagens vêm em inglês.
Um sistema de seguros real é muito maior. Para manter o foco na engenharia, o PolicyDesk não tem:
- Login de usuários e controle de quem pode fazer o quê.
- Cálculo do prêmio (o valor é informado na emissão; na vida real, ele é calculado por atuários a partir do risco).
- Várias coberturas por apólice, franquia (a parte do prejuízo que fica com o segurado) e carência (o período inicial sem cobertura).
- Pagamento parcial e integração com bancos.
Cada um desses itens é um bom exercício. O Tutorial: Nova Funcionalidade mostra como adicionar uma regra nova do começo ao fim.
- O PolicyDesk modela três coisas: segurados, apólices e sinistros.
- O código usa a mesma linguagem do negócio (linguagem ubíqua), em inglês no código e em português na API.
- As regras existem para proteger a seguradora e o cliente: identidade única, cobertura respeitada, nada pago sem análise.
- "Hoje" é calculado no fuso de Brasília, não em UTC.
- O fluxo de um sinistro é uma máquina de estados: só as mudanças desenhadas no diagrama são permitidas.
- Domain-Driven Design, Eric Evans: o livro que criou os termos "domínio" e "linguagem ubíqua".
- Domain-Driven Design Distilled, Vaughn Vernon: uma introdução curta e prática ao mesmo assunto.
- Próximo artigo sugerido: Primeiros Passos, para colocar o sistema para rodar.
PolicyDesk · Distribuído sob a licença MIT · Repositório
Comece aqui
Como o sistema é feito
A API
Engenharia
Prática
Consulta