Skip to content

non-singlton database management #514

@kiaking

Description

@kiaking

This issue relates to several other issues, for example, #360.

What is the problem?

Currently, the Vuex ORM stores database to the Container object, which is a global singleton. This is needed to retrieve any relationships through Vuex Getters and Actions.

Having a singleton object is not always bad. As long as you are using it in a single app, everything should work fine. However, it's a bad practice when doing SSR, as stated here at Vue SSR doc.

Usually, models are stateless, and it can be a singleton, but the database is not because the database holds Vuex Store instance, and that's stateful. Vuex ORM works with SSR, but it might cause unexpected behavior in a future implementation, so I would like to discuss how we can achieve non-singleton based architecture.

How would API look like

The simplest solution I can think of is to register database instance to the Vuex Store instance, then fetch model from the store instance. It could look like this.

export default {
  mounted () {
    // `users` is the entity name for the user model.
    this.$store.$db().users.insert({ data: {...} })
  }
}

Now, this is not going to be type-safe. To be able to get typing for the user model, we could do something like this.

import User from '@/models/User'

export default {
  mounted () {
    this.$store.$db().model(User).insert({ data: {...} })
  }
}

This will not look great as just doing User.insert() though, I think there's no easy way to retrieve injected store instance from the model.

It's a bit similar to how Redux ORM is doing things.

With the above approach, we can now easily create more than 1 database and attach them to the store.

export default {
  mounted () {
    // Get first database.
    this.$store.$db('entities').$model(User).insert({ data: {...} })

    // Get the second database.
    this.$store.$db('other_db').$model(User).insert({ data: {...} })
  }
}

Singleton based approach

As mentioned before, a singleton is not always bad. When you're not working with SSR, most of the time, you can use stateful singletons. In that case, we could continue with the current approach. In order to have multiple database connections, we could add new, for example, on method to the model. And you may;

import User from '@/models/User'

export default {
  mounted () {
    // Get first database.
    User.on('my_database').insert()
  }
}

This is similar to how Laravel handles multiple DB connections with their models.

We could set model connections when registering models to the database so that users don't have to set the connection name by themselves when using.

The implementation

To adapt to both approaches described above, I'm thinking of doing this.

  1. Register database instance on both Container object and store instance when installing Vuex ORM.
  2. When calling model methods in the singleton approach, fetch database instance from the container.
  3. When calling model methods in the injected approach, let store.$db() method inject the store to the model instance.

Probably I need to think this deeper, but here's the starting point.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestreleasedThe issue was implemented and it is released publicly

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions