I spent some time implementing DCI in Kotlin, and it turns out it's quite a nice language for it. Here's a simple money transfer example, using the technique I learned from this article that I shared in an earlier thread:
class Account(private var _balance: Int): MoneySource, MoneyDestination { override val balance: Int get() = _balance override fun increaseBalance(amount: Int) { _balance += amount } override fun decreaseBalance(amount: Int) { _balance -= amount } } interface MoneySource { fun decreaseBalance(amount: Int): Unit val balance: Int } interface MoneyDestination { fun increaseBalance(amount: Int): Unit val balance: Int } fun TransferMoney( source: MoneySource, destination: MoneyDestination, amount: Int ) { class DestinationAccountRole { fun MoneyDestination.deposit() { increaseBalance(amount) } } class SourceAccountRole { fun MoneySource.transferToDestination() { require (balance >= destination.balance, {"Insufficient funds"}) withdraw() with (DestinationAccountRole()) { destination.deposit() } } private fun MoneySource.withdraw() { decreaseBalance(amount) } } with (SourceAccountRole()) { source.transferToDestination() } }
There's also a simpler way to do it, without the with statements:
https://github.com/mbrowne/dci-examples/blob/main/kotlin/src/moneyTransferAltVersion/moneyTransfer.kt
But I like the version with with (even though it's a bit tedious) because it allows you to group the role methods in role classes, and the name of the role appears in the code when you're calling role methods.
And here's the Dijkstra example:
https://github.com/mbrowne/dci-examples/tree/main/kotlin/src/dijkstra
For that one I had to introduce a context class even though I didn't need a long-lived context, because there's a limitation when putting classes inside of functions in Kotlin: it only works if there are no circular dependencies between roles. (Disclaimer: after writing the initial version, I asked Gemini for some help on the Dijkstra example, to make it more idiomatic Kotlin.)
My conclusion is that Kotlin is a promising language for DCI. Another reason I'm excited about DCI in Kotlin is that it has so many compiler targets now: in addition to the JVM / Android, it compiles to native code (meaning it can also be used for iOS—and its UI framework can target iOS too), WebAssembly, or JavaScript. They re-architected the compiler a few years ago, and if anyone wanted to introduce dedicated syntax for DCI (role / context) and proper role-object contracts, that would be entirely doable.
What do you all think?
—Matt