-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEEDBACK] Further clarify the role of interfaces.py #11
Comments
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 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: # user/interfaces.py
from album.apis import AlbumAPI
def get_latest_albums():
AlbumAPI.get_latest() # artists/interfaces.py
from album.apis import AlbumAPI
def get_latest_albums():
AlbumAPI.get_latest() 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 # user/interfaces.py
from album.apis import AlbumAPI
def transform_albums(*, albums):
return [
{'title': f'*~-_-~{a.title}~-_-~', 'published_date': a.publish_date}
for a in albums
]
def get_latest_albums():
albums = AlbumAPI.get_latest()
return transform_albums(albums=albums) # album/interfaces.py
from album.apis import AlbumAPI
def transform_albums(*, albums):
return [
{'title': a.title, 'listens': a.listen_count, 'sales': a.sales}
for a in albums
]
def get_latest_albums():
albums = AlbumAPI.get_latest()
return transform_albums(albums=albums) What is the alternative here? We could develop these requirements inside the 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? |
@phalt Thanks for the detailed clarification. I have a follow-up question:
But what if there really isn't a new requirement? What if this Thanks again for this refreshing methodology. |
If it's not proving useful then it might be overengineering. I'd clean it up. There isn't anything wrong with anticipating future changes and then, when you get to the future, discover there isn't any. Software is fluid and changes. We'll just adapt to what makes sense for the situation currently. If we discover we need to add it, we just put it back in. This is what the "pragmatism" bit means at the start of the guide. Thanks for your comments, it's helped me challenge my assumptions! |
Thanks @phalt. Your positive attitude is quite refreshing. I wasn’t trying to challenge your assumptions per se, more like trying to pick your brain. So if you do have a general rule of thumb regarding this question, I’d be glad to hear your thoughts:
|
No probs. I'm just keen to have people interested in a thing I developed from all my hard work 😆 It sounds like the interfaces could be promoted into it's own domain, sort out like a gateway or orchestration layer. I mentioned that some domains can just have services, and their only job is to co-ordinate or abstract other domains beneath it. Perhaps that could fulfill the same requirements we have here? |
I'm a little hesitant to make a concrete suggestion as I have very little experience with DDD or DADs, but perhaps a reluctantly populated |
Sounds like a good suggestion :) |
Great. Thanks for your responsiveness. Feel free to close this :) I’m happy to contribute to the docs if you feel that should go somewhere in there but I do feel this is an edge-case, as you noted. |
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! |
Hey @RTS340 yes I have had issues like that in the past. This isn't a problem with Django API Domains per say - you can end up with them in many ways. Usually circular imports are usually due to design choices in the past create circular dependencies. I have two suggestions, one easy, one more complex:
The first way is easy but inevitably feels hacky, and that's because it is a little bit. The second way is a much better approach, but costs time and effort - which understandably we don't always have! |
Hi. 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 andselectors.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 fromDomainB
must do so strictly throughDomainBInterface
. But what about DRY?Let's assume three domains:
Artist
,Album
,User
. (just like in the example docs, it could be argued thatArtist
andAlbum
should live inside the same domain)Assume that both
Artist
andUser
will want to fetch the same info from ourAlbum
, say albums within the last seven days. Does that mean thatArtist
andUser
must both write the sameAlbum
interface?Below is some pseudo-code:
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 allDomainBInterface
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.The text was updated successfully, but these errors were encountered: