This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # aws (1)
- # beginners (15)
- # boot (30)
- # cider (13)
- # cljsrn (16)
- # clojure (458)
- # clojure-dev (15)
- # clojure-france (131)
- # clojure-greece (124)
- # clojure-korea (2)
- # clojure-spec (42)
- # clojure-uk (115)
- # clojure-ukraine (1)
- # clojurescript (103)
- # component (18)
- # cryogen (1)
- # datomic (4)
- # dirac (3)
- # figwheel (1)
- # funcool (13)
- # hoplon (60)
- # luminus (2)
- # off-topic (2)
- # om (28)
- # onyx (45)
- # parinfer (28)
- # pedestal (1)
- # proton (23)
- # re-frame (18)
- # reagent (36)
- # ring (1)
- # ring-swagger (5)
- # untangled (13)
- # vim (9)
Quick question on "Boundaries" as used in
duct, how exactly do you wire them up?
I have a
users table in a db, and a
db component (the duct hikaricp component), and so I should make a
UsersDatabase boundary, with a protocol implementing HikariCP, right? But, if I understand, the Boundary is not a
Now, say I want to
list-all users, that db access code lives in the boundary, but then how do I use that
list-all fn in, say, a
login endpoint? Is UsersDatabase a dependency injected by component? Or just something I req in the namespace?
I don't use Duct, but I can try to guess what the documentation means based on the "normal" use of component. Your components are records that hold state. The hikaricp component holds the db connection pool. A function that lists all users should take the db component as its first parameter. That's all you really need to do.
Then if/when you want to mock the database functions for testing higher-layer functionality, you can create a UsersDatabase protocol (and call it a Boundary if you like). Then all higher-level code should include and use the protocol functions only. This allows you to create a mock db component and implement the UsersDatabase protocol for it. After that, just swapping the record changes the functionality in your system.
On the "injection": the db component is a dependency of the handler component, so the db component can be taken from the handler component by any function which takes the handler as a parameter. The reason for having the dependencies in a tree structure is that you don't have to (and should not) pass the full system around all namespaces and will have a good understanding of your dependencies.
It may help you not to think of the component library as a dependency injection framework, but as a way to conveniently organize your parameters in the correct way for functional programming.
Look at the parameter for print-users. It is not convenient to write something like that when you have more dependencies. component creates and maintains that structure for you so you can give it to the top-level function. That's all it does.
@sandqvist That clears up a bit. But not all the way for me. So, if you want to create a new boundary, eg
duct generates a file with a
(defprotocol UserDatabase ...) where I would stick the API description, such as
Then, it generates code to
(extend-protocol UserDatabase duct.component.hikaricp.HikariCP ...) where I could implement
Do I need to inject
UserDatabase as a component? Or how do I use that in, for ex, and endpoint that wants to access the UserDb?
No, just require UserDatabase only. Then you can say
ah ok, I'd tried that with the error:
but I think that's because
No implementation of method: :list-all of protocol: #'trackit.boundary.user-database/UserDatabase found for class: com.zaxxer.hikari.HikariDataSource
com.zaxxer.hikari...isn't the same as the
duct.component...I'm extending, but I missed that when I first saw the error, and I need to watch what I call
Yes, the protocol must be implemented for the type(s) of the first parameter of the functions. I mostly use the components' types.
The other option is that the handler ns functions extract the com.zaxxer.hikariCP instance from the connection pool component, but it breaks the abstraction.
ya, i think component routes in a
com.zaxxer.hikariCP instead of a component, i'm trying to figure out what's going on...
If you are using the duct components, it may be that a function in your handler ns is looking inside the cp component to get the pool itself.
k i think i solved it, i was just destructuring early, and if i destructure less, i get what i wanted 🙂
No problem, I like to help people get started with component. It would have saved me a lot of work if someone had explained this stuff to me when I started learning Clojure.