[FEEDBACK] Further clarify the role of interfaces.py
See original GitHub issueHi. Thanks for your hard work on this documentation.
Even as a sole-developer wanting to refactor a fairly small Django application (12-15k LOC), I find value at least in the concepts offered by the approach presented here. It has set me on a path to better understand DDD, so I’ve also watched this talk which advocates services.py
for writing to the database and selectors.py
for reading from the database.
There’s one part that I don’t quite understand yet: the interface part. A few things are puzzling me about it and I hope to get some clarifications (I’d be happy to contribute to the docs if conclude the answer may be helpful to others). I hope I can articulate clearly.
Interfaces and DRY
We are told in the docs that if one domain needs to talk to another, it must do so using an interface that it cultivates. In other words, DomainA
that wants to take some info from DomainB
must do so strictly through DomainBInterface
. But what about DRY?
Let’s assume three domains: Artist
, Album
, User
. (just like in the example docs, it could be argued that Artist
and Album
should live inside the same domain)
Assume that both Artist
and User
will want to fetch the same info from our Album
, say albums within the last seven days. Does that mean that Artist
and User
must both write the same Album
interface?
Below is some pseudo-code:
# user/interfaces.py
from album.apis import AlbumAPI
def get_latest_albums():
AlbumAPI.get_latest()
# album/interfaces.py
from album.apis import AlbumAPI
def get_latest_albums():
AlbumAPI.get_latest()
Doesn’t this produce non-DRY code?
Interfaces and refactoring
Tying with what is brought-up above, it’s still unclear to me why the fact we have interfaces.py
saves us headache when refactoring.
If a method in domain_b.apis
has to change, then we potential will have to refactor all DomainBInterface
s that were constructed in other domains (domain_a.interfaces.DomainBinterface
, domain_c.interfaces.DomainBinterface
, etc…).
Whereas if we consume on the DomainBAPI
directly, we only need to change the method in one place? I’m sure there’s something I’m missing here.
Issue Analytics
- State:
- Created 4 years ago
- Comments:10 (6 by maintainers)
Hey @SHxKM this is a really well thought out comment and I appreciate the feedback.
First off, I am assuming the second code example has a mistake and it should be
artists/interfaces.py
and notalbums/interfaces.py
. Because a domain’s interface to itself is redundant. I’ll go on the basis that this is wrong for the rest of my reply 😄So you’re right - those two examples are identical, and in this instance the are violating the DRY principle. If this was as far as we were going to take this project, that redundancy could be refactored. One of the core principles of DADs (I need a better acronym) is to make it easier for future developers to come in and expand features in place. I think perhaps the example given is not a real-world case. Let me explain.
Let’s evolve with the example you gave:
Artists
andUsers
both want to communicate withAlbums
. We create the initial state:Awesome. We’re violating DRY here, so maybe a developer will choose to not do this and refactor. That’s probably okay for that requirement and it’ll pass code review (even mine). If we didn’t go any further with this, interfaces is redundant. We don’t really need DADs.
But thi is a real-world evolving project, so we’ve suddenly got a new requirement:
Now it should become clear why two interfaces is not redundant. The needs for
Artists
andUsers
fromAlbums
is different:What is the alternative here? We could develop these requirements inside the
Albums
domain, but then that domain will start having business logic for other domains within it. From my experience too, this breeds a “your problem” culture, especially when you own Albums, I own Users, and I don’t want to have to maintain your stuff.DDD talks about responsibilities and separating concerns. With DADs, the interfaces layer is that transformation layer that allows domains to remain pure, and the interfaces between them handling any required transformations.
Does this help clear up the reason behind them?
I’ve pinned this discussion because I think it is useful for others who are keen on the guide but don’t know what to do!