Refactoring to the Repository Pattern

Поділитися
Вставка
  • Опубліковано 3 жов 2024
  • It never ceases to amaze me how bad I am at predicting the subject of the next week’s video. This week I really did set out to benchmark Ktor (ktor.io/) and http4k ((http4k.org) for endpoints that perform IO, but it very quickly became clear that the data model from the example project wasn’t suitable. It uses Kotlin’s MutableList, but the operations on that interface are not suspend functions, and Ktor needs coroutines in order to not block on IO.
    So instead of measuring I ended up refactoring, migrating from MutableList to a repository object that is much more amenable to implementation with a database, even if in the tests we still actually hold data in an ArrayList.
    In this episode
    00:00:44 Our current model is MutableList of Customer
    00:02:09 REST is also a model
    00:02:49 Fixing the response for an empty list
    00:04:46 What is the problem with MutableList?
    00:05:40 Start by introducing a typealias for MutableList of Customer
    00:07:12 Now make the type alias an interface
    00:08:31 IntelliJ import bug
    00:10:44 We can't serialize our custom type, but we can sidestep
    00:11:58 Implement findById
    00:15:00 Implement addCustomer
    00:16:08 Implement deleteById
    00:17:09 Now make Customers not a List
    00:17:21 Lean on the compiler to find issues
    00:17:36 Distinguishing between production and test operations
    00:20:36 Some final tidying and conveniences
    00:22:47 Customers now lets us see the operations we are using
    00:23:17 There is a problem with suspend though
    00:23:58 that we'll punt into next week
    The code for this video is on a GitHub fork github.com/dmc...
    This video is in a playlist of Ktor episodes ( • Ktor ) and http4k ( • http4k )
    I get lots of questions about the test progress bar. It was written by the inimitable @dmitrykandalov. To use it install his Liveplugin (plugins.jetbra...) and then this gist gist.github.co...
    If you like this video, you’ll probably like my book Java to Kotlin, A Refactoring Guidebook (java-to-kotlin.dev). It's about far more than just the syntax differences between the languages - it shows how to upgrade your thinking to a more functional style.

КОМЕНТАРІ • 5

  • @valcron-1000
    @valcron-1000 3 місяці тому +2

    Unfortunately I don't think there is a way to abstract over suspend and non suspend methods: once you go `suspend` then everything needs to `suspend`. I worked on a codebase some time ago that dealt with this and we just decided to always make interfaces suspend since once an implementation needs it then you're screwed. I guess this is the famous "function coloring" problem.

    • @PairingWithDuncan
      @PairingWithDuncan  3 місяці тому

      I think you’re right, and I’ve heard of other codebases with the same policy. Which is a real shame, because suspend functions are definitely harder to test and I assume less efficient to call.
      I wonder if, if they had their time again, the language designers would have made suspend a context, with special compiler handling, but at least passing the hidden CoroutineContext that way.
      Is there not a refactor to propagate suspend up the call hierarchy when you find you need it?

    • @valcron-1000
      @valcron-1000 3 місяці тому +1

      @@PairingWithDuncan I don't think passing the context would be enough since suspend functions are either compiled to a state machine or to continuations (I work mostly with C# where state machines are used, not sure about Kotlin).
      In C#, once you find something `async` you either force it to be sync (losing the benefits of async in the first place), our you need to propagate the async all the way to `main`. In my experience the best thing is to just start with `async`/`suspend` from main and move on.
      In languages like Scala or Haskell you can abstract this away since you have access to "generics of generics" (aka higher kinded types), allowing you to write interfaces where the methods/functions are parametrized over some "context" (ex. `fun foo(s: String) -> m Int`, where `m` can be a sync/async context)

    • @PairingWithDuncan
      @PairingWithDuncan  3 місяці тому +3

      Kotlin compiles suspend to a state machine too, that’s the special compiler handling that would have to come with the context. As I understand it, that means that every invocation of a suspend function from a suspend function requires the extra code, which means that we pay a performance and code size price.
      Kotlin’s context receivers, (soon, irritatingly, to be context parameters) will allow us to pass the async context kind of implicitly, but given the state machine issue, that doesn’t really help to generalise the calling, as you say.

    • @garethrowlands
      @garethrowlands Місяць тому +1

      I agree Kotlin has no way to abstract over function 'coloring'. In my mind, this is a good argument for http4k over ktor. Ktor is more complicated than http4k but in most cases there's no practical benefit.