Skip to main content
Embedding the Fetcher Engine takes three steps: import it, provide the ports it needs, construct it with engine.New. No infrastructure ships with the import. You wire only the parts your host application actually uses.

1. Install


The Engine is a distinct Go module from the Fetcher services (github.com/LerianStudio/fetcher/v2). It carries zero third-party dependencies and has its own version line with path-prefixed tags (pkg/engine/vX.Y.Z). The import gives your module graph none of the service dependencies.

2. Provide the ports


The Engine depends only on host-provided interfaces. One port is always required. A second is required only when encrypted persistence is on. The rest are opt-in, and the Engine degrades gracefully without them. The port reference documents each contract and each degradation in full. Read it before you decide which ports to skip — “optional” does not mean “harmless”. For tests and first runs, the pkg/engine/memory harness provides in-memory implementations of the storage-facing ports: the connector registry, the connection store, the schema cache, the result sink, and the execution store. You need no MongoDB, Redis, RabbitMQ, or object storage to exercise the Engine. The harness ships no CredentialProtector, so a test that turns encrypted persistence on must supply one.

3. Construct, plan, execute


This example is self-contained. It uses the in-memory harness, so it runs with zero infrastructure.

What the example shows

  • One required option. WithConnectorRegistry is the only port engine.New insists on. The connection store here is a convenience, but skip it and every operation fails.
  • Construction-time validation. engine.New rejects a port passed as a typed nil, not only a literal nil. A misconfigured host fails at construction instead of panicking at first use.
  • Tenant scope on every call. NewTenantContext builds the only isolation dimension the Engine knows. It carries a tenant ID and an optional request ID — no organization and no product.
  • Plan, then execute. PlanExtraction validates the request against the live schema and applies the limits. ExecuteExtraction reads the plan.
  • Mode by composition. The example wires no ResultSink, so the Engine picks direct mode and returns the bytes inline with a SHA-256 digest. Add a sink and the same code returns a storage reference instead. See Direct mode and Store mode.

Moving to production


Swap the memory harness for real adapters. Fetcher’s own services are the reference implementation, and their source is public. They bridge the Engine ports to real infrastructure under pkg/enginecompat: Read how the services wire them:
  • Connection CRUDcomponents/manager/internal/bootstrap/connection_engine.go
  • Schema discovery and cachingcomponents/manager/internal/bootstrap/schema_engine.go
  • Plan and execute extractioncomponents/worker/internal/bootstrap/extraction_engine.go
Two rules carry over from the harness to production:
  1. Your adapters own tenant scope. The Engine passes a tenant context to every port call and enforces the boundary at its own edge. A store that ignores the tenant ID leaks data across tenants, and the Engine cannot catch that for you.
  2. Your adapters own secrets. The Engine calls Protect and Reveal and records only the returned key version as metadata. Key derivation, rotation, and storage stay in your host.

Next steps


Port reference

Every port, its contract, and the behavior you get without it.

Engine overview

The three-layer model, the import boundary, and the two result modes.