2

In a typical DDD layered architecture (application, domain & infrastructure layers) you'd generally declare interfaces in the domain/application layers and implement infrastructure-dependent (e.g. file system, network, third-party libs, etc.) implementations in the infrastructure layer.

For instance, a ClientRepository interface would be declared in the domain and a JpaClientRepository could be implemented in the infrastructure. Another example could be an EncryptionService interface living in the domain while the MD5EncryptionService implementation lives (more like hashing, but anyway) in the infrastructure layer.

However, I've noticed that in many DDD layered project samples, the isolation of infrastructure concerns doesn't hold for queries and that infrastructure elements such as raw SQL creep into the application layer (e.g. in command handlers).

For instance, we could look at the ForumQueryService from the IDDD samples that uses raw SQL. Same for the GetMeetingFeesQueryHandler of the Modular Monolith sample project for which even the MeetingFeesProjector lives in the application layer.

I'm trying to understand the rationale behind violating the established layering and not apply DIP for query components e.g. interfaces defined in the application layer, but implemented in the infrastructure? Should we need to swap the querying technologies we'd have to change code in the application layer rather than the infrastructure like we'd expect.

I understand the pragmatism of the approach and since query paths can't really be unit tested then interfaces would be a bit pointless as you wouldn't even have test doubles for those, nor alternate implementations, but it still "feels odd" to me to me to place infrastructure concerns in the application layer.

I'm looking for more literature, examples & opinions on the subject to clear my doubts. Thanks!

Note that I had actually answered a similar question in 2016, but still haven't found peace on the subject it seems....

For those interested, I also started this thread on Twitter.

2 Answers 2

2

The repository pattern= is an abstraction. And all abstractions leak. TCP does it when it times out rather than forcing the noisy network to be reliable. Virtual Machines do it when they lag and leave you clicking on the thing you were hovering over moments before rather than on what you're hovering over now. And The repository pattern does it when people decide it's easier to just reach past it and talk to the DB in it's native language.

Sometimes maintaining the separation is time consuming. It may be because the design isn't supporting the needed change well and it needs a redesign. It may be because the coder simply doesn't see how to use it as intended. And what harm could one little SQL string really do?

One name for doing this is technical debt. Rather than taking the time to do it properly and maintain the separation people go for the quick fix. This helps them get it out the door but bites them later when they need to make changes that would have been supported by the separation.

Some people do it because they're pragmatic. Some because they're lazy. Some because they don't know any better. Some because they just want to know if it will work and will make it pretty later.

Remaining faithful has a cost. Ignoring the architecture has a cost. Those costs are paid at different times. Mortgage your future when the rates are good and you know what your doing.

2
  • Thanks for the answer! In short what you are saying is that you'd rather remain faithful to the architecture here and have, let's say, have a ListClientQueryHandler interface in the application layer, with a JdbcListClientQueryHandler in the infrastructure rather than relaxing the rules for queries like in those examples provided?
    – plalx
    Oct 5, 2022 at 18:21
  • 1
    @plalx Better now? Oct 5, 2022 at 18:27
1

There's actually two separate styles on this:

So in a layered architecture the "business layer" depends on (i.e. uses) the "persistence layer". So it doesn't really matter where the Repositories are, since the "business" has access to all the implementations anyway. That is "obviously" a bit fishy, since the "persistence" will change according to the forces of business. So it doesn't really fit. But, you can use all the things from below "officially", I guess, including SQL, which is what you see.

In a more "hexagonal" type of design, the Repository is part of the "business layer". This is fishy too, since a repository is not part of the ubiquitous language, not part of the "domain". But also, the persistence is now officially dependent on the "business". If that changes (in its implementation details, mind you, like private data!), you'll now likely have to change the persistence.

Neither cases fit really well. It's because they are trying to organize code into a technical structure instead of its "natural" domain structure. It's a problem inherent in the layered architecture and all similar technically-oriented architectures.

2
  • Thanks for the answer! I guess you could avoid the question entirely by eliminating the layering context, but that's not what I'm looking for. A "package by feature" approach promotes mixing together all infrastructure & domain concerns, blurring the lines of what's core & what's secondary, what's pure & what's IO dependent which is arguably not ideal either. I generally favor an hybrid approach, such as a layered architecture with applied Dependency Inversion and then package by feature under such layers.
    – plalx
    Oct 5, 2022 at 18:15
  • "the persistence is now officially dependent on the business" → that's totally fine that the thing that persists depends on the thing being persisted, just not the other way around.
    – plalx
    Oct 5, 2022 at 18:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.