component

2016-10-26T00:29:45.000099Z

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 Component, correct? 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?

sandqvist 2016-10-26T17:33:25.000100Z

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.

sandqvist 2016-10-26T17:36:41.000101Z

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.

sandqvist 2016-10-26T17:47:19.000102Z

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.

sandqvist 2016-10-26T17:52:08.000103Z

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.

sandqvist 2016-10-26T18:01:09.000105Z

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 2016-10-26T18:17:02.000110Z

But it is a major benefit to get if you want to avoid using global vars and state.

2016-10-26T18:21:24.000111Z

@sandqvist That clears up a bit. But not all the way for me. So, if you want to create a new boundary, eg UserDatabase, duct generates a file with a (defprotocol UserDatabase ...) where I would stick the API description, such as list-all. Then, it generates code to (extend-protocol UserDatabase duct.component.hikaricp.HikariCP ...) where I could implement list-all, eg (jdbc/query ...) 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?

sandqvist 2016-10-26T18:22:53.000113Z

No, just require UserDatabase only. Then you can say (list-all hikaricp-component).

2016-10-26T18:27:46.000116Z

ah ok, I'd tried that with the error:

No implementation of method: :list-all of protocol: #'trackit.boundary.user-database/UserDatabase found for class: com.zaxxer.hikari.HikariDataSource
but I think that's because 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 list-all on

sandqvist 2016-10-26T18:29:10.000117Z

Yes, the protocol must be implemented for the type(s) of the first parameter of the functions. I mostly use the components' types.

sandqvist 2016-10-26T18:31:07.000118Z

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.

2016-10-26T18:31:58.000119Z

ya, i think component routes in a com.zaxxer.hikariCP instead of a component, i'm trying to figure out what's going on...

sandqvist 2016-10-26T18:33:12.000120Z

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.

2016-10-26T18:33:32.000121Z

k i think i solved it, i was just destructuring early, and if i destructure less, i get what i wanted 🙂

2016-10-26T18:33:39.000122Z

thanks so much for helping me solve this!

sandqvist 2016-10-26T18:34:47.000123Z

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.

2016-10-26T18:36:08.000125Z

ya, i'm increasingly catching the "vision" of component, thanks so much for passing the torch!