News for hackers. Stuff that matters.
Re:Master Hexagonal Architecture in Rust by mrkeen on Tuesday September 17, 2024 @04:57AM (#41565784)
> 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) {..}
  }
Actual better design:

  class TaxCalculator {
    UserFetcher userFetcher;
    PurchasesFetcher purchasesFetcher;
    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.
Re:Master Hexagonal Architecture in Rust by yencabulator on Tuesday September 17, 2024 @04:52PM (#41572606)
That sounds like a codebase that doesn't contain a single JOIN..
Re:Master Hexagonal Architecture in Rust by imron on Tuesday September 17, 2024 @05:14PM (#41572815)
Not advocating for or against, but having worked on systems like this, the joins here would happen in the Fetchers.

That is, User is the domain object, which could be built from one or more database tables inside the Fetcher.

Re:Master Hexagonal Architecture in Rust by mrkeen on Wednesday September 18, 2024 @02:56AM (#41576626)
And that's fine.

If it's really big data I imagine I'd be in some kind of scalable no-SQL situation.

If it's not so big, Postgres will comfortably handle my access patterns even without joins.

If it's in the sweet spot where I need Postgres JOINS but I don't need no-SQL, well then, refactoring will be a breeze. I'll turn:

  class TaxCalculator {
    UserFetcher userFetcher;
    PurchasesFetcher purchasesFetcher;
    TaxResult calculate(UserId userId) {..}
  }
into:

  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.