Pipe and Filter
Qué organiza
Pipe and Filter compone procesamiento mediante filters con entradas y salidas definidas, conectados por pipes que transportan datos. Cada filter transforma, selecciona o procesa lo recibido y produce datos según su contrato. Una pipeline lineal es una topología posible; el modelo también admite redes de procesamiento.
Garlan y Shaw describen filters independientes, sin conocimiento de la identidad de sus vecinos ni estado compartido entre filters, y destacan procesamiento incremental de streams. An Introduction to Software Architecture, sección 3.1. Una secuencia de llamadas no constituye automáticamente esta arquitectura: independencia y contratos de composición son parte de la solución.
Filters, pipes y composición
En este ejemplo propio, una aplicación procesa observaciones de sensores:
Data flow:
Input → Decode → Validate → Normalize → Aggregate → OutputDecode transforma bytes en observaciones; Validate distingue datos aceptables; Normalize ajusta unidades; Aggregate calcula resultados. El pipe lleva la salida correspondiente hacia la entrada del siguiente filter. La composición debe especificar también qué ocurre con observaciones inválidas.
Los contratos incluyen representación, significado, cardinalidad y terminación: un filter puede producir cero, uno o varios elementos por entrada. Dos schemas compatibles no garantizan que el resultado tenga el significado que espera el consumidor. Convertir unidades después de agregar valores puede producir un resultado diferente de convertir antes.
La independencia favorece reutilizar un normalizador en varios recorridos. No exige que todas las etapas puedan cambiar de orden arbitrariamente. Tampoco una referencia a una misma tabla mutable compartida mantiene automáticamente la independencia del modelo clásico.
Streaming y barreras
Un filter incremental puede producir salida antes de consumir toda la entrada. Eso permite solapar trabajo y procesar flujos grandes con buffers limitados. Una etapa que ordena todos los elementos puede requerir una barrera o una estrategia de procesamiento por ventanas.
Un compilador puede representarse conceptualmente como:
Source → Lexer → Parser → Optimizer → Code GeneratorEl ejemplo no clasifica cualquier compilador real como Pipe and Filter. Garlan y Shaw explican que fases no incrementales y estructuras compartidas pueden alterar ese modelo. Una lista de nombres de fases no demuestra los contratos ni su independencia.
Otro recorrido posible es Decode→Validate→Transform→Enrich→Persist para ingestión. Se debe examinar qué estado necesita cada etapa y qué garantías exige Persist; no convertir el esquema en una receta universal de ETL.
Implementación de los pipes
Un pipe puede materializarse mediante llamadas o canales en memoria, streams, queues, archivos entre etapas, procesos comunicados o canales de red. Pipe no equivale a Message Queue. El patrón conceptual no obliga a broker, distribución ni procesamiento asíncrono.
Una función que entrega un valor al siguiente filter puede conservar el modelo si los contratos y responsabilidades son independientes. Una cadena que comparte implícitamente un objeto global con todas las reglas mezcladas no adquiere composability por usar métodos separados.
Archivos completos entre etapas suelen llevar hacia batch sequential cuando no existe intercambio incremental. No conviene ocultar esa diferencia bajo una etiqueta de streaming. La topología, el transporte y el execution model necesitan decisiones separadas.
Orden, paralelismo y estado
Varias etapas pueden trabajar simultáneamente sobre elementos diferentes. También pueden existir varias instancias de un filter, siempre que sus reglas de partición, estado y orden lo permitan. Aumentar instancias no reduce necesariamente la latencia de un solo elemento que debe atravesar todas las transformaciones.
Stateless no significa «sin datos», sino que el resultado no requiere conservar contexto mutable entre invocaciones. Un agregado por sensor puede ser stateful: necesita identificar qué entradas pertenecen a cada grupo y quién controla su estado. Encapsular estado local es distinto de permitir que todos los filters modifiquen una representación global.
La guía de Pipes and Filters de Microsoft discute independencia, paralelismo, estado y mensajes repetidos. Para el ejemplo de sensores, debemos decidir si se exige orden por sensor, qué ocurre con datos tardíos y cómo se restaura un agregado. Esa decisión limita la distribución segura del trabajo.
Buffers y backpressure
Si Decode produce más rápido de lo que Aggregate consume, se acumula trabajo entre ellos. El buffer puede absorber una ráfaga; si la diferencia se sostiene, el consumo de memoria o almacenamiento sigue creciendo.
backpressure desarrolla cómo la capacidad downstream afecta la admisión upstream. flow-control ayuda a expresar cuánto puede emitirse y queue-management aborda backlog, espera y políticas de cola. Aquí interesa aplicarlos al límite entre etapas, sin duplicar sus definiciones.
En nuestro ejemplo, respuestas posibles incluyen ralentizar el lector, acotar buffers, rechazar o descartar bajo una política explícita, ampliar capacidad del stage o cambiar scheduling. Acotar el buffer sin definir qué hace el productor cuando se llena deja incompleto el diseño. Descartar puede ser aceptable para cierta telemetría y prohibido para otro dato.
Si falla Filter B
El contrato debe distinguir errores del elemento, fallos transitorios y errores de programación. Datos inválidos no se corrigen necesariamente repitiendo el mismo procesamiento. Estas alternativas tienen consecuencias distintas:
| Respuesta | Obligación que debe revisarse |
|---|---|
| Fallar toda la pipeline | Qué resultados parciales ya se produjeron. |
| Reintentar una etapa | Efectos repetibles y trabajo adicional. |
| Omitir un elemento | Si perderlo conserva el resultado permitido. |
| Enviarlo a otra ruta | Ownership del diagnóstico y recuperación. |
| Compensar | Cómo corregir efectos, si existe una operación válida. |
| Reiniciar desde checkpoint | Qué entrada, estado y salida fueron confirmados. |
retry puede introducir duplicados, alterar orden, aumentar backlog y amplificar carga. Si Persist guardó un resultado y falló antes de confirmar progreso, repetir puede guardar otra vez. idempotency ayuda a analizar el efecto repetido; una queue no lo elimina por sí misma.
Un checkpoint solo es confiable si su progreso corresponde a estado y efectos ya confirmados. Una dead-letter-queue puede apartar elementos problemáticos, pero no completa automáticamente la recuperación ni restablece un agregado que ya cambió. Compensar también requiere semántica de negocio y no implica rollback universal.
Observabilidad, comparación y alcance
En nuestro ejemplo, conviene observar throughput, duración, espera, ocupación de buffers, reintentos y resultados rechazados por etapa. Correlacionar identidad de entrada y versión de transformación ayuda a reconstruir un resultado sin confundir intentos con elementos únicos.
layered-architecture organiza por responsabilidad o abstracción; Pipe and Filter organiza por transformación y composición de datos. Pueden coexistir: una aplicación por capas puede encapsular una pipeline en su procesamiento de negocio. Una etapa tampoco equivale necesariamente a una layer.
Un servicio de microservices puede contener una pipeline local. Usar mensajes o eventos para conectarla no demuestra automáticamente event-driven-architecture: el profile necesita significancia arquitectónica de esos hechos y contratos. architecture-profile y internal-structure conservan esos alcances.
El patrón resulta útil cuando las transformaciones tienen fronteras y contratos reutilizables. Se vuelve costoso si las etapas dependen de abundante contexto compartido, conversiones repetidas o una transacción indivisible. Las flechas deben explicar datos y obligaciones reales, no decorar una ejecución secuencial.