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_managerpara 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.