Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design pattern: versioning #52

Open
sisp opened this issue Oct 4, 2019 · 4 comments
Open

Design pattern: versioning #52

sisp opened this issue Oct 4, 2019 · 4 comments
Labels
❔ question General question

Comments

@sisp
Copy link
Contributor

sisp commented Oct 4, 2019

I've been thinking how to deal with model versioning. The scenario I'm looking at is saving a snapshot of the current state in local storage, so when the browser is closed and re-opened (or the app is reloaded), the previous state is automatically loaded again. All is good as long as the state layout does not change. But what happens when a snapshot is saved, I change a model class (or multiple model classes, or the entire state layout), re-deploy the app, and then the snapshot, which has the old layout, is loaded by the new state implementation? A way to migrate from one/any (old) snapshot version to the current version is needed, I think.

A couple of questions I've been asking myself:

  • Should models have version information (e.g. semver-style versions)?
    • What is the version of a parent model class when the version of one of its child model classes changes?
    • Would each model class implement migration code in its fromSnapshot method?
  • Should the root store have a version?
  • How and where is migration code implemented for complex layout changes?
@xaviergonz
Copy link
Owner

xaviergonz commented Oct 4, 2019

That's why "fromSnapshot" in models are there.

E.g.

// first version
@model("...")
class M extends Model({
}) {
  _version: prop(1)
  fullName: prop<string>()
} {}

// second version
@model("...")
class M extends Model({
}) {
  _version: prop(2)
  firstName: prop<string>(),
  lastName: prop<string>()
} {
  fromSnapshot(maybeOldSnapshot: any) {
    if (maybeOldSnapshot._version >= 2) {
      return maybeOldSnapshot // up to date
    }
    // update to latest
    return {
      _version: 2,
      firstName: maybeOldSnapshot.fullName.split(" ")[0],
      lastName: maybeOldSnapshot.fullName.split(" ")[1],
    }
  }
}

That way whenever an old snapshot is loaded with fromSnapshot(...) it will be updated to the latest version.

Also this way parent models don't have to worry about the versioning of its children, just about their own.

@xaviergonz xaviergonz added the ❔ question General question label Oct 4, 2019
@terrysahaidak
Copy link

terrysahaidak commented Oct 4, 2019

I implemented some kind of persist library with built-in support for migrations for mobx-state-tree.

The idea here is to have serialize, deserialize and migrate methods in each migration.

The serialize defines how to transform data from the snapshot to JSON. The main mission of it is resolving all the references, for example, I'm storing part of the store where I'm using some reference, in serialize I'm finding the exact entity and storing it too.

The deserialize doing vise-versa.

And the migrate transforming snapshot from v1 to v2. I'm storing the last migration index in the local store. It's pretty the same thing we do on the backend side. Each migration migrate snapshot.

With mobx-keystone, it should work even better since we have the exact model name inside each entry in the snapshot, so it might help to resolve and work with it.

@sisp
Copy link
Contributor Author

sisp commented Oct 5, 2021

See #326 for the latest API related to versioning.

@PEZO19
Copy link

PEZO19 commented May 2, 2023

@xaviergonz, regarding #52 (comment):

is there a preferred, canonical way to handle more complex scenarios?

  1. An old model is split into multiple new models
  2. Multiple old models are merged into one new model
  3. Both 1 and 2 happen in same migration, multiple occurrences

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
❔ question General question
Projects
None yet
Development

No branches or pull requests

4 participants