Clojurescript equivalent of javascript's module pattern

I am just starting to work with clojurescript on the node-js side. I’m using it for building command line tools that will run on node.
Now that I have my proof of concept already setup and doing more or less what I want is time to organize the code a bit better.

On JS when I need something similar to a configured http client I usually export a single function that accepts the basic parameters and returns an object with methods bound to that parameters (normally using the revealing module pattern). Something similar to creating a new instance on OOP.
Here is a small example of how I would do this on JS:

const request = require('request')
module.exports = (user, pass, baseUrl) => {
   const client = request.defaults({baseUrl, auth: {user, pass}})

   const getSomething = (name) => client.get('resources/' + name)
   const createSomething = (name, options) =>'resources', {...})
   return { getSomething, createSomething }

However on clojurescript I can’t find a proper way of doing this. All defines are top level declarations computed at compile time, and making an structure such the one above would require to declare all my functions with a client parameter, then partially apply them and them use them on the body of the logic. This can be something like this:

(ns some-client [:require ["request" :as request]])
(defn get-something [client, name] 
    (.get client (str "resources/" name)))
(defn create-something [client, name, options] 
    (.post client (str "resources") {:name name :data: options}))

(defn make-client [usr, pass, baseUrl] 
    (let [client (.defaults request {:auth {:user usr :pass pass} :baseUrl baseUrl})]
        {:get-something (partial get-something client)
         :create-something (partial create-something client)}))

This may not look so bad, but as soon as you need to use it on another place where all the functions would require such client things start to get messy. You will need to accept the client on all the functions and if that other namespace is just a collection of functions that you will need to use on another place, you will be forced to follow the same schema of return a client creator, accept the client you depend on and make sure you pass it to every function that could need it. I can become as horrible as this:

(ns other-helper)

(defn trivial-stuff [client name bla] 
    (let [get-something (client :get-something)]
     (get-something name))) ; make things like filtering and that

(defn more-trivial-stuff [client name bla] 
    (let [get-something (client :get-something)])
    (get-something name)) ; make things like filtering and that

(defn non-trivial-stuff [client name bla]
    (trivial-stuff client name bla)
    (more-trivial-stuff client name)))

(defn more-non-trivial-stuff [client name bla]
        (trivial-stuff client name bla)
        (more-trivial-stuff client name)))

(defn compile-utils [client]
 {:more-non-trivial (partial more-non-trivial-stuff client)
  :non-trivial (partial non-trivial-stuff client)})

I can’t make any def for the clients because I will need the credentials at runtime, so I have to accept all that stuff as parameters and bind the results, To me that looks like a lot of boilerplate and repetitive code that is not maintainable at all.

Does clojurians have a better approach ? Is any style guide on this regard ?
This is the second time I approach clojurescript and it looks very appealing at first, but as soon as you start building non trivial stuff it starts to become messy.

NOTE: for the shake of simplicity I didn’t managed any js interop or used channels for async handling. I just declared js objects as normal cljs maps and took everything as it were synchornous, but including js interop and all that stuff will make things even worse.

Protocols + datatypes are really what you are looking for IMO. As for asynchrony, Clojurescript has Promise libraries just like Js.

1 Like

Seems that someone has edited my post to make it look better. Looking at the diff I can’t understand what he did, I would like to know how to properly post codde snippets, so if someone wants to explain me how, I am open to it.

@vvvvalvalval I’ll check protocols and datatypes, any tutorial/writting that you want to recommend me ? I took a look at components, but it looks like a total overkill to my more or less simple use case.
Regarding asynchrony I’m comfortable with channels for now. I am very used to promises on JS, but I want to avoid using libraries for now. Any advantage promises has over channels ? (apart from simplicity and error handling ? :smile: )

I have been investigating a bit about what @vvvvalvalval has mentioned, and in some way seems to address my problem, but I am not totally sure about it.

My first doubt would be why should I use deftype instead of defrecord ? defrecord seems to be targeted at domain-level stuff while deftype is just a way to define a new data-type, just like a vector or a map.

Reading about the topic I can see that creating a new datatype inside of certain namespace will create the types “operands” inside that namespace, so If I want to use those types in another namespace I would have to import the types and the operands, and then pass the type to the operand that I want (sorry if I’m not using the correct terms here ). I’m not sure how this will simplify my code where I will have to define a client type, and then a get and post methods and provide the instance of client to the method I want to use: (get myClient params).
Maybe the semantics will improve and probably the definitions will be less verbose. Is that what you mean ? Any real-life example? The transit library seems to do something like this, I’m not sure.

Hello @kbsant, thanks to pointing me to the library and the examples.
mount seems like a good approach for applications built by various components that require an startup logic to be executed. However, none of the examples accepts any parameter, they just magically know which values to use.
Also it seems that it creates a bit of coupling between different namespaces, which is far from ideal.

I see that other people use an atom inside the namespace an a configuration function that just swaps the atom with the new config. While this may work on my current scenario, it works as some kind of singleton, which is not good if you want the ability of using the same library with different configurations.

Maybe I can use the revealing module pattern with clojurescript, but being a lisp could become an indentation nightmare quickly, not to mention that I will loose the fancy way of calling namespaced functions and I will be forced to use let statements to extract the returned generated methods. Everything seems clumsy on a first sight.

I’m not exactly sure what your problem is, but I suspect it is due to a project structuring problem - i.e. why you using the compile-* functions? Why not just create the client as a map containing just the auth and base url data and then write functions that work on that?

Maybe if you post a link to a project on github that has this problem it might be clearer?

Here are a few projects that deal with this in different ways:

Also, just to clean your example up a bit, instead of:

(defn trivial-stuff [client name bla] 
    (let [get-something (client :get-something)]
     (get-something name))) ; make things like filtering and that

You can use destructuring:

(defn trivial-stuff [{:keys [get-something]} name bla] 
    (get-something name)