| name | impl-service |
| description | Implementar serviços de lógica de negócio em services/. Use quando criar service, lógica de negócio, orquestração de repositórios, ou integração com LLM. |
| allowed-tools | Read, Write, Edit, Glob, Grep |
Implementar Service (Camada de Lógica de Negócio)
Regras Arquiteturais (NON-NEGOTIABLE)
- TODA lógica de negócio deve estar em services, NUNCA em routers ou repositories
- Validações de negócio (limites, regras, constraints) ficam no service
- Prompts de LLM ficam no service, NUNCA em routers
- Chamadas LLM DEVEM usar tracing Phoenix:
_tracer.start_as_current_span() - Dependency Injection: repositories são injetados no
__init__ - Orquestração: services chamam repositories, NUNCA acessam DB diretamente
- Bloco de validação: todo service DEVE ter
if __name__ == "__main__":
Estrutura de Arquivos
src/synth_lab/services/
├── {entity}_service.py # Um arquivo por domínio
├── errors.py # Exceções de negócio
└── {feature}/ # Subpastas para features complexas
├── __init__.py
└── {sub_service}.py
Convenções de nome:
- Arquivo:
{entity}_service.py(singular, snake_case) - Classe:
{Entity}Service(PascalCase) - Métodos:
{verbo}_{substantivo}(ex:create_experiment,list_synths)
Padrões de Código
Service Básico (sem LLM)
"""
{Entity} service for synth-lab.
Business logic layer for {entity} operations.
"""
from synth_lab.models.pagination import PaginatedResponse, PaginationParams
from synth_lab.repositories.{entity}_repository import {Entity}Repository
class {Entity}Service:
"""Service for {entity} business logic."""
# Constantes de validação
NAME_MAX_LENGTH = 100
def __init__(self, repository: {Entity}Repository | None = None):
"""Initialize with dependency injection."""
self.repository = repository or {Entity}Repository()
def list_{entities}(
self,
params: PaginationParams | None = None,
) -> PaginatedResponse[{Entity}Summary]:
"""List {entities} with pagination."""
params = params or PaginationParams()
return self.repository.list_{entities}(params)
def get_{entity}(self, {entity}_id: str) -> {Entity}Detail:
"""Get {entity} by ID."""
return self.repository.get_by_id({entity}_id)
def create_{entity}(self, name: str) -> {Entity}:
"""Create new {entity} with validation."""
# Validação de negócio
if not name or not name.strip():
raise ValueError("name is required")
if len(name) > self.NAME_MAX_LENGTH:
raise ValueError(f"name exceeds {self.NAME_MAX_LENGTH} chars")
# Criar e persistir
entity = {Entity}(name=name.strip())
return self.repository.create(entity)
Service com LLM
"""
{Feature} LLM service for synth-lab.
LLM-powered business logic.
"""
from loguru import logger
from synth_lab.infrastructure.llm_client import LLMClient, get_llm_client
from synth_lab.infrastructure.phoenix_tracing import get_tracer
_tracer = get_tracer("{feature}_service")
class {Feature}Service:
"""Service with LLM integration."""
def __init__(self, llm_client: LLMClient | None = None):
self.llm = llm_client or get_llm_client()
self.logger = logger.bind(component="{feature}_service")
def generate(self, data: InputData) -> Result:
"""Generate result using LLM."""
with _tracer.start_as_current_span("generate"):
prompt = self._build_prompt(data)
response = self.llm.complete_json(
messages=[{"role": "user", "content": prompt}],
operation_name="generate_result",
)
self.logger.info(f"Generated for {data.id}")
return self._parse_response(response)
def _build_prompt(self, data: InputData) -> str:
"""Build LLM prompt (private method)."""
return f"""
Instrução: ...
Dados: {data.model_dump_json()}
"""
def _parse_response(self, response: dict) -> Result:
"""Parse and validate LLM response."""
return Result(**response)
Bloco de Validação (OBRIGATÓRIO)
if __name__ == "__main__":
import sys
from synth_lab.infrastructure.config import DB_PATH
from synth_lab.infrastructure.database import DatabaseManager
all_validation_failures = []
total_tests = 0
if not DB_PATH.exists():
print(f"Database not found at {DB_PATH}")
sys.exit(1)
db = DatabaseManager(DB_PATH)
service = {Entity}Service()
# Test 1: Operação básica
total_tests += 1
try:
result = service.list_{entities}()
if result.pagination.total < 0:
all_validation_failures.append("Invalid total")
except Exception as e:
all_validation_failures.append(f"List failed: {e}")
# Test 2: Validação de erro
total_tests += 1
try:
service.get_{entity}("invalid_id")
all_validation_failures.append("Should raise NotFoundError")
except {Entity}NotFoundError:
pass # Expected
except Exception as e:
all_validation_failures.append(f"Wrong exception: {e}")
db.close()
if all_validation_failures:
print(f"VALIDATION FAILED - {len(all_validation_failures)}/{total_tests}:")
for f in all_validation_failures:
print(f" - {f}")
sys.exit(1)
else:
print(f"VALIDATION PASSED - All {total_tests} tests OK")
sys.exit(0)
Checklist de Verificação
Antes de finalizar, verificar:
- Lógica de negócio está no service (não em router/repository)
- Validações de input (required, max length, format)
- Repository injetado via DI no
__init__ - Chamadas LLM usam
_tracer.start_as_current_span() - Prompts em métodos privados
_build_prompt() - Bloco
if __name__ == "__main__":com validação real - Exceções específicas do domínio (ex:
{Entity}NotFoundError) - Docstrings em todos os métodos públicos
- Type hints em parâmetros e retornos