How Cindel Runs Migrations
Cindel stores a database-level migration version inside every database file. When you open the database,CindelMigrationPlan compares the stored version with targetVersion, runs any missing steps in order, persists each successful toVersion after the step completes, and returns a database opened with the target schemas. Completed steps are skipped on future opens; only the outstanding steps run.
Core Types
CindelMigrationPlan
Declare a plan with a targetVersion, a baselineVersion (for databases that have no stored version yet), and an ordered list of steps:
CindelMigrationStep
Each step describes exactly one version move and carries its own schemas, migration logic, and optional verification hooks:
Step Lifecycle
Every migration step follows the same ordered lifecycle:1
Export old data
Use Both APIs read data in id order with bounded batches, so the process is predictable regardless of collection size.
context.exportObjects(schema) to read old typed objects, or context.exportDocuments(schema) to read raw map-shaped documents.2
Register target schemas
Call Do not import target objects before this call.
context.registerTargetSchemas() to prepare the target collections before you write any converted data into them.3
Import converted data
Use Make sure required fields are filled, removed fields are no longer referenced, and ids are preserved when records should remain the same logical objects.
context.importObjects(schema, iterable) for new typed objects, or context.importDocuments(schema, iterable) for target documents.4
Verify before and after (optional)
The Useful checks: required collections exist, document counts match expectations, ids were preserved, and converted values are valid for the new model.
verifyBefore hook runs before data is rewritten. The verifyAfter hook runs after. Throw from either hook to abort the step and prevent it from being marked complete.Complete Example: Renaming a Field
This example migrates aUser model from version 1 to version 2, where the fullName field is renamed to name and the email_address persisted field is normalised to email.
Automatic Compaction
SetcompactOnSuccess: true on your CindelMigrationPlan to have Cindel call compact() automatically after a migration finishes. This reclaims storage space freed by replacing old records and is the preferred way to compact after a migration.
Best Practices
- Keep steps small and single-purpose. One step should handle exactly one version move. For two releases of schema changes, write two steps — not one step that tries to handle every old shape.
- Preserve
dbIds. When existing records should remain the same logical objects after migration, copyold.dbIdinto the new object during conversion. - Always pass the migration plan at startup. Leave the plan in place until you are certain that no supported device can have a database below
targetVersion. Removing the plan prematurely leaves some users unable to open their database. - Choose your export format deliberately. Use
exportObjectswhen the old schema still produces useful Dart objects. UseexportDocumentswhen fields are being renamed heavily and you need direct access to persisted field names.
Low-Level Primitives
migrationVersion(), setMigrationVersion, registerMigratedSchemas, and compact() are low-level primitives exposed for controlled tooling and diagnostics. Do not call them directly in application migration code — use CindelMigrationPlan and CindelMigrationStep instead.