Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

DATA

Change capture

Keep a reporting store current with every insert, update and delete, in the order they happened.

You mark a data model as captured and name where its copies go. Dartvel records the writes, delivers them, and catches a destination up after it was down.

ON THIS PAGE

Mark a data model as captured

Say where the copies go

What a destination receives

Delivery, retries and backfill

Watch the lag

Erasure reaches the copies

Status

Mark a data model as captured

Add capture: true to the data model. You read and write it the same way as before. Saving a record also records the change.

// Captured: every insert, update, delete, soft delete and restore is
// recorded, in the order it committed, and delivered to the destinations
// dartvel.capture in pubspec.yaml names. The email is named in each change
// and never carried in it. The subject is whose shipment it is, so an
// erasure reaches the row and every copy made from it.
@DVModel(subject: DVSubject.self, capture: true)
class const _Shipment({
  required final String id,
  required final String reference,
  required final int total,
  @DVModel.sensitiveField() required final String customerEmail,
});

Copy code to clipboard

Say where the copies go

Destinations go under dartvel.capture in pubspec.yaml. This is the only other thing you write.

capture:
  retention: 7d
  destinations:
    warehouse:
      type: database
      connection: WAREHOUSE_URL
      models: [Shipment]
      lagThreshold: 10m

Copy code to clipboard

connection is the name of a secret. The value stays out of the pubspec, and the server reads WAREHOUSE_URL from its environment when it starts. A URL written into the pubspec stops the build with DV-CDC-007.

type: database copies each data model into another database, one collection per model, holding the newest version of each record.

models is optional. Leave it out and the destination gets every captured data model. A name that is not a captured data model stops the build with DV-CDC-008.

retention is how long the log keeps a delivered change. It defaults to 7d. lagThreshold is optional.

A misspelt setting, an unknown type or a duration without a unit stops the build with DV-CDC-006.

What a destination receives

Changes arrive in commit order, each with a sequence number, the record version, the tenant and the transaction id.

Deletes, soft deletes and restores are changes too, so a copy never keeps a record the source removed.

Inside DV.transaction a change is recorded only when the transaction commits. A rollback delivers nothing.

A change is applied by record version. A repeat or a late arrival cannot replace newer data.

Writes made in Studio, and a device's offline writes replayed on the server, are captured like any other save.

Sensitive values never leave

A change names a sensitive field that changed but never carries its value. The log does not store it, and no destination has a place for it.

If a change cannot be recorded, the save is undone and throws DVCaptureWriteError. A saved record always has its change in the log.

Delivery, retries and backfill

The server delivers on the job queue. Each destination has its own queue, so one that is down does not hold up the others.

A refused batch is retried with a backoff and the same change ids. Nothing is skipped.

A destination with no copy yet is backfilled from the records already stored, in chunks, one job each. So is a data model you add to a destination later. The copy then follows new changes from where it began.

A destination that was down for longer than the retention window is backfilled again, so it never has a gap (DV-CDC-002).

If a destination's secret is not set, the server says so with DV-CDC-009 and delivers to the others. Its changes wait in the log until the retention window passes.

A server started without a role runs these jobs itself. In a deployment with worker processes, the workers run them.

Watch the lag

The server measures how far behind each destination is and reports it at /metrics as dartvel_dv_capture_lag_seconds and dartvel_dv_capture_lag_changes. Past lagThreshold it logs DV-CDC-003, so a copy that is hours old is noticed.

Erasure reaches the copies

When DV.Privacy erases a person, the logged values for their records are removed and every destination drops its copy at the same time, without waiting for the next delivery. If a destination cannot be reached, the erasure is marked incomplete. See Privacy and erasure.

Status

Partial

Spec section: Change Data Capture and Warehouse Sync

Planned work and implementation limits

No ClickHouse, BigQuery, Snowflake or Parquet destination.

No Studio view of lag or backfill progress.

A backfill of a data model separated by schema per tenant copies only the default tenant's records.

PREVIOUS Import and export CSV, NDJSON and Excel in and out of a model
NEXT Database SQLite, Postgres, MySQL and migrations
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom