|
|
|
|
|
by didibus
1669 days ago
|
|
I'd have to respectfully disagree with the article. It seems like unnecessary abstraction that just obscure the logic, and it makes the code much harder to reason about in my opinion. There's this implicit behavior injected which may or may not be pure. Here's my suggestion instead, break out your pure and impure behavior. Don't design it so that you inject the behavior, instead seperate them into independent units and compose them at the handler. (defn get-article-id
[request]
(get-in request [:path-params :id]))
(defn make-response
[status body]
{:status status
:body body})
(defn get-article!
[data-source request]
(->> request
(get-article-id)
(db/get-article-by-id! data-source)
(make-response 200)))
This is often known as the Functional Core, Imperative Shell pattern. The functional core cannot call out to the imperative shell. |
|
* it handles http requests * it uses a jdbc data source * the result of the parsing function and the arguments of the database fetch function are coupled, * the result of the database fetch and the formation of the http response are coupled
This has implications both in testing and in application maintenance. What needs to change if you decide you want to add caching in front of the database? What if you want to pick up requests off of a Kafka stream instead of from a web server?
Like I said, there are tradeoffs. The entire premise of the article is about depending on abstractions rather than implementations. Depending on your particular context, where you want to end up on the continuum should reflect your particular context.