Estou trabalhando para refatorar certos aspectos de um serviço da web existente. A maneira como as APIs de serviço são implementadas é por meio de um tipo de "pipeline de processamento", onde existem tarefas que são executadas em sequência. Sem surpresa, as tarefas posteriores podem precisar de informações calculadas por tarefas anteriores e, atualmente, a maneira como isso é feito é adicionando campos a uma classe "estado do pipeline".
Eu estive pensando (e esperando?) Que existe uma maneira melhor de compartilhar informações entre as etapas do pipeline do que ter um objeto de dados com um zilhão de campos, alguns dos quais fazem sentido para algumas etapas de processamento e não para outras. Seria uma grande dor para tornar essa classe segura para threads (não sei se isso seria possível), não há como argumentar sobre seus invariantes (e é provável que não tenha nenhum).
Eu estava folheando o livro de padrões de design da Gang of Four para encontrar alguma inspiração, mas não sentia que houvesse uma solução lá (o Memento estava um pouco no mesmo espírito, mas não exatamente). Também procurei on-line, mas no momento em que você pesquisa por "pipeline" ou "fluxo de trabalho", você é inundado por informações sobre tubos Unix ou mecanismos e estruturas de fluxo de trabalho proprietários.
Minha pergunta é: como você abordaria a questão de registrar o estado de execução de um pipeline de processamento de software, para que tarefas posteriores possam usar as informações computadas pelas anteriores? Eu acho que a principal diferença com os pipes Unix é que você não se importa apenas com a saída da tarefa imediatamente anterior.
Conforme solicitado, algum pseudocódigo para ilustrar meu caso de uso:
O objeto "contexto do pipeline" possui vários campos que as diferentes etapas do pipeline podem preencher / ler:
public class PipelineCtx {
... // fields
public Foo getFoo() { return this.foo; }
public void setFoo(Foo aFoo) { this.foo = aFoo; }
public Bar getBar() { return this.bar; }
public void setBar(Bar aBar) { this.bar = aBar; }
... // more methods
}
Cada uma das etapas do pipeline também é um objeto:
public abstract class PipelineStep {
public abstract PipelineCtx doWork(PipelineCtx ctx);
}
public class BarStep extends PipelineStep {
@Override
public PipelineCtx doWork(PipelieCtx ctx) {
// do work based on the stuff in ctx
Bar theBar = ...; // compute it
ctx.setBar(theBar);
return ctx;
}
}
Da mesma forma, para um hipotético FooStep
, que pode precisar da barra computada pelo BarStep antes dela, juntamente com outros dados. E então temos a chamada de API real:
public class BlahOperation extends ProprietaryWebServiceApiBase {
public BlahResponse handle(BlahRequest request) {
PipelineCtx ctx = PipelineCtx.from(request);
// some steps happen here
// ...
BarStep barStep = new BarStep();
barStep.doWork(crx);
// some more steps maybe
// ...
FooStep fooStep = new FooStep();
fooStep.doWork(ctx);
// final steps ...
return BlahResponse.from(ctx);
}
}