---
name: nova-service
description: >
  Cria uma função de serviço em apps/{app}/services/{dominio}_service.py para orquestrar
  lógica que envolve múltiplos models, efeitos colaterais externos (email, arquivo, notificação)
  ou fluxos complexos que não pertencem a um único model. Usa dataclass Result para retorno
  estruturado. Segue .claude/rules/naming-conventions.md e general-architecture.md.
  Use quando o usuário disser "criar serviço", "nova service", "lógica de negócio complexa",
  "aprovar registro", "enviar email de aprovação", "lógica multi-model", "orquestrar X",
  "criar função de serviço", "extrair lógica da view", "lógica com efeito colateral externo".
  DO NOT USE para lógica simples em um model — colocar diretamente no model (Fat Model).
  DO NOT USE para criar views — use /novas-views.
argument-hint: "<app> <nome_do_servico>"
---

# /nova-service $ARGUMENTS

Cria uma função de serviço no padrão SurveyHub.

## Quando usar Services vs. Model

**Use o Model** quando:
- Lógica envolve apenas UM model
- Transição de estado (ativar, pausar, encerrar)
- Cálculo ou propriedade computada do próprio model

**Use Services** quando:
- Lógica envolve MÚLTIPLOS models de apps diferentes
- Efeitos colaterais externos (email, arquivo, notificação)
- Orquestração complexa de operações
- Ex: aprovar registro → ativar usuário + enviar email

## Informações Necessárias

| Pergunta | Exemplo |
|----------|---------|
| App (`$1`) | `users`, `surveys` |
| Nome da função (`$2`) | `aprovar_registro`, `exportar_resultados` |
| O que faz? | "Aprova RegistrationRequest e ativa o User" |
| Models envolvidos? | `RegistrationRequest`, `User` |
| Efeitos colaterais? | Email? Arquivo? Notificação? |
| O que retornar? | success: bool + message: str |

**Derivar:**
- `{dominio}` = prefixo do arquivo (ex: `approval`, `export`)
- `{funcao}` = `$2` snake_case em português
- `{Result}` = dataclass de resultado (ex: `ApprovalResult`)

---

## Fase 1 — Criar o Arquivo

```bash
mkdir -p apps/{app}/services && touch apps/{app}/services/__init__.py
```

Criar `apps/{app}/services/{dominio}_service.py`:

```python
"""Serviços de {domínio} — {descrição breve}."""

import logging
from dataclasses import dataclass

from django.db import transaction

logger = logging.getLogger(__name__)


@dataclass
class {Result}:
    """Resultado estruturado de operações de {domínio}."""

    success: bool
    message: str
    # {model}_pk: str | None = None  # adicionar campos extras se necessário


@transaction.atomic
def {funcao}({param}) -> {Result}:
    """{Descrição imperativa da função em uma linha}.

    Args:
        {param}: [descrição do parâmetro].

    Returns:
        {Result} com success=True se bem-sucedido, ou success=False
        com message descrevendo o motivo da falha.
    """
    logger.info("Iniciando {funcao} para %s", {param})

    # 1. Validar pré-condições
    # if not {param}.pode_ser_X():
    #     return {Result}(success=False, message="Motivo da falha.")

    # 2. Lógica principal (model/banco)
    # {param}.ativar()

    # 3. Efeitos colaterais externos
    # _enviar_email_confirmacao({param})

    logger.info("{funcao} concluída para %s", {param})
    return {Result}(success=True, message="{Ação} realizada com sucesso.")
```

---

## Fase 2 — Usar na View

A view apenas orquestra — sem lógica de negócio:

```python
from apps.{app}.services.{dominio}_service import {funcao}

@login_required
@require_POST
def {acao}_view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
    {model} = get_object_or_404({Model}, pk=pk)
    resultado = {funcao}({model})
    if resultado.success:
        messages.success(request, resultado.message)
    else:
        messages.error(request, resultado.message)
    return redirect("{app}:{model}_detail", pk=pk)
```

---

## Fase 3 — Testes

Criar `apps/{app}/tests/test_{dominio}_service.py`:

```python
import pytest
from apps.{app}.services.{dominio}_service import {funcao}
from apps.{app}.tests.factories import {Model}Factory


@pytest.mark.django_db
class Test{Funcao}:
    def test_sucesso(self):
        {model} = {Model}Factory()  # estado válido para a operação
        resultado = {funcao}({model})
        assert resultado.success is True

    def test_falha_estado_invalido(self):
        {model} = {Model}Factory(active=True)  # estado inválido
        resultado = {funcao}({model})
        assert resultado.success is False
        assert resultado.message

    def test_atomico_reverte_em_falha(self):
        """@transaction.atomic deve reverter se algo falhar no meio."""
        ...
```

---

## Checklist

- [ ] Arquivo em `apps/{app}/services/{dominio}_service.py`
- [ ] `__init__.py` em `apps/{app}/services/`
- [ ] Dataclass `{Result}` com `success: bool` + `message: str`
- [ ] `@transaction.atomic` se houver múltiplas escritas no banco
- [ ] Logging no início e fim
- [ ] Validação de pré-condições retorna `{Result}(success=False, ...)` (não exceção)
- [ ] Service usado na view — sem lógica de negócio na view
- [ ] Testes: happy path + cenário de falha

## Referências

- `.claude/rules/general-architecture.md` — quando usar services/
- `.claude/rules/naming-conventions.md` — convenções para services
- `.claude/rules/architecture/django-views.md` — como views usam services
