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

Event API Implementation #12

Open
cuebit opened this issue May 9, 2020 · 2 comments
Open

Event API Implementation #12

cuebit opened this issue May 9, 2020 · 2 comments
Assignees
Labels
enhancement New feature or request

Comments

@cuebit
Copy link
Member

cuebit commented May 9, 2020

This topic is open to consider implementation of the Event API, how best to proceed in designing such implementation that will continue to endure through future releases while being supportive of authors of the Vuex ORM ecosystem.

Global Events

As it stands, the @latest release observes selection and mutation hooks. These hooks are triggered on a singleton (the Query class) and accumulated from the models by way of declarative static methods.

The behaviour for event handling will need to change considering Query is no longer a singleton and potentially out-of-scope for such a requirement. However, global hooks still require a singleton for global event listening/broadcasting.

One suggestion being that the database instance, given it is a singleton in instance form and easily accessible, could be a point of listening/emitting. For example:

store.$database.on('query:beforeSelect', handler)

The benefit of this pattern enables event scoping by prefix (in string representation). This is purely for emitting and subscribing to an event. Models can still declare static methods.

Naming Convention

The subject of normalizing or changing the naming convention for these hooks internally are also up for debate. By internally we mean the name of events emitted rather than the method names bound to a model since these model methods do not subscribe to events directly, rather they are declared as reference handlers.

Selection Hooks

The current selection hooks are: beforeSelect, afterWhere, afterOrderBy and afterLimit.

Selection hooks pass a collection of models tappable for mutation to which a query instance is currently processing.

Mutation Hooks

The current mutation hooks are: beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete and afterDelete.

Mutation hooks pass the model in the current iteration queue for persistence. Should a models handler return false, the model is skipped and not persisted.

Design Limitations

The crux with event handling is that it is stateless therefore a broadcasted event shouldn't be required to read, let alone expect, a response from subscribers – which proves problematic if events such as beforeCreate or beforeSelect may be required to return. The overhead caused by evaluating a response on each model that's listening inclusive of any additional subscribers to the event is cause for concern. In addition, the @latest release already possesses quantitive performance degradation and @next, at the time of writing, has no performance tests in place to verify any improvement on that front. So it's debatable how taxing this may still be.

For that reason, it's worth considering how to proceed with an idiomatic and synchronous Event API. For example, we may well continue to support return values from event handlers, consequently taking the natural hit on performance. Or we decide to cut ties with this approach and leave it down to user-land logic to process data before asking Vuex ORM to persist it.

Ecosystem

The Event API in its current form also works in favour of plugin authors. Not only can the Events API be integrated on objects that extend it, emitting internal events can enable plugin authors to tap into points for plugin logic. This isn't something we necessarily need to give too much thought, since plugin authors will often circumvent default behaviours through monkey-patching, but can grow as required for non-public logic.

Adding to this, plugins can leverage and adapt the Event API for its own use, rather than “bring your own” functionality which further bears weight on an application using assistive plugins.

Discuss.

@cuebit cuebit added the enhancement New feature or request label May 9, 2020
@cuebit cuebit self-assigned this May 9, 2020
@cuebit
Copy link
Member Author

cuebit commented May 9, 2020

We may well continue to support return values from event handlers, consequently taking the natural hit on performance. Or we decide to cut ties with this approach and leave it down to user-land logic to process data before asking Vuex ORM to persist it.

A recursive scenario in this case would be a typical setup with Vuex ORM and Vuex ORM Axios. A user fetches data, transforms it and passes it to Vuex ORM to persist. The decision to ignore persistence could easily be achieved in the Vuex ORM Axios plugin data transformer, or in between fetching, processing and persisting (with or without the axios plugin). Besides, response data that is expected to be dissolved is almost a waste of request & response load and timing.

If return values are of essence for certain events, then we can still achieve this by passing in a curry function that self executes and splice's the model from the array. After all, splice mutates the original array.

Similarly, and this will become evident soon, the Collection API that will be proposed actually has a remove method that accepts a primary key, model instance or a predicate that handles splicing of a collection... So passing in a collection instance and instructing users to use the remove method works in our favour...

@cuebit cuebit mentioned this issue May 9, 2020
10 tasks
@kiaking
Copy link
Member

kiaking commented May 9, 2020

If return values are of essence for certain events, then we can still achieve this by passing in a curry function that self executes and splice's the model from the array. After all, splice mutates the original array.

Yeah I also though about this. Mutate the payload directly. It could play nicely with Vue and Vuex since both of them prefer mutating the reactivity state directly rather than returning new object.

Though for select hook, while we could mutate the payload, it would be easier if users can define a hook like this.

beforeSelect (models) {
  return models.filter(m => m.active)
}

Maybe for the hooks, we should make it treat a bit special way while using the event api internally. I'll try think about this a bit more.

@kiaking kiaking added this to the v1.0.0-alpha.1 milestone Nov 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants