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
@Serviceou@Componentimpede 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
publicpermitem 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
ProcessEngineinteiro 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.