polylith

https://polylith.gitbook.io/ and https://github.com/polyfy/polylith
sneakypeet 2021-02-09T06:13:12.037800Z

How would you do “dependency injection” For example, we currently have a simple db protocol (with fn like save, get, find). Then we have separate implementations of this protocol (in memory db and gcp datastore). Code that needs db access simply includes the db namespaces and calls the functions (similar to an interface), without caring about the underlying implementation. (aka we can swap out the implementation from the repl without having to change code). My knee jerk reaction would be to have 3 components in this case 1. A db component, exposing the interface everyone will use for db access. It will also expose a protocol for database implementation. 2. A component for in-mem db implementation, that depends on 1 for the protocol 3. A component for gcp datastore implementation, that depends on 1 for the protocol This solution is 100% biased about what we have in our current systems. Is this the correct way to approach it in the polylith structure? Or is there a better mechanism to deal with

tengstrand 2021-02-18T23:27:49.064300Z

That sounds good. If you finish the migration to Polylith in production, we could also create a page in the high-level documentation where we put you and other users of Polylith, and maybe an output from the 'info' command and some text explaining your experience with Polylith. Could be interesting for people to read.

sneakypeet 2021-02-19T04:18:27.064500Z

Yeah totally. We’ve started migrating (@allandaviesza is heading up the initial work).

tengstrand 2021-02-19T08:39:01.064700Z

Cool!

tengstrand 2021-02-09T07:04:31.037900Z

Good morning! I will try to answer your question. To put a piece of functionality into a component has two benefits: 1. The encapsulation and the exposed interface can make the code easier to use and reason about. 2. You get reuse. In your case you benefit only from 1 and in that case I would probably start by putting everything into one component instead, where each implementation of the protocol could live in its own namespace, e.g.: components database src my/top/namespace interface.clj protocol.clj in-mem-clj gcp-datastore.clj Then you would expose the interface for the database access in the interface, and put the code needed for that in the protocol namespace and the different implementations in the in-mem and gcp-datastpre namespaces. Here the implementing code will only be used by one component, so putting it in the same component makes sense to me. You can also split up in-mem and gcp-datastore in sub namespaces when you have the need. And don't be afraid of having long descriptive names, which was the reason I called it database instead of db!

sneakypeet 2021-02-09T07:20:10.038500Z

@tengstrand Thank you for replying. this also solves the problem where people can accidentally use the implementation component. I’ve been really impressed by poly tool. we are very likely to move our existing stack to it. Thanks for the hard work and sharing

tengstrand 2021-02-09T07:23:39.038700Z

That sounds cool! If you decide to migrate your existing codebase to Polylith, we would be very interested to hear about your experience, and also happy to help out.

sneakypeet 2021-02-09T07:43:00.039Z

:thumbsup: will def give feedback. We have an interesting case where we have both existing node and clj services

sneakypeet 2021-02-09T07:43:24.039200Z

(as well as a some ui stuff)

sneakypeet 2021-02-09T09:49:51.040200Z

@tengstrand is there any reason that polylith.clj.core.workspace-clj.definitions/definitions does not also include the doc string of a function?

sneakypeet 2021-02-09T09:50:38.040800Z

could be handy to auto generate documentation from the component/interfaces

tengstrand 2021-02-11T06:45:44.046300Z

There are several ways to support this (I have my ideas) and it would be interesting to hear what you think would be a good solution, so feel free to add a PR! We have other work we need to do first, but I wouldn't be surprised if we add it later because I think it could be cool to support generation of e.g. HTML based documentation.

sneakypeet 2021-02-15T06:27:50.060Z

The beauty of polylith tool is the fact that you can just get the workspace.edn and then do with that whatever you want. I’m not entirely sure if the poly tool itself should be responsible for document generation. But it would be nice if there was a command that would return all the meta data for interfaces etc. This can then be leveraged to generate documentation (Either by poly tool, or whatever external code/existing tool). I still need to dabble with the code, but my current thinking is to either 1. have an option that enriches the workspace with documentation. (probably fast, but you would need to alter some existing code) 2. have a separate function to which you can pass the workspace and it will use the workspace to go and figure out the documentation/meta data (probably slower, but can be standalone)

tengstrand 2021-02-16T15:40:02.061100Z

One option would be to include everything that a meta function call would return, e.g. (meta #'my-def-defn-or-defmacro), as a :meta attribute to all def/defn/defmacro definitions in the interface. That would include attributes like :doc, :line, :column and more. That could either be added by default, or added if passing in e.g. :meta as an argument to the 'ws' command.

sneakypeet 2021-02-17T03:50:02.061700Z

I weary of bloating the workspace data, so having it as an argument makes sense. I agree :meta is better than doc

sneakypeet 2021-02-18T08:47:51.063600Z

@tengstrand after playing around and thinking about things, I’ve come up with a slightly different solution. PR with change information here https://github.com/polyfy/polylith/pull/72

tengstrand 2021-02-18T20:07:26.063900Z

Good evening and thanks for the PR! It seems to work as expected. Due to https://github.com/polyfy/polylith/issues/66 we will soon need to make some breaking changes in the 'ws' structure anyway, so I want to investigate more in the weekend what would be the best long term solution here. See you around and don't forget to watch the Mars lander: https://mars.nasa.gov/mars2020/timeline/landing/watch-online

tengstrand 2021-02-09T10:49:35.040900Z

Just so that I understand your question. Do you ask why the definitions function has a doc string but not function ? I guess there are a lot of missing doc strings in the codebase, not just here. There is a balance how much documentation to put into the code, because it needs to be maintained. That's the reason I have mainly added it where an algorithm or similar has to be explained, or at the top/interface level. Now it's mainly added to help me, so there are room for improvements to support new developers or curious people like you.

tengstrand 2021-02-09T10:49:58.041300Z

Yes, that's true.

sneakypeet 2021-02-09T11:37:13.041500Z

I can submit a PR sometime in the future if this is something you are open to

tengstrand 2021-02-09T12:02:33.041700Z

We are looking into this already, so the PR is not required. We will announce when the decision is made, how we will proceed.

sneakypeet 2021-02-09T13:10:45.041900Z

No thats not what I’m asking. I’m suggestion, that when you call definitions on a statement, and that statement has a doc string, that you include that docstring in the return definition. example

[{:name ".."
  :type ".."
  :doc "foo bar zoo"
  ...}]
That way documentation can be generated for the components interfaces from the workspace hash-map. I also realise that the ws file might become very bloated if you just include all the doc strings. But it might be a useful feature if implemented similarly to the :loc option

tengstrand 2021-02-09T13:34:09.042100Z

Okay, now i understand, good input. You can add a PR if you want, but otherwise I will add this anyway.