Migrate systems onto Flowcore v2
Bergur DavidsenUpdated 2026-07-16
A Flowcore v2 migration changes more than an API client. Legacy systems may rely on scenarios, strands, transformer-shell pods, direct webhook calls, or mutable database tables as the only record of business activity. A safe migration preserves existing behavior while moving ownership to explicit event contracts, v2 resources, and supported delivery models.
Start by understanding which part of the system is authoritative today. Do not decommission the old path until the new one has processed real traffic and its output has been reconciled.
Define the migration boundary
A greenfield service can begin directly with v2 manifests, @flowcore/pathways, and a managed Data Pathway. An existing Flowcore service needs a different plan because retained events, legacy subscriptions, and deployed transformer pods may still be active. A database-first application may need historical import and a staged move of read models.
Document the current trigger, business state change, downstream effects, identifiers, timestamps, and owner for each workflow being migrated. This reveals which changes are durable facts and which tables or messages are only derived implementation state.
Avoid translating every database row into an event mechanically. An event should explain what happened in the business domain, not reproduce a storage schema that happened to exist before Flowcore.
Replace legacy concepts deliberately
Older Flowcore systems may use scenarios and strands to run tenant-specific transformer-shell pods. New v2 services should normally run as ordinary applications with their own deployment, health checks, logs, and transform endpoint. Delivery comes from a managed Data Pathway or an intentionally operated virtual pump.
Platform resources should move into reviewed flowcore.io/v2 and iam.flowcore.io/v1 manifests. Application code should use PathwaysBuilder for contracts, publishing, and handlers, or @flowcore/sdk when direct platform commands are required.
Legacy GraphQL or webhook behavior may still exist during transition. Treat it as a compatibility path with a removal owner rather than letting it remain an undocumented permanent dependency.
Design the target event contracts first
Stabilize event names and payload meaning before moving runtime infrastructure. For each business action, decide which event represents the accepted outcome, which source identity prevents duplicate import, and which consumers need historical replay.
Preserve business-effective time separately from ingestion time when importing history. A record created years ago should not appear to have happened at migration time simply because that is when Flowcore stored it.
If the old and new payloads are incompatible, introduce a new version rather than changing the meaning of existing history. Compatibility bridges can translate between versions temporarily while consumers migrate.
Choose a cutover strategy
The safest default is a strangler migration: begin publishing durable facts at an existing write boundary, build a new projection or consumer, compare it with current behavior, and move traffic gradually.
A parallel run is useful when output can be compared without causing duplicate external side effects. Historical import followed by a forward-only cutover works when the source can provide stable records and a clear cutoff time. A direct service replacement is appropriate for an isolated legacy transformer whose input and output contracts are already known.
Whichever strategy you choose, define how duplicates are prevented and how rollback works. Running old and new delivery simultaneously can produce the same side effect twice unless consumers use stable idempotency keys.
Migrate a transformer-shell service
A modern replacement owns its normal application runtime and receives events through a transform endpoint. The Pathways registration should distinguish inbound contracts from events the service is allowed to publish.
const pathways = new PathwaysBuilder({
baseUrl: "https://webhook.api.flowcore.io",
tenant: process.env.FLOWCORE_TENANT!,
dataCore: process.env.FLOWCORE_DATA_CORE!,
apiKey: process.env.FLOWCORE_API_KEY!,
})
.register({
flowType: "order.0",
eventType: "order.import-requested.0",
schema: importRequestedSchema,
writable: false,
})
.register({
flowType: "order.0",
eventType: "order.import-completed.0",
schema: importCompletedSchema,
writable: true,
})Expose PathwayRouter through an authenticated /transform endpoint and configure a managed Data Pathway to subscribe only to the inbound event. Do not subscribe the service to its own completion event unless it intentionally handles that event as a separate workflow.
When publishing an outbound event that has no local handler, use the supported fire-and-forget option so the write does not wait for local processing. Keep payloads below the webhook limit and move large file content into object storage with reference events.
Do not call broad provisioning from a runtime key that only has ingestion access. Create resources through manifests or a bootstrap identity, then run the service with narrower permissions.
Import historical records safely
A historical import needs a stable source ID, a documented time mapping, and a reconciliation result. Divide large input into bounded chunks and record which rows were accepted, rejected, or uncertain.
If a request times out after ingestion, retry with the same source identity. Generating a new identity for every attempt makes it impossible to distinguish a legitimate second fact from a duplicate import.
Import only the history required for audit, replay, or the new projection. Data that is no longer useful or that should not be retained does not become safer merely because it has been moved into an event platform.
Migrate projections before query traffic
Build idempotent v2 handlers and backfill the new projection while the old read path still serves users. Compare counts, important aggregates, and representative records. Differences need an explanation before cutover; matching row counts alone can hide incorrect values.
When the new projection is trustworthy, switch the query API or UI deliberately. Keep the old model available briefly for rollback, but stop writing to it once ownership has moved unless a temporary compatibility requirement is documented.
Rotate identities during the move
Migration is a good time to replace shared tenant-wide credentials with one key per service and environment. Separate bootstrap access from runtime publication and consumption. Rotate transform secrets with an overlap period so managed delivery does not fail during deployment.
After decommissioning a strand or compatibility bridge, revoke its key and remove its role bindings. Deleting the pod while leaving broad credentials active does not complete the migration.
Verify before decommissioning the old path
Publish a representative production event and trace it through Flowcore, the new pathway, handler, and projection. Confirm that duplicate delivery remains safe and that operational dashboards show current progress.
Then stop the old consumer without deleting its configuration immediately. Observe the new path through a defined confidence window. Once traffic, reconciliation, and rollback evidence are satisfactory, remove the legacy scenario, strand, scripts, credentials, and alerts in a separate reviewed cleanup.
Continue with Diagnose common Flowcore failures for issues that can appear during and after cutover.