1

I'm working on a Scala project that uses DynamoDB for persistence, and does this by modelling the records as case classes.

This is becoming increasingly more relational, which means we have classes like this:

UserRecord(
    id="1",
    name="Matt",
    friendIds=Seq("2", "3")
)

which are then converted into a business object by making some extra database calls, to produce something that looks like this:

User(
    id="1",
    name="Matt",
    friends=Seq(
       User(id="2", ...),
       User(id="3", ...)
    )
)

The rest of the code then deals with the User and not the UserRecord.

This is problematic for a few reasons:

  • Not all code paths actually require the User objects in the friends list, so it's a waste of time and database capacity to do the reads every time
  • It doesn't work with recursive structures (if two users were friends then this would go on making requests forever)

One idea I had was to make relationships lazy futures, so that extra database calls are only made when they are accessed, but not sure this is idiomatic as it seems to be introducing a lot of complexity to our plain case classes that are currently very easy to use.

Is there a general approach to solving this sort of problem in functional languages, or even Scala specifically?

(related question, which has an interesting set of answers but didn't really address this problem: Why would I not need an ORM in a functional language like Scala?)

  • 3
    as far as I can tell this excellent answer applies. "That's right, let's start talking about a monad..." – gnat Jun 12 '17 at 13:55
2

FWIW, most ORMs have an ability to

  • Build a complex object tree lazily, by defining computable properties that correspond to related queries;
  • Build (reasonably) complex object trees eagerly, to save round-trips when it is known that the related data are needed.

AFAICT you can use futures as an interface in either case; it the eager case a future would instantly resolve into a prefetched related object. You may end up needing separate lookup / factory methods for lazy objects and eagerly prefetched objects.

Not everything can be prefetched in one round-trip, though most simple and outer joins can be. The unpacking logic will be different: nice table rows in the lazy case vs combined result sets that need sorting into objects in the eager case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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