Fork me on GitHub

A bit of topic, but I have been thinking about how to structure some backend code and my mind keeps coming back to the re-frame model. My goal is to isolate the domain logic as pure functions (event handlers) and to put side-effects, and in particular DB I/O at the edges. Side effects that happen as consequence of handlers seem easy enough: just have the handler compute and return a map declaring the effects that should take place, just like a re-frame effectful handler. What I find harder to reason about are effects that should happen before a hander, e.g. going to the db to fetch the data needed. Should I have an event handler that does nothing but "prepare" the data by returning a db-effect? Maybe a framework like this already exists?


To illustrate with a more concrete example, say a request to GET /users/25 is made. That would trigger a [::http-routing :users 25] event. This event would then in turn return {:db [:query "select * from users where id = 25" :on-success ::db-get-user-success :on-failure ::db-get-user-failure]}, triggering the db effect. Upon completion of that effect, the corresponding event would be triggered, e.g. [::db-get-user-success {:id 25 :username "foo"}].


I’m 90% sure you’ve thought of this, and that I’m just misunderstanding the question, but might something like the way that re-frame injects coeffects through interceptors be useful here?


(just in case this is that 10% case)


We’ve been experimenting with decoupling domain logic from db logic in our pedestal app using protocols and DI library such as stuartsierra/comonent. We create a protocol for the db and domain logic layers. Implement that protocol with a defrecord. The db implementation is passed into a domain logic function which calls the db protocol function with the db implementation and any args. That implementation of the domain logic is then injected into our route handler ctx map through an interceptor. So the route handler deals with route stuff like pulling args from a request map. It passes those args into the passed domain logic implementation which performs business logic before passing the result of that into the db implementation that might do an sql transaction.


It is that 10% case, @U08BW7V1V... While I had read about them, I don't think I have ever used that re-frame feature. On first glance, it seems to me that the same goal could be achieved without the need for those additional concepts (cofx, interceptors, etc) by just chaining effect and event handlers, the interceptors could however help with shaping the event's input data, making it less dependent on the effect's representation. E.g. transforming a DTO to a domain-specific representation.


@U3XCG2GBZ I think that's what they call the "repository pattern"? I think it helps with testing as it makes the event handler technically pure, but conceptually the mutations still happen in the "middle" of it -- unless you restrict that db implementation to be read-only and only do writes as an effect. I am also concerned it might lead to coupling the domain logic to whatever representation of data the db spits out.


Your db layer would transform the data into something your domain could consume. And in such a way that replacing the db and rewriting the data access layer would not require a change to your domain logic.


@polymeris Sounds like a good idea. Some material about it in this very fresh talk

👍 32