title: Conceito: Audit description: O Audit como log imutável de rastreabilidade para ações e fatos do framework.
Audit¶
O Audit é a família de persistência responsável pela rastreabilidade de todas as ações e fatos relevantes que ocorrem no local_middag. Ao contrário da Revision (que foca nos dados), o Audit foca no evento em si e no seu contexto.
O que é¶
É um registro persistido e imutável que responde à pergunta fundamental: "Qual fato ocorreu, quem foi o autor, em qual canal de execução e o que foi alterado?".
Um registro de Audit (Audit Log) é composto por:
* Ação (Ato): O fato ocorrido (ex: item_status_changed).
* Ator: O usuário ou sistema que executou a ação.
* Contexto: Informações sobre a origem (web, cli, api, system).
* Diff: A lista granular de quais campos mudaram (valor anterior vs novo).
* Snapshot (Opcional): Um registro extra do estado contextual para fins de conformidade ou perícia técnica.
Por que existe¶
Em sistemas corporativos e ambientes educacionais (como o Moodle), a rastreabilidade é um requisito de conformidade (LGPD/GDPR) e um mecanismo essencial de depuração. Sem um sistema de Audit centralizado: 1. Cada service precisaria implementar sua própria lógica de log. 2. Logs seriam dispersos e em formatos inconsistentes (arrays, strings, arquivos). 3. Seria impossível reconstruir a "quem fez o quê" de forma estruturada.
O design do Audit no framework separa o Signal (a ocorrência em memória) do Audit Log (o registro persistente) para garantir que apenas fatos relevantes ocupem espaço no banco de dados.
Como se relaciona com outros conceitos¶
- Audit vs Signal: O Signal é o gatilho; o Audit é a consequência persistida. Muitos Signals ocorrem no sistema, mas nem todos geram um Audit Log.
- Audit vs Revision: O Audit foca na mudança (diff) e no autor. A Revision foca no estado (snapshot completo do objeto). Eles são complementares, mas independentes.
- Audit vs Item: O Audit aponta para um Item quando a ação afetou um registro específico de negócio.
Decisões de design¶
- Transversal e Desacoplado: O código que executa a lógica de negócio (Service) não deve "saber" que está sendo auditado. O Audit é anexado de fora através de Decorators (para garantir consistência forte) ou Listeners (para reações laterais).
- Identidade Estável (UUID v7): Cada registro de Audit possui um
uuidúnico universal. Usamos o padrão UUID v7 porque ele é ordenável por tempo, otimizando as inserções no banco de dados Moodle. - Persistência Granular: Diferente de um log de texto simples, as mudanças são salvas em tabelas satélites (
middag_audit_diff), permitindo buscas complexas como "quem alterou o campo 'status' nos últimos 30 dias?".
O que não é¶
- Não é um Log de Aplicação (PHP Error Log): O Audit é para fatos de negócio e ações de usuário, não para rastrear erros de código ou stack traces.
- Não é Event Sourcing: O framework não usa o Audit para reconstruir o estado atual do objeto (quem faz isso é a Revision ou o próprio Item). O Audit é para fins informativos e de perícia.
- Não é um Mecanismo de Pub/Sub: O registro de Audit é o fim do fluxo. Para reagir a um evento, use o Signal e o Dispatcher.
Perspectiva para builders de extensions¶
Como desenvolvedor de extension, para que suas ações sejam auditadas:
1. Publique um Signal: Ao final do seu processamento, use middag::dispatch($seu_signal).
2. Preencha o contrato de payload do seu Signal de forma rica (IDs, autores, itens afetados).
3. Utilize as políticas de auditoria padrão do framework ou registre uma Audit Policy específica para o seu TYPE.
Exemplo ilustrativo¶
Um registro típico de Audit reconstruído para exibição:
Fato: "coursegroup_equivalence_added"
Data: "2024-03-27 10:45:00"
Autor: "admin (ID: 2)"
Origem: "web"
ID do Item: 1001 (Tipo: coursegroup)
Mudanças:
- Campo: status
De: "draft"
Para: "published"
- Campo: group_id
De: null
Para: "G-42"
No código (anexação via Decorator):
// O framework aplica este decorator ao seu repository via Container
final class audit_item_repository_decorator implements item_repository_interface {
public function save(item_entity $item): void {
$old_item = $this->repository->find_by_id($item->get_id());
$this->repository->save($item); // Executa persistência original
// O Audit é gerado comparando $old_item e $item
$this->audit_service->record_diff($old_item, $item);
}
}