Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

We have an internal lib for data management that’s philosophically similar to linear too. I opted for having required transactions for developer safety.

Imagine that you support the model discussed above where it’s possible to update the local store optimistically without syncing back to the db. Now you’re one missing .save() away from having everything look like it’s working in the frontend when really nothing is persisting. It’s the sort of foot gun that you might regret supporting.

Our model is slightly different in that we require the .save() on objects to create the mutation for the sync. The primary reason is that we’re syncing back to real tables in Postgres and require referential integrity etc to be maintained.

    tx((db) => {
      const author = new Author(db)
      author.save()
      article.name = “New name”
      article.author = author
      article.save()
    }
Mutating an object outside of a transaction is a hard error. Doing the mutation in a transaction but failing to call save within the same transaction is a hard error too.


You make a great point about missing .save().

Mark (our team member) has advocated for a callback-based API that looks a lot like what you landed on. It has the advantage of removing an import too!

Question: how do you solve the 'draft' state issue that remolacha mentioned?


I haven’t seen a better solution than remolacha’s #2 (create separate temporary state for the form).

Forms just inherently can have partially-finished/invalid states, and it feels wrong to try and kraal model objects into carrying intermediary/invalid data for them (and in some cases won’t work at all, eg if a single form field is parsed into structured data in the model)


Exactly that. It’s tempting to try to combine them - we’ve all been there. They’re subtly but inherently different, in my experience.


A thenable API from save() could remove the need of a explicit tx management which is a worse devEx


I’m not quite seeing what you mean. What you mind redoing the example above for my benefit?

We have controllers that all the users actions are funnelled through. The top level functions in there are wrapped in transactions so in practice it’s not something you manually wrangle.


I was thinking about something like:

  author.save()
 .then(() => {
    const article = new Article(db);
    article.name = "New name";
    article.author = author;
    return article.save(); // could be used to chain the next save
 })
 .then(() => {
    console.log("Both author and article saved successfully.");
 })
 .catch((error) => {
    console.error("rollback, no changes");
 });

but I confess I might have said that without having the same understanding of the problem as you so might be nonsense. it just happen that I decided to implement transactions this way in a side project of mine.


Gotcha, thanks for the clarification. I’m not sure what that would buy me here.

I have a rule that I only async if it’s a requirement. In my case I can carry out all the steps in a single (simple) sync action. Our updates are optimistic so we update all the models immediately and mobx reflects that in the react components on the next frame.

The network request for the mutation is the only thing that’s running async. If that fails we crash the app immediately rather than trying to rollback bits in the frontend. I know that approach isn’t for everyone but it works well for us.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: