Fork me on GitHub
#clojure-austin
<
2023-09-26
>
Matthew DiLoreto23:09:25

> https://cjohansen.no/stateless-data-driven-uis/ This sounds great in theory, and I used to drink this particular flavor of Kool-aid, but I have actually come to dislike it over the years. These are essentially the same patterns you see in React/Redux apps. I find that problems arise apps due to the implicit complexity of keeping 2 separate stores: one server-side database, and a second client-side in-memory atom/redux store. The client-side store is usually a simple projection of the server-side database, and that's where complexity can creep in. For example, you fetch a list of objects from the server and store them in the client-side store. You hook up a bunch of UI components to listen to that slice of the store, then have a button that you expect to influence that collection (think about a remove or add button). Now you want to issue a request to the server to mutate the collection, but you also need to mutate the client-side store to reflect the same change, so you essentially reimplement the backend functionality (remove/add are simple, but often the effects are more complex) in the frontend. If the backend mutations return errors, you also have to handle those and make sure the frontend store matches the state of the backend given that specific error (maybe the state changed, maybe it didn't. Maybe some other part of the state changed). As applications grow, I tend to see these frontend stores explode in size and complexity. The problem gets worse at even greater application size, because you will often have a new feature that also needs to listen to some part of the application state, but due to decisions in the implementation of 1 part of the UI, or programmer ignorance, a second "view" of the same backend data is used. E.g. there was an original users array in the store, but that powers some legacy page/feature, and whenever the value changes some effect is triggered that I don't actually want anymore, so now I need newUsers in the store. Both users and newUsers represent the same type User[], and are ostensibly meant to reflect the same data, but they do not. This also applies to any derived data, which is even more common (e.g. there are users in the store, but someone else needs userAddresses. Both should always be in sync, but fetching the addresses is a separate step. It's so easy to get users and userAddresses out of sync, and any mutations of users should also affect userAddresses. You eventually see that you are reimplementing your entire database on the client). These days I prefer to forego the frontend store approach entirely and rely on simpler caching mechanisms. In React this means using react-query or swr for REST APIs or apollo for graphQL apis. They really simplify this whole thing tremendously. In Clojure/script I don't know exactly what this would look like, but you can get really far with old-school server-side rendered apps (especially if you also consider using https://htmx.org/).

norman19:09:27

I think fulcro and apollo are very similar. While the apps you write will be structured differently, the fulcro db and the apollo cache are basically doing the same thing