Estoy trabajando para refactorizar ciertos aspectos de un servicio web existente. La forma en que se implementan las API de servicio es mediante una especie de "canal de procesamiento", donde hay tareas que se realizan en secuencia. Como era de esperar, las tareas posteriores pueden necesitar información calculada por tareas anteriores, y actualmente la forma en que se hace esto es agregando campos a una clase de "estado de canalización".
He estado pensando (¿y esperando?) Que hay una mejor manera de compartir información entre los pasos de la tubería que tener un objeto de datos con un trillón de campos, algunos de los cuales tienen sentido para algunos pasos de procesamiento y no para otros. Sería un gran dolor hacer que esta clase sea segura para subprocesos (no sé si sería posible), no hay forma de razonar sobre sus invariantes (y es probable que no tenga ninguna).
Estaba hojeando el libro de patrones de diseño Gang of Four para encontrar algo de inspiración, pero no sentía que hubiera una solución allí (Memento tenía algo del mismo espíritu, pero no del todo). También busqué en línea, pero en el momento en que buscas "canalización" o "flujo de trabajo", te inunda la información de tuberías de Unix o los marcos y motores de flujo de trabajo patentados.
Mi pregunta es: ¿cómo abordaría el problema de registrar el estado de ejecución de una canalización de procesamiento de software, de modo que las tareas posteriores puedan utilizar la información calculada por las anteriores? Supongo que la principal diferencia con las tuberías Unix es que no solo te importa el resultado de la tarea inmediatamente anterior.
Según lo solicitado, algunos pseudocódigo para ilustrar mi caso de uso:
El objeto "contexto de canalización" tiene un montón de campos que los diferentes pasos de canalización pueden llenar / leer:
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 uno de los pasos de la tubería también es un 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;
}
}
Del mismo modo para un hipotético FooStep, que podría necesitar la barra calculada por BarStep antes, junto con otros datos. Y luego tenemos la llamada 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);
}
}