Hey Kotlin/Spring developers. I have a basic ques...
# spring
j
Hey Kotlin/Spring developers. I have a basic question about "best practices" in Kotlin/Spring. Past Java experience and "best practice" guidance has steered me towards always creating a separate interface and implementation for every service. I understand the benefits of interfaces, but I'm wondering if they're unnecessary and just extra overhead in the Kotlin projects I'm working on. In our case, every interface only ever has a single implementation. The implementation's public signature matches the interface exactly. In unit tests, if we need mocks, Mockk (and I think most any other mocking framework) easily handles creating mocks for concrete classes and shows no performance difference between that and mocking an interface. They currently appear to add no benefits to our project, and do come with a small implementation cost. Is there a solid reason I should be leading my team to using interfaces anyway, or is this a good case where we drop them.
i
Nothing changes with kotlin, but if you don't need interfaces and never uses their benefits, just don't make your life harder and drop them 🙂 We use interfaces at some places in my current team, e.g. we prefer writing simple custom implementation (fakes) for integration tests. The reason is that mocking forces creation of new spring context in spring and it takes some time. With fakes context is the same for every test and you just reset state of object between tests. Something like that:
Copy code
interface Client {
  fun sendEvent(event: Event) // real impl sends event to external system
}

class ClientFake {
  private val _events = CopyOnWriteArrayList<Event>()
  val events: List<Event>
    get() = _events

  override fun sendTradingEvent(event: Event) {
    _events += event
  }
}
👍 1
c
I’ve kept interfaces for Spring/Kotlin, for a couple of reasons: • One never knows when you may need to provide other implementations; • Wrapping/Decorating; for example, a CachingRepository around some Repository interface; • Ease of testing (more predictable than mocking classes) • Hiding of implementations as private classes or otherwise - all the consumer sees is the interface Having said that, there are classes of smaller/utility items that I can’t be bothered to create interfaces for. ymmv.
r
Tbh I never understood the philosophy of splitting interface and implementation by default. I don’t do it in Java either.
c
it becomes more valuable in a multi-module / published-sdk context, where you want a clean “consumer” API (part of which are public interfaces) separate from your implementation (internal or private classes)
j
YAGNI don't create an interface until you have more than one implementation
đź’Ż 10
👎🏾 1
contradicting what I just said. Another reason to create interfaces might be if you find you want to test a private function directly (usually not recommended). It can help to make the function you want to test public on the impl and not exist at all on the interface. That way your test code can access it by working with the impl, while production clients of the interface won't see it.
k
as already said, interfaces makes most sense in output ports (hexagon), if you plan to have multiple implementations. (never call implementation Impl !!!1) on input side, only multimodule api case makes sense to me. but this is nothing related to java/kotlin
👍 3
c
Yours sounds like a case where you can and should drop them. Annoys me so much when I see Service/ServiceImpl everywhere. If you ever do add an alternative implementation then by all means add an interface but otherwise it's just extra code and extra code = bad.
đź’Ż 2
j
This all matches with what I was thinking. Everything we're building is straightforward microservices tied to specific domains. We don't have need of utility wrappers like a caching layer at this time and none of our code in these projects is a shared as a library for other services. YAGNI sums it up pretty well. We don't need it now, and it's not difficult to add it later if/when we need it. Thanks for the input everyone.