An approach to deal with dependency resolution and optimistic updates in react applications

198 views Asked by At

In an architecture where objects have many complex relationships, what are some maintainable approaches to dealing with

  1. Resolving Dependencies
  2. Optimistic Updates

in react applications?

For example, given this type of schema:

```
type Foo {
  ...
  otherFooID: String,
  bars: List<Bar>
}

type Bar {
  ...
  bizID: String,
}

type Biz {
  ...
}
```

A user might want to save the following ->

firstBiz = Biz();
secondBiz = Biz();
firstFoo = Foo({bars: [Bar({biz: firstBiz})]
secondFoo = Foo({bars: [Bar({biz: secondBiz})] otherFooId: firstFooId.id})

First Problem: Choosing real ids

The first problem with above is having the correct id. i.e in order for secondFoo to save, it needs to know the actual id of firstFoo.

To solve this, we could make the tradeoff, of letting the client choose the id, using something like a uuid. I don't see anything terribly wrong this this, so we can say this can work

Second Problem: Saving in order

Even if we determine id's from the frontend, the server still needs to receive these save requests in order.

```
- save firstFoo 
// okay. now firstFoo.id is valid
- save secondFoo
// okay, it was able to resolve otherFooID to firstFoo
```

The reasoning here is that the backend must guarantee that any id that is being referenced is valid.

```
- save secondFoo
// backend throws an error otherFooId is invalid
- save firstfoo
// okay
```

I am unsure what the best way to attack this problem is

The current approaches that come to mind

  1. Have custom actions, that do the coordination via promises

    save(biz).then(_ => save(Bar).then(_ => save(firstFoo)).then(_ => save(second)

The downside here is that it is quite complex, and the number of these kinds of combinations will continue to grow

  1. Create a pending / resolve helper

    const pending = {}
    const resolve = (obj, refFn) => {
      return Promise.all(obj, refFn(obj));
    }
    const fooRefs = (foo) => {
      return foo.bars.map(bar => bar.id).concat(foo.otherFooId);
    }
    
    pending[firstFoo].id = resolve(firstFoo, fooRefs).then(_ => save(firstFoo))
    

    ```

The problem with 2. is that it can cause a bunch of errors easily, if we forget to resolve or to add to pending.

Potential Solutions

It seems like Relay or Om next can solve these issues, but i would like something less high power. Perhaps something that can work in with redux, or maybe it's some concept I am missing.

Thoughts much appreciated

1

There are 1 answers

1
Tudor Ilisoi On BEST ANSWER

I have a JS/PHP implementation of such a system My approach is to serialize records both on the client and server using a reference system

For example unsaved Foo1 has GUID eeffa3, and a second Foo references its id key as {otherFooId: '@Foo#eeffa3[id]' }

Similarily you can reference a whole object like this

Foo#eefa3:{bars['@Baz#ffg4', '@Baz#ffg5']}

Now the client-side serializer would build a tree of relations and model attributes like this

{
modelsToSave:[

  'Foo#effe3':{
    attribs:{name:'John', title:'Mr.'},
    relations:{bars:['@Bar#ffg4']}
  },
  'Bar#ffg4':{
    attribs:{id:5}
    relations:{parentFoo:'@Foo#effe3'}
  },
]
}

As you can see in this example I have described circular relations between unsaved objects in pure JSON.

The key here is to hold these "record" objects in client-side memory and never mutate their GUID

The server can figure out the order of saving by saving first records without "parent" dependencies, then records which depend on those parents

After saving, the server wil return the same reference map, but now the attribs will also include primary keys and foreign keys

JS walks the received map twice (first pass just update server-received attributes, second pass substitute record references and attribute references to real records and attributes).

So there are 2 mechanisms for referencing a record, a client-side GUID and a server-side PK

When receiving a server JSON, you match your GUID with the server primary key