polylith

timo 2024-07-16T07:14:34.538659Z

Hi there, I am using integrant and looking at the new example you have in the repo. I've read the discussion about having the defmethod in the base and not in the interface of the component, makes sense to me. I have it the other way round and a lot of my components have a ig-defmethod in their interface. That's also what I am leveraging for using a profile to switch out mocks via profiles. +mock uses a mock component as an extra-dep and +default uses the real component as an extra-dep. With your example I miss the use of profile. Since you have an embedded-pg and another pg-component I'd assume this is exactly the kind of use case to use a profile to switch between them. Why don't you use a profile for switching between theses components in your example? And how would you leverage profiles when your defmethods are in the base and not in the components' interface?

Mark Sto 2024-07-19T13:23:34.425199Z

Hi @timok! An example author is here. Thanks for a good question! I’ll try to elaborate on it and, hopefully, address it. So, first of, why might we need/use profiles in Polylith? From my perspective, this is a way to swap a component’s implementation in the “build time”, as with the rest of the Polylith, to be able to use different implementations in different circumstances, e.g. environments (dev vs. prod), projects (legacy app vs. new one), testing context (real instance vs. mocked one), etc. For this “swapping” to work, we need to have 2+ components which are identical on the contract level (interface). We can easily have this for, say, loggers, a bit less easier for, say, web servers, and, normally, should not have it for DB-specific ops, since, well, all DBs are different in many operational aspects (transactions mgmt is the first thing that usually comes to my mind here). That said, let’s check if we do really need to use profiles in this particular example, “Polylith + integrant”? If we take a closer looks on the components you’ve mentioned, embedded-pg and pg-ops, we’ll see that they serve different purposes, following the SOC principle. The first one is “stateful” (is a part of the Integrant system) and is necessary to spin up and tear down an instance of a DB. The second one is “stateless” (a regular Polylith component) and hosts a set of methods (fns) to work with the provided DB, i.e. running queries, mutating transactions, etc. For this sake, they expose different contracts and, therefore, cannot actually be considered “swappable”. In the example we don’t need to switch btw the two, we rather need both for the system to work. Now, let me address the last bit of your question — the most tricky one — leveraging profiles to swap different “stateful” component’s implementations, when and how? In this particular example we have a “stateful” component that makes it into an Integrant system and also has a corresponding Polylith brick. Since it’s a brick, we can easily add a different component implementation nearby, if we need one. For instance, we might want to be able to run an in-memory H2 DB instead of an embedded Postgres for whatever reason, e.g. to be able to spot when the two DBs diverge in their behavior or performance characteristics. (If that’s the case, it also makes sense to rename the component to be embedded-db or just db.) That way, we should be able to, say, run our full test suite twice, with one DB first and then with another, picking different profiles, e.g. +pg and +h2. The fact that our component’s implementations sit behind the same interface (`integrant.embedded-pg.interface`) will also result in a seamless Integrant system’s component swap in the example base system. As we can see, the fact that we have the relevant Integrant multi-methods extracted into a particular base won’t prevent us from swapping “stateful” Polylith components using profiles. Actually, it may even save us from having to tinker with an Integrant system, e.g. for testing purposes. On the other hand, the proposed approach allows us to do so when we really need it.

👍 1
🙏 1
🌟 1
Mark Sto 2024-07-19T13:41:51.710049Z

P.S. Let me stress that this approach — extracting keys mm’s into bases and/or splitting one’s components the proposed way — is in no way a dogma. This is merely a distillation of my personal experience of working with Polylith/Integrant systems that I wanted to share with the community. YMMV, as is usually the case.

👍 1
timo 2024-07-22T06:47:31.825559Z

Thanks for clarification. You are right in that using profiles would work with your approach as well now that I think about it.

🤝 1