https://kotlinlang.org logo
#arrow
Title
# arrow
s

starke

03/27/2024, 1:25 AM
Hello! I’ve watched this

Building applications with Kotlin and Arrow.kt in style

video like 3 times now and really like the functional style of the sample project. As I’m trying to adapt my project to work more like that sample, I keep running into difficulties that I think are a result of me using
kotlin-inject
to supply the dependencies. I’m starting to think that using a DI library may not be super compatible with the way that
ResourceScope
works, because I can’t really figure out a good way to provide my database dependencies as a
Resource
when using
kotlin-inject
to provide the dependency instances. Does anyone have any guidance on whether it makes sense to use something like
kotlin-inject
alongside the arrow
Resource
management stuff? Any insight would be helpful! Thanks 🙂
r

Riccardo Cardin

03/27/2024, 6:11 AM
Hey. In my experience both in Kotlin and Scala, when you approach a functional style of programming using a framework or a library like ZIO, Cats Effect, or Arrow arrow, it isn't easy to use a DI framework either. They do not integrate. Both need to have control of the object lifecycle because, through it, they guarantee some specific features. In the case of resource management, you need to instrument Arrow to understand how to obtain and release the resource. I think you should create all the stuff at the very beginning of your application. Let me know if you need some reference applications.
1
👍 1
s

simon.vergauwen

03/27/2024, 8:28 AM
Hey @starke, It's possible to mix-and-match but
ResourceScope
doesn't mix with DI since they overlap in features like @Riccardo Cardin mentioned. If you're interested in Resource, here is 2 talks of me talking about it. • https://nomisrev.github.io/grateful-shutdown-with-structured-concurrency/https://nomisrev.github.io/graceful-resource-handling-using-structured-concurrency-in-kotlin/ And an example repo, https://github.com/nomisRev/ktor-arrow-example
🙌 1
❤️ 3
lol at my typo in the url 🤣
🙏 1
🤣 1
s

starke

03/27/2024, 12:13 PM
awesome I’ll check those out - thank you both!
🎉 1
I did get everything working nicely, but now as my application is growing the manual dependency injection is getting a little unwieldy. so I would definitely be interested in seeing any reference applications you could point me to @Riccardo Cardin! maybe they solve this in a more elegant way than what I’m thinking about doing.
s

simon.vergauwen

04/11/2024, 5:56 AM
What kind-of APIs would clean-up your manual dependency injection @starke? I personally found I end up with the similar amount of boilerplate then let's say Spring.
s

starke

04/12/2024, 7:17 PM
Yeah I’m not really concerned about the amount of boilerplate. So far the manual injection is actually a lot simpler than using a framework! Just wondering if there are any common patterns or suggested ways to modularize my dependencies so it’s not all piled into one
Dependencies
file/class 🤔 I don’t have a lot of dependencies yet, but am considering doing something like this to modularize the DI a bit more.
Copy code
class Dependencies(
    val healthChecks: HealthCheckRegistry,
    val persistence: PersistenceModule,
    val services: ServicesModule,
    val externalClients: FooModule,
)

class PersistenceModule(
    val dataSource: HikariDataSource,
    val database: Database,
)

class ServicesModule(
    val fooService: EventsService,
    val barService: GroupsService,
)

class FooModule(
    val bar: Bar,
)
Maybe overkill though? I may just be stuck in a dagger-style DI mindset 😆 This will be my first project that implements DI without a framework, so I’m just looking for whatever guidance y’all may have. Thanks!
And here’s an example of what I have inside one of the module classes (
PersistenceModule
in this case)
Copy code
suspend fun ResourceScope.newPersistenceModule(
    env: Environment.DataSource,
): Dependencies.PersistenceModule {
    val hikari = newHikariDataSource(env)

    return Dependencies.PersistenceModule(
        dataSource = hikari,
        database = newDatabase(hikari)
    )
}

private suspend fun ResourceScope.newHikariDataSource(
    env: Environment.DataSource,
): HikariDataSource = autoCloseable {
    HikariDataSource(
        HikariConfig().apply {
            jdbcUrl = env.url
            username = env.username
            password = env.password
            driverClassName = env.driverClassName
        }
    )
}

private suspend fun ResourceScope.newDatabase(
    dataSource: DataSource,
    adapters: DatabaseTypeAdapters = DatabaseTypeAdapters(),
): Database {
    performMigrations(dataSource)

    val jdbcDriver = closeable { dataSource.asJdbcDriver() }
    return Database(
        driver = jdbcDriver,
        fooAdapter = adapters.foo,
        barAdapter = adapters.bar,
    )
}

private fun performMigrations(dataSource: DataSource) {
    val migrationFilesLocation = Paths
        .get("server", "build", "resources", "migrations")
        .toAbsolutePath().toString()

    Flyway.configure()
        .dataSource(dataSource)
        .locations("filesystem:$migrationFilesLocation")
        .validateMigrationNaming(true)
        .validateOnMigrate(true)
        .load()
        .migrate()
}
s

simon.vergauwen

04/12/2024, 7:20 PM
That is exactly what I am doing, but I am missing an example
I split it up arbitrarily however I need it
👍 1
s

starke

04/12/2024, 7:21 PM
ah good to know, thanks! yeah I haven’t found any examples online of a more complicated project that does manual DI
s

simon.vergauwen

04/12/2024, 7:21 PM
I worked with a lot, but nothing public
👍 1
s

starke

04/12/2024, 7:21 PM
your videos and ktor example repo were very helpful in getting the basics set up though
🙌 1
r

Riccardo Cardin

04/13/2024, 6:37 AM
Hey. Maybe the problem is that your application is growing too much. Did you consider to split it?
3 Views