Gerenciamento de Serviços do Activiti: Resolvendo Falhas de Injeção no Spring Boot

A classe WorkflowService atua frequentemente como uma fachada para unificar os componentes do motor de fluxo Activiti, centralizando o acesso a interfaces como RepositoryService e RuntimeService. No entanto, implementações que não seguem as convenções do ecossistema Spring costumam resultar em erros críticos de NullPointerException durante a execução, causados por falhas na injeção de dependências.

Diagnóstico de Falhas Arquiteturais

Para construir uma solução robusta, é necessário identificar os antipadrões comuns presentes em implementações iniciais:

  • Ausência de estereótipos do Spring: A omissão de anotações como @Service ou @Component impede que o contêiner IoC gerencie o ciclo de vida da classe. Sem isso, o contêiner ignora a classe e não processa as injeções, deixando os serviços nulos.
  • Violação de Encapsulamento: Atributos expostos com o modificador public permitem mutação externa indeivda e dificultam a manutenção. O acesso às instâncias dos serviços deve ser estritamente controlado.
  • Acoplamento Desnecessário: Injetar o ProcessEngine inteiro para extrair manualmante os serviços é uma prática redundante. O Spring Boot Starter do Activiti já expõe os serviços específicos como beans gerenciados e prontos para uso.

Refatoração da Fachada de Serviços

A abordagem mais segura e moderna no Spring Boot utiliza a injeção via construtor. Isso garante a imutabilidade das dependências, facilita a escrita de testes unitários e elimina a necessidade da anotação @Autowired nos campos. Abaixo está a versão otimizada da classe:

package com.empresa.bpm.gerenciador;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.springframework.stereotype.Service;

/**
 * Fachada centralizada para interações com o motor de BPM.
 * Utiliza injeção por construtor para garantir imutabilidade.
 */
@Service
public class WorkflowService {

    private final RepositoryService repoService;
    private final RuntimeService executionService;
    private final TaskService userTaskService;
    private final HistoryService auditService;

    public WorkflowService(RepositoryService repoService,
                           RuntimeService executionService,
                           TaskService userTaskService,
                           HistoryService auditService) {
        this.repoService = repoService;
        this.executionService = executionService;
        this.userTaskService = userTaskService;
        this.auditService = auditService;
    }

    public RepositoryService obterRepositorio() {
        return repoService;
    }

    public RuntimeService obterMotorExecucao() {
        return executionService;
    }

    public TaskService obterGerenciadorTarefas() {
        return userTaskService;
    }

    public HistoryService obterHistorico() {
        return auditService;
    }
}

Configuração do Ambiente de Execução

Para que o contêiner Spring consiga instanciar e fornecer os serviços do Activiti via construtor, a integração deve estar devidamente configurada no projeto.

Adicione a dependência do starter no seu arquivo de build (ex: pom.xml):

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.1.0.M6</version>
</dependency>

Configure as propriedades do motor no arquivo application.yml:

spring:
  activiti:
    # Atualiza o esquema do banco de dados automaticamente
    database-schema-update: true
    # Nível de auditoria detalhado
    history-level: full
    # Habilita a varredura de arquivos de definição
    check-process-definitions: true
    # Diretório personalizado para os diagramas
    process-definition-location-prefix: classpath:/bpmn/
    db-history-used: true

Consumo na Camada de Negócios

Com a fachada devidamente registrada e imutável, os controladores ou serviços de domínio podem consumi-la de forma segura. O exemplo abaixo demonstra a inicialização de instâncias e a conclusão de tarefas:

package com.empresa.bpm.web;

import com.empresa.bpm.gerenciador.WorkflowService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/processos")
public class ProcessoController {

    private final WorkflowService gerenciadorBpm;

    // Injeção da fachada refatorada
    public ProcessoController(WorkflowService gerenciadorBpm) {
        this.gerenciadorBpm = gerenciadorBpm;
    }

    @PostMapping("/iniciar")
    public ResponseEntity<String> iniciarFluxo(@RequestParam String chaveProcesso) {
        String idInstancia = gerenciadorBpm.obterMotorExecucao()
                .startProcessInstanceByKey(chaveProcesso)
                .getId();
                
        return ResponseEntity.ok("Instância de processo criada: " + idInstancia);
    }

    @PostMapping("/concluir-tarefa")
    public ResponseEntity<String> finalizarTarefa(@RequestParam String idTarefa) {
        gerenciadorBpm.obterGerenciadorTarefas().complete(idTarefa);
        return ResponseEntity.ok("Tarefa processada com sucesso.");
    }
}

Caso a aplicação continue apresentando falhas de injeção com instâncias nulas ao utilizar este padrão, verifique se o escaneamento de componentes (@ComponentScan) abrange o pacote onde a classe fachada está localizada. Além disso, confirme se as dependências do motor de fluxo estão devidamente resolvidas no gerenciador de builds e se as propriedades de conexão com o banco de dados relacional estão configuradas corretamente no arquivo de propriedades do ambiente.

Tags: activiti spring-boot java dependency-injection BPMN

Publicado em 7-20 08:08