integrant 2023-11-10

hi folks, I am trying out Integrant library for state management. It's quite intuitive when it comes to managing (starting/halting) servers but I am confused regarding a certain use case. I want to use integrant to instantiate and destroy an object. As usual I defined defmethods init-key and halt-key! . The problem is that I want a reference to the initialised object from init-key . Is there an idiomatic way to do this? Should I just have it available in an atom during init-key ?

(defmethod ig/init-key :my-space/my-key [_ opts]
  (let [account-id (:account-id opts)
        license-key (:license-key opts)]
    (MyObject. account-id license-key)))

;;How do I use the instantiated my object?

from where you want access myObject?

(defmethod ig/init-key :my-space/my-key [_ opts]
  (let [account-id (:account-id opts)
        license-key (:license-key opts)]
    (MyObject. account-id license-key)))

;;Function in same namespace
(defn fetch-name
  [id]
  ;;use MyObject Here)

you can keep the initiated system available in some namespace. (def system (-> config (ig/prep) (if/init))) your object will be available under some key

wouldn't that lead to a cyclic dependency? • system namespace requires myobject name space (for it to instantiate the dependencies) • My object namespace requires system name space to get MyObject

I'm not follow, sorry

Typically systems created by Integrant will have some equivalent to a main loop. Perhaps this is a server of some kind, or perhaps a scheduler. Presumably fetch-name in your example will be called directly or indirectly from this component.

For example, if you had a server:

{:my-space/server {:my-object #ig/ref :my-space/my-key}
 :my-space/my-key {:account-id ..., :license-key ...}}

Sometimes a server will have subhandlers, so you could also write it:

{:my-space/server {:handlers [#ig/ref :my-space/my-handler]}
 :my-space/my-handler {:my-object #ig/ref :my-space/my-key}
 :my-space/my-key {:account ..., :license-key ...}}

However, the point is to avoid global references, and instead make every dependency explicit, passed either via Integrant as a reference, or as a function argument.

(defn fetch-name [my-object id]
  ...)

Does that make sense?

@weavejester yes makes sense. If I understood this right, I have to pass all the dependencies top down. So I will have to define my edn in such a way that server depends on MyObject. This way I can pass MyObject from server all the way down to routes and handlers (and eventually to fetch-name ) Is that right?

Yep, exactly. There should be a clear, traceable graph of dependencies.

There are state management systems that use global variables, such as Mount, but Integrant is opinionated about making dependencies explicit.

yes understood. I can see where you are coming from. This definitely requires a shift in perspective. I was just watching your talk https://skillsmatter.com/skillscasts/9820-enter-integrant-a-micro-framework-for-data-driven-architecture-with-james-reeves and it gets clearer. Thanks a lot for making this library!

You're welcome 🙂