That's a subtly different situation, as you've presented it here. In that case you know up-front what the set of databases you need to support are, so you can explicitly design to them. One promise of Hexagonal Architecture is that you should be able to get the benefits of being able to move underlying stores without knowing in advance the precise products that you might want to move to.
Depending on the early history of your product that might be the same; or it might not. If you know from day one that you need to support two databases rather than one, that would be enough to cause design choices that you wouldn't otherwise make.
> If you know from day one that you need to support two databases rather than one, that would be enough to cause design choices that you wouldn't otherwise make.
I disagree (strongly in favour of of DI / ports-and-adapters / hexagonal).
I don't want my tax-calculation logic to know about one database, let alone two!
Bad design:
class TaxCalculator {
PGConnection conn;
TaxResult calculate(UserId userId) {..}
}
Hypothetical straw-man "future-proof" design:
class TaxCalculator {
MagicalAbstractAnyDatabaseInterface conn;
TaxResult calculate(UserId userId) {..}
}
I think a lot of commenters are looking at this interface stuff as writing more code paths to support more possible databases, per the middle example above. But I do the work to keep the database out of the TaxCalculator.
class TaxCalculator {
UserPurchasesFetcher userPurchasesFetcher;
TaxResult calculate(UserId userId) {..}
}
which is backed by JOINS inside. And I can do this refactoring in two mutually-independent steps. I can make my Postgres class implement UserPurchasesFetcher without thinking about TaxCalculator, and vice versa.
And if it's about the data integrity that JOINs could notionally provide, I no longer believe in doing things that way. The universe doesn't begin and end within my Postgres instance. I need to be transacting across boundaries, using event sourcing, idempotency, eventual consistency and so forth.
Depending on the early history of your product that might be the same; or it might not. If you know from day one that you need to support two databases rather than one, that would be enough to cause design choices that you wouldn't otherwise make.