I recently attended a short, but very interesting, talk by Jonas Tengstrad in which he introduced Polylith at the "Kompetensbio" event in Malmö. I spoke to Jonas afterwards and he patiently tried to explain the benefits of polylith to me but I still don't quite get it I think. Below are some of my take-aways and follow-up questions that perhaps can help me shed some light on my confusion. Note that my questions are a sincere attempt for me to understand polylith, and not a criticism (since I'm just learning about polylith, the only thing I could possible criticize would be a strawman).
- It's a way to structure your code for increased re-usability, understandability and easier maintainance (the leiningen plugin helps out with this)
- It allows for de-composition of components which can later be composed into potentially new applications (systems?) using already existing building blocks with minimal plumbing code.
- The individual components doesn't have to be released and used as binaries in different systems, the changes are reflected immediately (which forces you to use a monorepo). This is very interesting.
- The structuring of the software components that polylith promotes seems like a good approach to me. However what I might fail to see is the novelty of this approach. To me, it reminds me of the hexagonal/onion architecture (HA) approach, arguably with some more emphasis on the (public) "interfaces" part. But "interfaces" to me reminds me of "ports" in HA and the components reminds me of "adapters" and the actual business logic of a polylith component reminds me of the "domain model" implementation in HA. However I like that the "base" is a distinct "artefact" of the architecture, and not just another adapter. Likewise the "system" artefact that ties things together resembles the "configuration" or "infrastructure" component i HA. Am I miss-understanding this?
- They way I typically try to structure software components is by business domains, aka bounded contexts in DDD parlance. What I like about this is that I can pick whatever architecture or architectural style I find fit for the given subdomain. My impression from polylith is that there's less emphasis on the integrity of the domain model (which you would achieve in DDD by making use of anti-corruption layers etc to really have pure self contained model for each bounded context). To me "polylith" sounds more like an holistic approach, where a "bounded context" would be assembled from already existing components and services. A potential problem I see with this approach is that due to the ease and emphasis of composing new "applications" in polylith from already existing building blocks, the subtleties of a bounded context might get lost. Take the classical example of a "customer". In polylith you might have a "customer" component and (from what I understand) it's easy to re-use this component in several "applications" (bounded contexts). But if you re-use this component as is, you might suffer since a "customer" probably has two different meanings in two different contexts (which translates to different behavior and possibly even different life-cycles). A "customer" might not even be the best name to describe the "customer" in a given bounded context. Maybe "doctor", "driver", "player" etc is the name used for "customers" by the domain experts and imho you should try to represent these as models in the code. Eric Evans, Vaughn Vernon and lots of other prominent DDD proponents goes into great lengths of describing this in their books. What would make more sense to me would be if polylith is used inside a bounded context, but from what I understand that's not the case. Am I getting this wrong?
- This might be a minor point, but I fail to see how you can have a "database" component at all? It seems to lead to a shared database approach between components which I believe to be an anti-pattern. The way I read the example app is that there's a single schema covering the concerns of multiple components and multiple bounded contexts? Again, if this is all a single bounded context it makes sense but otherwise I would argue that a bounded context should also be responsible for its own database (at least logically), data model and schema.
- When components are distributed, and deployed on different nodes, and you have a network call between components, my impression was that you should write a new "interface" for this. This sounds like a good idea, but I'd like to know which artefact/system that owns such an interface? I think that for distributed components to be truly isolated it should be possible for pick, for example, a completely different threading model (e.g. standard java threads, reactive streams, kotlin coroutines, clojure channels etc) than the rest of the system (see this video for example where Ben Christensen talks about the pitfalls of a distributed monolith). This doesn't have to be a problem for polylith if the answer is that you just create a new interface for each potential threading-model. Is that what you should do?
- My current thinking is that duplicating certain code is sometimes OK. Am I wrong in thinking this? Is there never such a case with polylith?
- One of the main insights from DDD besides bounded context is the notion of an aggregate. An aggregate is important because if defines the unit of consistency for a given set of entities. A concern I have with polylith is that aggregates might not be well-defined, and you're forced to make use of certain databases to enforce the constraints of an aggregate (ACID transactions spanning over multiple database entities for example). I get the impression, but I'm probably wrong here, that you when you compose components together (in a distributed manner) you can read and write to them arbitrarily.
Note I've only looked briefly at the docs and I've not yet watch any talks online so feel free to just point me to some references where I can read up more if my questions makes no sense at all 🙂.
Thanks for your patience 🙂