Pular para conteúdo

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);
    }
}

Referências