1
resposta

Efetuar rollback nas ações anteriores usando padrão commandHandler

Digamos que eu tenha o GerarPedidoHandler que vá receber como parâmetro a lista de ações que ele precise executar. Caso algumas das ações falhe, eu precise fazer um rollback em todas as ações anteriores e não execute as próximas. Nesse caso, ainda vale a pena passar as ações por parâmetro? Eu implementei isso mas achei a solução feia e não sei até que ponto está correta.


// Nesse exemplo, a ação EnviarEmail lançará uma exceção

class TesteGeraPedido {

    public static void main(String[] args) {
        List<AcoesAposGerarPedido> listaAcoes = Arrays.asList(new SalvarNoBanco(new EnviarEmail(null)));

        GeraPedidoHandler geraPedidoHandler = new GeraPedidoHandler(listaAcoes);

        GeraPedido pedido = new GeraPedido("vitor", 5);
        geraPedidoHandler.handle(pedido);

    }
}

//------------------------------------------------------------------//

public class GeraPedidoHandler {

    List<AcoesAposGerarPedido> acoes;

    public GeraPedidoHandler(List<AcoesAposGerarPedido> acoes) {
        this.acoes = acoes;
    }

    public void handle(GeraPedido geraPedido){
        acoes.forEach(acao -> acao.executarTarefa(geraPedido));

    }
}


//------------------------------------------------------------------//


public abstract class AcoesAposGerarPedido {

    AcoesAposGerarPedido proximaAcao;

    public AcoesAposGerarPedido(AcoesAposGerarPedido proximaAcao) {
        this.proximaAcao = proximaAcao;
    }

    protected abstract void executar(GeraPedido geraPedido) throws Exception;

    protected abstract void desfazer(GeraPedido geraPedido);

    public void executarTarefa(GeraPedido geraPedido){

        try{

            executar(geraPedido);

            if(proximaAcao != null)
                proximaAcao.executarTarefa(geraPedido);


        }catch (Exception e){
            desfazer(geraPedido);

         // Caso a próxima Acao não tenha sido executada, ele irá desfazer mesmo assim, o que não faz sentido
            if(proximaAcao!= null)
                proximaAcao.desfazer(geraPedido);

            throw new RuntimeException(e);
        }

    }
}

//------------------------------------------------------------------//

public class EnviarEmail extends AcoesAposGerarPedido {

    public EnviarEmail(AcoesAposGerarPedido acaoAnterior) {
        super(acaoAnterior);
    }

    @Override
    protected void executar(GeraPedido geraPedido) throws Exception {
        System.out.println("Email enviado");
        throw new Exception("erro ao enviar email");
    }

    @Override
    protected void desfazer(GeraPedido geraPedido) {
        System.out.println("Email desfeito!");
    }
}

//------------------------------------------------------------------//

public class SalvarNoBanco extends AcoesAposGerarPedido{

    public SalvarNoBanco(AcoesAposGerarPedido acaoAnterior) {
        super(acaoAnterior);
    }

    @Override
    protected void executar(GeraPedido geraPedido) {

        System.out.println("Salvou no banco");
    }

    @Override
    protected void desfazer(GeraPedido geraPedido) {

        System.out.println("Desfez operação no banco");
    }
}


//------------------------------------------------------------------//

1 resposta

Oi Pablo,

Para casos mais avançados e com muitas ações eu particularmente prefiro utilizar o padrão Observer, no qual cada ação seria um listener.

Assim as ações não conhecem umas as outras, deixando o código mais desacoplado e limpo, e daria para colocar o código de controle transacional na classe que dispara o evento.

Bons estudos!