Pular para conteúdo

title: Camada: Aplicação description: Orquestração de casos de uso e coordenação entre o domínio, a infraestrutura e a reatividade.


Camada: Aplicação

A Camada de Aplicação (framework\application) é o maestro do framework. Ela não contém regras de negócio propriamente ditas (que pertencem ao Domínio), mas coordena como essas regras são executadas para realizar uma tarefa completa do usuário.


O que é

É a camada que implementa os Casos de Uso do framework. Ela recebe uma intenção (via Controller, CLI ou Hook), resolve as dependências necessárias através do Container de DI, e orquestra a interação entre Services, Repositories e o Dispatcher.

Principais componentes: * Application Services: Coordenam fluxos de execução (ex: item_service). * Command Handlers: Executam uma unidade de trabalho específica, geralmente de forma assíncrona. * Listeners e Subscribers: Reagem a fatos ocorridos no sistema (Signals) para disparar efeitos colaterais. * Decorators de Auditoria: Anexam logs de Audit e Revisions de forma transversal aos fluxos operacionais.

Por que existe

Sem a Camada de Aplicação, a lógica operacional ficaria espalhada em controladores ou repetida em múltiplos pontos de entrada. Ela existe para: 1. Uniformidade: Garantir que "criar um Item" siga sempre os mesmos passos, seja via API Web ou via CLI. 2. Separação de Preocupações: Isolar a orquestração (fluxo) da persistência (SQL) e da regra pura (Domínio). 3. Gestão de Transações: Coordenar operações multi-repository que precisam ser atômicas (tudo ou nada).

Esta camada é fundamental para manter o framework extensível, permitindo que novas reações sejam adicionadas a um caso de uso sem alterar o código original.

Como se relaciona com outros conceitos

  • Aplicação vs Domínio: A Aplicação pergunta "quem pode fazer isso?" (DI) e "onde eu guardo?" (Repository). O Domínio responde "eu posso fazer isso?" (Invariante).
  • Aplicação vs Infraestrutura: A Aplicação utiliza interfaces (Contracts) definidos na Infraestrutura para persistir dados, sem se acoplar ao SQL real.
  • Aplicação vs Reatividade: É aqui que o Dispatcher é mais utilizado. Ao final de cada caso de uso bem-sucedido, a Camada de Aplicação publica um Signal para notificar o restante do framework.

Decisões de design

  • Audit via Decorator: O log de auditoria e as revisões não devem "sujar" o código do service de aplicação. Eles são anexados via Decorator na persistência, garantindo que o service foque apenas no fluxo principal.
  • Consistency vs Best Effort: A Aplicação decide o que é obrigatório (falha junto com a transação) e o que é lateral (falha isolada via Listener).
  • Coordenação de Transações: Quando um caso de uso envolve múltiplos repositories (ex: Item e Job), a Camada de Aplicação é quem coordena o transaction_manager para garantir a integridade dos dados.

O que não é

  • Não contém SQL: A Aplicação nunca acessa o banco diretamente. Ela usa os Repositories.
  • Não contém Regra de Domínio: Se a regra é "o status só pode ser 'active' se o 'parent' existir", isso deve estar na Entity de Domínio, não no Service de Aplicação.
  • Não contém Lógica de Apresentação: Ela não sabe o que é JSON, HTML ou formulários do Moodle. Recebe DTOs ou objetos hidratados e devolve o resultado da operação.

Perspectiva para builders de extensions

Ao desenvolver uma extension, sua lógica mais importante viverá aqui: 1. Service da Extension: Crie um service para cada grande fluxo da sua extension (ex: billing_service). 2. Use DI: Receba os Repositories e Facades do core via construtor. 3. Despache um Signal: Sempre que sua extension completar uma ação relevante, use middag::dispatch() para permitir que outros módulos (ou o core) reajam ao seu fato.

Exemplo ilustrativo

Um service de aplicação orquestrando a atualização de um Item:

namespace local_middag\framework\application\service\item;

final class item_application_service {
    public function update_item(int $id, array $data): void {
        $item = $this->repository->find_by_id($id);

        // Aplica regra de domínio (retorna nova instância)
        $updated_item = $item->with_data($data);

        // Orquestra persistência e reatividade dentro de uma transação
        $this->transaction_manager->execute(function() use ($updated_item) {
            $this->repository->save($updated_item);

            // Notifica o sistema (Auditoria e Jobs reagirão aqui)
            middag::dispatch(new item_updated_signal($updated_item));
        });
    }
}

Neste exemplo, o service não sabe como o SQL salva, nem quem está ouvindo o signal. Ele apenas coordena o sucesso da operação.

Referências