Pular para conteúdo

NotificationTemplates

Este documento descreve todas as operações disponíveis para o agregado NotificationTemplates e seus recursos, seguindo os padrões de Domain-Driven Design (DDD) e Command Query Responsibility Segregation (CQRS).

Índice


Visão Geral

O sistema de NotificationTemplates permite gerenciar templates de notificações enviadas aos usuários através de diferentes canais (Email, WhatsApp, SMS).

Características principais:

  • Multi-canal: Suporta Email, WhatsApp e SMS
  • Tipado: Templates são associados a um NotificationTemplateType que define as variáveis obrigatórias
  • Validação de Variáveis: Sistema valida se todas as variáveis obrigatórias foram preenchidas antes do envio
  • Contextual: Templates podem ser genéricos ou específicos para JobTasks
  • Priorizado: Sistema de prioridades para múltiplos templates
  • Dinâmico: Suporta placeholders para personalização

Arquitetura

O sistema é composto por três entidades principais:

NotificationTemplateType (Tipo)
├── Reason: NotificationTemplateReason
├── Variables: List<NotificationTemplateVariable>
│   ├── Variable 1 (isRequired: true)
│   ├── Variable 2 (isRequired: false, defaultValue: "...")
│   └── ...
└── Templates: List<NotificationTemplate>
    ├── Template Email
    ├── Template WhatsApp
    └── Template SMS

NotificationTemplateType (Tipo de Template)

Define o tipo/motivo da notificação e quais variáveis estão disponíveis ou são obrigatórias.

Campo Tipo Descrição
Id Guid Identificador único
Name string Nome do tipo (ex: "Serviço Disponível")
Reason NotificationTemplateReason Enum que define o motivo
Description string? Descrição do tipo
Variables List Lista de variáveis disponíveis
Templates List Templates associados a este tipo

NotificationTemplate (Template)

Define o conteúdo da notificação para um canal específico.

Campo Tipo Descrição
Id Guid Identificador único
Name string Nome do template
Subject string Assunto da notificação
Body string Corpo da mensagem (com placeholders)
Channel NotificationTemplateChannel Canal (Email, WhatsApp, SMS)
NotificationTemplateTypeId Guid FK para o tipo
JobTaskId Guid? FK para tarefa específica (null = genérico)
IsActive bool Status ativo/inativo
Priority int Prioridade (maior = preferência)
IsGeneric bool Calculado: !JobTaskId.HasValue

NotificationTemplateVariable (Variável)

Define uma variável que pode ser usada nos templates de um tipo.

Campo Tipo Descrição
Id Guid Identificador único
Name string Nome da variável (ex: "UrlAction")
Description string? Descrição da variável
IsRequired bool Se é obrigatória
DefaultValue string? Valor padrão (se não fornecido)
NotificationTemplateTypeId Guid FK para o tipo

Fluxo de Envio de Notificações

1. Busca templates pelo Reason do tipo
   └── GetActiveTemplatesByReasonAsync(reason, jobTaskId)

2. Para cada template encontrado:
   ├── 2.1 Aplica valores default das variáveis
   │        ApplyDefaultVariables(template, providedVariables)
   │
   ├── 2.2 Valida variáveis obrigatórias
   │        template.ValidateVariablesForSend(variables)
   │        └── Se faltar variável → RuleViolationDomainException
   │
   ├── 2.3 Processa o template (substitui placeholders)
   │        ProcessTemplateAsync(template, contextObject, variables)
   │
   └── 2.4 Envia a notificação pelo canal apropriado
            SendNotificationByChannel(channel, email, phone, ...)

Validação de Variáveis

O sistema valida automaticamente se todas as variáveis obrigatórias foram fornecidas antes de enviar a notificação.

Como funciona:

  1. No momento da criação do template: Verifica se o Body ou Subject contém os placeholders das variáveis obrigatórias do tipo.

  2. No momento do envio: Verifica se o dicionário de variáveis contém todas as variáveis obrigatórias que estão presentes no template.

Exceções:

  • Criação/Atualização: Se uma variável obrigatória não estiver no template: RuleViolationDomainException: Template body/subject is missing required variables: UrlAction

  • Envio: Se uma variável obrigatória não for fornecida: RuleViolationDomainException: Missing required variables for sending notification: UrlAction

Valores Default:

Variáveis com DefaultValue definido não geram erro mesmo se não forem passadas no dicionário, pois o sistema aplica o valor default automaticamente.


Commands (Comandos)

1. Criação de Template (CreateNotificationTemplate)

Contrato: CreateNotificationTemplateCommand

Campo Tipo Obrigatório Descrição
Name string Sim Nome do template
Subject string Sim Assunto (máx 500 chars)
Body string Sim Corpo da mensagem
Channel NotificationTemplateChannel Sim Canal de envio
NotificationTemplateTypeId Guid Sim ID do tipo de template
JobTaskId Guid? Não ID da tarefa (null = genérico)
IsActive bool Sim Status (padrão: true)
Priority int Sim Prioridade (padrão: 0)
Observation string? Não Observações
Author string? Não Autor da operação

Validações: - Body deve conter todas as variáveis obrigatórias do tipo - Channel deve ser válido - NotificationTemplateTypeId deve existir

Exemplo:

{
  "name": "Serviço Disponível - WhatsApp",
  "subject": "Serviço Disponível",
  "body": "Olá {{ProfessionalUser.Name}}! Novo serviço disponível. Acesse: {{UrlAction}}",
  "channel": "WhatsApp",
  "notificationTemplateTypeId": "123e4567-e89b-12d3-a456-426614174000",
  "jobTaskId": null,
  "isActive": true,
  "priority": 0,
  "author": "admin@santamao.com"
}

2. Atualização de Template (UpdateNotificationTemplate)

Contrato: UpdateNotificationTemplateCommand

Mesmos campos da criação, com adição de:

Campo Tipo Obrigatório Descrição
Id Guid Sim ID do template

Validações: - Template deve existir - Body deve conter todas as variáveis obrigatórias do novo tipo


3. Exclusão de Template (DeleteNotificationTemplate)

Contrato: DeleteNotificationTemplateCommand

Campo Tipo Obrigatório Descrição
Id Guid Sim ID do template

Recomendação: Use UpdateNotificationTemplate com IsActive = false para manter histórico.


Queries (Consultas)

1. Buscar Template por ID (GetNotificationTemplateById)

Contrato: GetNotificationTemplateByIdQuery

Retorno: Inclui dados do tipo e suas variáveis.

{
  "id": "...",
  "name": "Serviço Disponível - WhatsApp",
  "subject": "Serviço Disponível",
  "body": "Olá {{ProfessionalUser.Name}}! Novo serviço disponível. Acesse: {{UrlAction}}",
  "channel": "WhatsApp",
  "notificationTemplateType": {
    "id": "...",
    "name": "Serviço Disponível",
    "reason": "ServiceAvailable",
    "variables": [
      { "name": "UrlAction", "description": "URL para aceitar", "isRequired": true },
      { "name": "ProfessionalUser.Name", "description": "Nome do profissional", "isRequired": false }
    ]
  },
  "jobTaskId": null,
  "isActive": true,
  "priority": 0,
  "isGeneric": true
}

2. Pesquisar Templates (SearchNotificationTemplates)

Contrato: SearchNotificationTemplatesQuery

Campo Tipo Descrição
Channel NotificationTemplateChannel? Filtrar por canal
Reason NotificationTemplateReason? Filtrar por motivo (via tipo)
JobTaskId Guid? Filtrar por tarefa
IsActive bool? Filtrar por status
IsGeneric bool? Filtrar genéricos/específicos

NotificationTemplateType

Estrutura

Cada tipo define:

  1. Reason: O motivo/contexto da notificação
  2. Variables: Lista de variáveis disponíveis para os templates deste tipo

Variáveis do Tipo

Variáveis são definidas no tipo e ficam disponíveis para todos os templates associados.

Exemplo de Tipo: ServiceAvailable

var type = new NotificationTemplateType(
    name: "Serviço Disponível",
    reason: NotificationTemplateReason.ServiceAvailable,
    description: "Notificação enviada ao profissional quando um novo serviço está disponível"
);

// Variável obrigatória
type.AddVariable("UrlAction", "URL para aceitar o serviço", isRequired: true);

// Variáveis opcionais
type.AddVariable("ProfessionalUser.Name", "Nome do profissional", isRequired: false);
type.AddVariable("JobTask.Name", "Nome da tarefa", isRequired: false);
type.AddVariable("Order.StartDate", "Data de início do serviço", isRequired: false);

Tipos Padrão do Sistema

Tipo Reason Variáveis Obrigatórias
Serviço Disponível ServiceAvailable UrlAction
Serviço Confirmado ServiceConfirmed UrlAction
Pagamento Pendente PaymentPending PaymentUrl, Amount

Tipos e Enums

NotificationTemplateChannel

public enum NotificationTemplateChannel
{
    Unknown,   // Desconhecido
    Email,     // Email
    WhatsApp,  // WhatsApp
    Sms        // SMS
}

NotificationTemplateReason

public enum NotificationTemplateReason
{
    // Serviços
    ServiceAvailable,
    ServiceUnavailable,
    ServiceConfirmed,
    ServiceCancelled,
    ServiceCompleted,
    ServiceInProgress,

    // Agendamentos
    AppointmentScheduled,
    AppointmentReminder,
    AppointmentConfirmed,
    AppointmentCancelled,
    AppointmentRescheduled,

    // Pagamentos
    PaymentApproved,
    PaymentPending,
    PaymentRejected,
    PaymentRefunded,

    // Usuários
    WelcomeProfessional,
    WelcomeClient,
    ProfileApproved,
    ProfileRejected,
    PasswordReset,
    EmailConfirmation,

    // Sistema
    SystemAlert,
    MaintenanceNotice,
    NewFeature
}

Sistema de Prioridades

Hierarquia de Seleção

  1. Template Específico de Alta Prioridade (JobTaskId != null, Priority alto)
  2. Template Específico de Baixa Prioridade (JobTaskId != null, Priority baixo)
  3. Template Genérico de Alta Prioridade (JobTaskId = null, Priority alto)
  4. Template Genérico de Baixa Prioridade (JobTaskId = null, Priority baixo)

Valores Recomendados

Faixa Uso
0-10 Templates padrão
11-50 Templates sazonais
51-100 Templates especiais/urgentes

Placeholders

Sintaxe

Use duplas chaves: {{NomeDaVariavel}}

Formatos suportados:

{{VariavelSimples}}           → Variável fornecida no dicionário
{{Objeto.Propriedade}}        → Navegação em objetos do contexto
{VariavelSimples}             → Formato alternativo (chave simples)
{Objeto.Propriedade}          → Formato alternativo com navegação

Placeholders por Contexto

Usuários: - {{User.Name}} - Nome do usuário - {{User.Email}} - Email do usuário - {{User.Phone}} - Telefone do usuário

Profissionais: - {{ProfessionalUser.Name}} - Nome do profissional - {{ProfessionalUser.Email}} - Email do profissional - {{ProfessionalUser.Phone}} - Telefone do profissional

Pedidos: - {{Order.Id}} - ID do pedido - {{Order.StartDate}} - Data de início

Variáveis Extras (passadas no dicionário): - {{UrlAction}} - URL de ação - {{PaymentUrl}} - URL de pagamento - {{Amount}} - Valor formatado - {{ExpiresAt}} - Data de expiração


NotificationService

Métodos Genéricos

// Enviar notificação para qualquer destinatário
Task SendNotificationAsync<T>(
    NotificationTemplateReason reason,
    T contextObject,
    Email? email,
    Phone? phone,
    Dictionary<string, string> variables,
    Guid? jobTaskId = null,
    Guid? correlationId = null,
    CancellationToken cancellationToken = default);

// Enviar notificação para User
Task SendNotificationToUserAsync<T>(
    NotificationTemplateReason reason,
    User user,
    T contextObject,
    Dictionary<string, string> variables,
    Guid? jobTaskId = null,
    Guid? correlationId = null,
    CancellationToken cancellationToken = default);

// Enviar notificação para ProfessionalUser
Task SendNotificationToProfessionalAsync<T>(
    NotificationTemplateReason reason,
    ProfessionalUser professionalUser,
    T contextObject,
    Dictionary<string, string> variables,
    Guid? jobTaskId = null,
    Guid? correlationId = null,
    CancellationToken cancellationToken = default);

Métodos Específicos

// Notifica profissional sobre serviço disponível
Task SendServiceAvailableNotificationAsync(
    ProfessionalUser professionalUser, 
    Guid correlationId, 
    CancellationToken cancellationToken);
// Variáveis: UrlAction (gerada automaticamente)

// Notifica usuário que profissional aceitou
Task SendResponseAcceptedNotificationAsync(
    Order order, 
    Guid correlationId, 
    CancellationToken cancellationToken);
// Variáveis: UrlAction (gerada automaticamente)

// Notifica usuário sobre pagamento pendente
Task SendPaymentCreatedNotificationAsync(
    Order order, 
    Guid correlationId, 
    CancellationToken cancellationToken);
// Variáveis: PaymentUrl, Amount, ExpiresAt (extraídas do OrderPayment)

Exemplos de Uso

Criando um Tipo com Variáveis

var type = new NotificationTemplateType(
    name: "Pagamento Pendente",
    reason: NotificationTemplateReason.PaymentPending,
    description: "Notificação de pagamento pendente"
);

type.AddVariable("PaymentUrl", "URL para pagamento", isRequired: true);
type.AddVariable("Amount", "Valor do pagamento", isRequired: true);
type.AddVariable("ExpiresAt", "Data de expiração", isRequired: false);
type.AddVariable("User.Name", "Nome do usuário", isRequired: false);

Criando Templates para o Tipo

// Email
new NotificationTemplate(
    name: "Pagamento Pendente - Email",
    subject: "Pagamento pendente - Pedido #{{Order.Id}}",
    body: @"Olá {{User.Name}},
        Seu pagamento de {{Amount}} está pendente.
        Válido até: {{ExpiresAt}}
        Acesse: {{PaymentUrl}}",
    channel: NotificationTemplateChannel.Email,
    templateType: type
);

// WhatsApp
new NotificationTemplate(
    name: "Pagamento Pendente - WhatsApp",
    subject: "Pagamento Pendente",
    body: "Pagamento pendente de {{Amount}}. Acesse: {{PaymentUrl}}",
    channel: NotificationTemplateChannel.WhatsApp,
    templateType: type
);

Enviando Notificação Genérica

await _notificationService.SendNotificationToUserAsync(
    reason: NotificationTemplateReason.PaymentPending,
    user: order.User,
    contextObject: order,
    variables: new Dictionary<string, string>
    {
        { "PaymentUrl", "https://pay.example.com/123" },
        { "Amount", "R$ 150,00" },
        { "ExpiresAt", "15/01/2026 23:59" }
    },
    correlationId: Guid.CreateVersion7(),
    cancellationToken: cancellationToken);

Observações Importantes

1. Validação de Variáveis

  • Obrigatórias: Devem estar no template E serem passadas no envio
  • Default: Se não passadas, usa o valor default
  • Opcionais: Não geram erro se ausentes

2. Templates Genéricos vs Específicos

  • Genéricos (JobTaskId = null): Fallback para todas as tarefas
  • Específicos: Sobrescrevem genéricos para uma tarefa específica

3. Multi-Canal

Canal Características
Email HTML, links, imagens
WhatsApp Emojis, Markdown limitado
SMS 160 chars, texto puro

4. Melhores Práticas

  • Defina variáveis obrigatórias apenas para dados essenciais
  • Use valores default para variáveis comuns
  • Teste templates antes de ativar
  • Mantenha templates inativos para histórico

5. Performance

  • Templates carregados por demanda
  • Cache de templates ativos recomendado
  • Índices em Channel, Reason, JobTaskId

6. Conformidade

  • Email: Inclua link de unsubscribe (LGPD/GDPR)
  • WhatsApp: Respeite janela de 24h
  • SMS: Evite horários inadequados