TPF and DDD Alignment (Current + Future) ​
This guide summarizes how TPF aligns with DDD concepts today, where it diverges, and what we intend to improve. It captures the key ideas from the checkpoint-pipeline discussion so newcomers can quickly orient themselves.
Executive Summary ​
- TPF aligns with DDD at the use-case level: a pipeline is closest to an application service.
- TPF diverges at the aggregate level: instead of a single aggregate with multiple tables, TPF uses a progression of immutable aggregate states, each of which is stored in its own table.
- DDD layering is possible but not enforced: it depends on how steps are written.
- Cross-context orchestration is compatible but needs explicit contracts and backpressure-aware piping.
DDD Terms (short and concrete) ​
- Application service: orchestrates a use case; coordinates domain operations and infrastructure.
- Domain operation: a business behavior on an aggregate or domain service (invariants live here).
- Aggregate: consistency boundary in the domain model.
- Bounded context: boundary where a model and language are consistent.
- Cross-context orchestration: communication across bounded contexts (typically async in traditional DDD; TPF may prefer synchronous piping where practical - see §5 and the discussion below).
TPF Mapping to DDD ​
1) Application Service ​
TPF mapping: A pipeline is a use-case orchestrator. Each pipeline step can be seen as a slice of the application service.
- Good alignment: pipelines naturally express use-case flow, sequencing, and side effects.
- Risk: steps can accidentally mix domain logic and infrastructure if not disciplined.
- TPF guidance: keep steps thin; put business rules inside domain types or domain services called by the step.
2) Domain Operation ​
TPF mapping: A step can wrap a domain operation, but the operation should still live in the domain layer.
- Good alignment: step calls a domain method (e.g.,
Order.create,Order.approve). - Risk: domain logic implemented directly in step classes (layering leak).
- TPF guidance: steps should orchestrate; domain operations should enforce invariants.
3) Aggregate Boundary ​
TPF mapping: The pipeline becomes the aggregate boundary, not a single class/table.
- TPF uses a progression of aggregate states (one per step/table).
- This is a deliberate trade-off: immutable, append-only checkpoints instead of in-place updates.
- Example:
OrderRequest->InitialOrder->ReadyOrderas successive stable states.
4) Bounded Context ​
TPF mapping: A pipeline (or set of pipelines) can represent a bounded context.
- Works best when the context owns its data and invariants.
- Cross-context calls should use explicit handoff contracts.
- TPF guidance: treat pipeline-to-pipeline contracts as public APIs, not shared internal types.
5) Cross-Context Orchestration ​
TPF mapping: Sync, reactive piping is possible; async is optional.
- The main risk is consistency illusion across pipelines.
- TPF needs explicit connector semantics (backpressure + idempotency).
- TPF guidance: only assume strong consistency inside a single pipeline; be explicit about cross-pipeline guarantees.
Workflow Shape: Linear/Tree vs DAG ​
TPF favors linear or tree-like flows where possible:
- Linear/tree workflows are easier to reason about and preserve pipeline semantics.
- DAG (directed acyclic graph) workflows introduce joins, fan-in, and potential feedback loops, which complicate ordering, timeouts, and backpressure.
The checkpoint model works best when pipelines are composed in a tree-like fashion, with explicit handoff contracts at each edge.
Decision as Checkpoint (rule of thumb) ​
Rather than introducing a special decision step, a decision can be modeled as a checkpoint boundary:
- End the current pipeline at the decision.
- Start one pipeline per outcome branch.
- This keeps step semantics simple and makes outcomes explicit.
Operationally this adds orchestration, but it keeps the design model stable as the system grows.
Autonomy vs Consistency (TPF stance) ​
TPF does not treat autonomy as a default good. In a single business with a unified tech stack:
- Strong checkpoint consistency is preferred within a pipeline.
- Sync piping is preferred between pipelines when practical.
- Eventual consistency is only accepted when explicitly chosen.
This differs from FTGO (Food To Go, from Chris Richardson's Microservices Patterns) default assumptions of async sagas and autonomy-first boundaries.
CreateOrder / ApproveOrder Examples (FTGO mapping) ​
CreateOrder
- TPF pipeline steps:
OrderRequestProcess->OrderCreate->OrderReady - Each step persists its own immutable state.
- The pipeline output is the checkpoint, not a mutable aggregate.
ApproveOrder
- A separate pipeline that consumes the checkpoint and produces a new immutable state.
- If not explicitly modeled as a business pipeline step, an approval failure is treated as an operational concern (handled via retries, alerts, monitoring, etc.), whereas modeling it as a business pipeline makes it part of the business workflow and subject to business rules and compensating actions.
DDD Layering in TPF ​
TPF does not enforce layering. A suggested discipline:
- Domain layer: entities, value objects, domain services.
- Application layer: pipeline steps that orchestrate domain operations.
- Infrastructure layer: persistence, mappers, transports.
Current Gaps ​
- No explicit aggregate boundary declaration.
- No built-in enforcement of layering.
- Pipeline-to-pipeline contracts are implicit (build-time checks needed).
Planned Directions ​
- Handoff contracts between pipelines (explicit, build-validated).
- Traceability: built-in lineage tracking (
TraceEnvelope, a planned runtime wrapper that carries a payload plus a reference to the previous item for lineage). - Backpressure contracts across piped pipelines.
- Error sink as a first-class runtime component.
- Workflow constraints: optional guidance to keep pipelines linear or tree-like by default.
- Design diagnostics: flag risky patterns (e.g., implicit DAG joins, unbounded buffering).
- Workflow fan-out: allow sub-pipelines to branch from a step with explicit branch policies; fan-out implies controlled tree-like branching only (no joins/merges that would create general DAG topologies).
- Observers vs taps: distinguish stable checkpoint observers from mid-step taps with weak guarantees.
- Remote subscription trigger: orchestrators accept streaming input for pipeline-to-pipeline chaining.