Skip to content
Bluetape4k docs1.11

Subjects and event contracts

Latest stable Based on Bluetape4k release 1.11.0

A Subject is a delivery contract, not merely a Flow adapter. Before selecting one, decide what a late collector receives and how many consumers handle each item.

Comparison of Publish, Behavior, Replay, Multicast, and UnicastWork contracts

MeaningSubjectLate collectorTypical use
new events for active subscribersPublishSubjectno historycallback events
current state plus updatesBehaviorSubjectlatest valuestate observation
bounded history plus updatesReplaySubjectconfigured historyreconnect/catch-up
coordinated fan-outMulticastSubjectno implicit replaymulti-consumer boundary
distribute work between consumersUnicastWorkSubjectqueued workworker pool

Prefer StateFlow or SharedFlow when they express the contract. Subjects are useful when explicit complete() and emitError() terminal events matter.

PublishSubject does not retain values emitted before collector registration.

coroutineScope {
val subject = PublishSubject<SensorEvent>()
val collector = launch { subject.collect(::handle) }
subject.awaitCollector()
subject.emit(SensorEvent.Started)
subject.complete()
collector.join()
}

Real adapters must also define how callback threads schedule suspending emission and who closes both registration and launched jobs. awaitCollector() fixes startup ordering; it is not an unbounded buffer.

complete() is normal termination; emitError(cause) is failed termination. Later terminal calls do not reverse the first terminal state. Collector cancellation remains cancellation and must not be converted into a Subject error.

Test three paths separately: normal completion, producer failure, and collector/parent cancellation with registry cleanup.

History is retained memory. Choose size-bound or size-and-time-bound replay from a real requirement. If only the latest state matters, BehaviorSubject or StateFlow is clearer.

UnicastWorkSubject delivers each queued item to one worker. Use Publish or Multicast semantics when every active subscriber needs the same event.

SymptomLikely causeCheck
first event missingemission beat registrationawaitCollector() or replay policy
late collector has no valuePublish used as stateBehavior/Replay/StateFlow
memory growsretained history or slow collectorbounds and queue depth
one cancelled collector affects otherscancellation/registry cleanupSubjectCancellationTest
every worker handles the same jobbroadcast confused with work-sharingUnicastWorkSubject

Next: choose child-failure semantics at scope level in Structured concurrency policies.