3 ms·
Does the throwaway line > The Redux reducer that processes the action server-side in C# is almost the same as the one in typescript working client-side ... bo
by cormacrelf 10y ago
Does the throwaway line
> The Redux reducer that processes the action server-side in C# is almost the same as the one in typescript working client-side
... bother anyone else? Surely this is the worst-of-the-worst kind of non-DRY code you can write, where you have to have two identical implementations in two different languages, but they also have to have other responsibilities? Even if you did write tests for both, you would have to make sure that the tests were testing the same things.
Having thought about this a bit since I started typing, I think your server-side redux has to be different in some fundamental ways. It won't be an in-memory representation like the client has. It will have to actually perform the DB operations. The server will care about different state than the client. The server often won't care about in-memory representations, so won't need to produce a new state upon every action. If it does, it might be in a different form - a cache that keeps (f)recent data, for instance, rather than an entire state tree that evolves.
I'd like to hear from someone with experience on this, who has written two reducers for the same dataset, perhaps in different languages. Did you find yourself with subtle re-implementation bugs? Was it actually more productive than providing a regular REST API and having your client-side Redux store persist itself?
I personally have to wrangle lots of highly structured, partly denormalised data, with a quite a few layers of SQL indirection. Moving one part of the nested data to another branch, for example, is tricky to get right, so I test it thoroughly. And then I have to deal with the fact that my requirements include undo and save buttons – there is a constant distinction between previously-persisted and newly-added objects. I could not imagine having to write this kind of code twice and keep the two implementations in sync. But here's a shot:
- The server would act like a side-effect hook à la @ngrx/effects.
- Make a redux reducer tree out of multiple grains. Each one gets dispatched all the different actions received by the root, so you can have both a `Parent` grain and a `Child` grain react to a `ChildMovedToOtherParent` action. This is only necessary for me because of an over-denormalised schema, but I can imagine other uses.
- Handling undos is tricky. On the client, it's easy, because everything's in-memory and immutable; just store previous states. On the server, it's much harder. Event Sourcing would have you just pick a point in time and rebuild from there. I can see that kinda working: instead of writing to the DB immediately, queue up the actions in-memory and execute them when the server hears a Save action, creating a point you can't rewind past.
- All of this sounds a bit unstable, however. When you queue up DB calls, you have to execute them all sequentially, even if it's just modifying the same record over and over again. The most efficient way to do this, without running a hundred useless DB calls on save, would be simply to hold all your objects in memory, using a reducer to compute their appropriate final to-be-persisted state. This is way too complicated.
Has anyone got any experience working with an architecture like this? Or is everybody that's using Redux for state management only as ambitious as a counter or a one-way data funnel and not one bit more?
- WorldMaker 10y ago«Surely this is the worst-of-the-worst kind of non-DRY code you can write, where you have to have two identical implementations in two different languages, but they also have to have other responsibilities? Even if you did write tests for both, you would have to make sure that the tests were testing the same things.» It's always been somewhat common in client-server programming since the dawn of time. Even sometimes in the same language on both sides like many classic C/C++ game models where the client and server are intentionally kept at a remove and use different codebases. In the classic game architecture paradigm, the client can't be trusted, but you also want to make sure that the client responds as quickly as possible. It's interesting to see some of those game architecture approaches returning to web development: things like "optimistic updates" in the Redux world are a very similar pattern to classic game state client/server management. Client reducers act as if the server-side action was carried out in advance of server response and server response corrects only if necessary. I don't think I can comment that deeply on the rest of your post, but maybe the above digression helps a bit?