Hello everyone, I'm attempting to create a simple ...
# coroutines
m
Hello everyone, I'm attempting to create a simple implementation for a server-side event bus:
Copy code
@Singleton
class EventBus {
	private val events = MutableSharedFlow<Any>()

	suspend fun publish(event: Any) {
		events.emit(event)
	}

	@OptIn(DelicateCoroutinesApi::class)
	fun <T : Any> subscribe(clazz: KClass<T>, onEvent: suspend (T) -> Unit) = GlobalScope.launch {
		events.filterIsInstance(clazz).collect { event ->
			ensureActive()
			onEvent(event)
		}
	}
}
I used
GlobalScope.launch
because it is supposed to live as long as the server is up. Do you see any issues with this implementation?
z
testing:
GlobalScope
is great for making code untestable
who calls
subscribe
? That thing should be responsible for the lifecycle of the subscription. Even if that happens to be "the lifetime of the process" in production, that assumption should not be hard-coded into something like an event bus
m
@Zach Klippenstein (he/him) [MOD] do you have I'd appreciate concrete suggestions
Caller looks like this:
Copy code
@Singleton
class CreateOrderUseCase @Inject constructor(
  ...
  private val eventBus: EventBus
) : UseCase {

  init {
   eventBus.subscribe(PaymentReceivedEvent::class, ::onPaymentReceived)
  }
z
There's another smell, performing side effects in a constructor. This is gonna be untestable too for similar reasons
k
I feel like your use cases would be covered much more safety by just passing in a shared flow in place of an event bus
Your custom abstraction has no way to unsubscribe which is undoubtedly going to lead to memory leaks
g
Main issue with this implementaion is that it's own existence I think you are trying to implement anti-pattern here
1
Instead of using specific, not shared, rective flows, you downgrade solution to type-unsafe event bus, which was an anti-pattern 10 years ago at least
☝️ 3
☝🏻 1