Hello Clojureverse! First time posting
I’ve been checking out at Clojure for a while by watching a bunch of Clojure talks, and am just starting to familiarize myself with the language & ecosystem & idioms.
Given that, I wouldn’t be able to use Clojure at $DAYJOB
, although I’m eager to be able to apply all the learnings/patterns/principles/idioms that still fits my current project.
Coming from the amazing Solving Problems the Clojure Way talk by Rafal Dittwald, I want to focus on organizing my application code. I know this is a very common topic (DDD, Clean architecture, Functional core imperative shell, etc) but I’m intrigued by the concepts discussed in the talk: minimize, concentrate, defer, and I believe the concepts can be applied in most languages (just differ in the comfort level as different language provides different constructs). This question might not be Clojure specific, but I was introduced to this from Clojure and want to know how Clojure devs approach this topic.
What I want to achieve:
- I want my app to consist of a preferably thin layer of impurity (state: db conn, http client, cache), perhaps in the outermost layer of the app, in my case the HTTP handlers/controller.
- I want the “core” or the biz logic to be pure, so I can easily test with very minimal mocking needed.
Issues I have:
A. How to handle dependencies for the pure “core”/biz logic?
When the biz logic needs to fetch something from DB, check some property of the returned value, and do an HTTP request with the returned value, do I pass the DB conn/HTTP client? That will require me to mock. If I pass in just the value and let the controller call the DB, then my controller have to know what data to pass to the biz logic. Even if I call the DB in the controller, I still need to do HTTP call afterwards, if I return a “result” object, then my controller have to know what to do with the result. This feels like I’m splitting the biz logic within the controller and the pure “core”.
B. How to reuse pure “core” biz logic functions?
For example, in a RSS Reader application, once a user is registered, I want to create some default “collections” to store the RSS feed items (“Saved stories”, “Unread stories”). My biz logic then would need to:
- check if user email already registered (
func IsEmailInUse(email string) bool
) - hash password (
func GeneratePasswordHash(pass string) string
) - insert user (
func InsertUser(user User) error
) - insert default collections (
func InsertDefaultCollections(userID int) error
)
each of the action above are impure as I need to interact with DB and they depend on the previous steps. Relating to problem A, if I have a pure function for each of the steps, problem A will just become harder to solve as I will need to repeatedly pass dependencies, or pass a function, or do the steps on my impure layer (controller).
Hoping I can get some advice on how to approach some of the issues, how people usually approach these things in Clojure, or just to discuss this in general!
I’m open to learning more Clojure features/libraries if the question is best answered in Clojure, after all I’m learning the language too!
Thank you!
8 posts - 3 participants