Transmissions
A Transmission is the fundamental unit of information in the Transmission library. All communication between components flows through these transmission types.
The Three Types
sealed interface Transmission {
interface Signal : Transmission
interface Effect : Transmission
interface Data : Transmission
}
Signal
Signals represent input from UI or external events that should trigger business logic processing.
Characteristics
- Entry point for user interactions
- Processed by TransmissionRouter
- Distributed to relevant Transformers
- Should be immutable data classes or objects
Examples
// Simple signal
data object RefreshSignal : Transmission.Signal
// Signal with data
data class SearchSignal(val query: String) : Transmission.Signal
// Sealed interface for grouped signals
sealed interface CounterSignal : Transmission.Signal {
data object Increment : CounterSignal
data object Decrement : CounterSignal
data class SetValue(val value: Int) : CounterSignal
}
From Counter Sample
sealed interface CounterSignal : Transmission.Signal {
data object Lookup : CounterSignal
}
From Components Sample
sealed interface InputSignal : Transmission.Signal {
data class InputUpdate(val value: String) : InputSignal
}
sealed interface ColorPickerSignal : Transmission.Signal {
data class ColorSelected(val color: Color) : ColorPickerSignal
}
Effect
Effects represent side effects or intermediate processing steps that can trigger additional business logic.
Characteristics
- Created from Signals or other Effects
- Can trigger further processing
- Represent intermediate states in business logic
- Can be sent between Transformers
Examples
// Logging effect
data class LogEffect(val message: String, val level: LogLevel) : Transmission.Effect
// Navigation effect
data class NavigateEffect(val destination: String) : Transmission.Effect
// Network effect
sealed interface NetworkEffect : Transmission.Effect {
data class LoadData(val url: String) : NetworkEffect
data class SaveData(val data: Any) : NetworkEffect
}
From Components Sample
sealed interface InputEffect : Transmission.Effect {
data class InputUpdate(val value: String) : InputEffect
}
sealed interface ColorPickerEffect : Transmission.Effect {
data class SelectedColorUpdate(val color: Color) : ColorPickerEffect
data class BackgroundColorUpdate(val color: Color) : ColorPickerEffect
}
Data
Data represents the final result of business logic processing, ready for consumption by UI or other components.
Characteristics
- Final output of Transformer processing
- Consumed by UI components
- Should represent complete, ready-to-use information
- Flows through the router's data stream
Examples
// Simple data
data class UserData(val id: String, val name: String) : Transmission.Data
// UI state data
data class CounterData(val count: Int, val isLoading: Boolean) : Transmission.Data
// List data
data class ItemListData(val items: List<Item>) : Transmission.Data
From Counter Sample
data class CounterData(val id: String) : Transmission.Data
From Components Sample
data class InputUiState(
val writtenText: String = "",
val backgroundColor: Color = Color.White
) : Transmission.Data
Flow Relationships
The relationship between these types follows predictable patterns:
graph TD
UI[UI Layer] --> |User Input| S[Signal]
S --> |Processing| E1[Effect]
S --> |Direct Result| D1[Data]
E1 --> |Further Processing| E2[Effect]
E1 --> |Final Result| D2[Data]
E2 --> |Chain Processing| E3[Effect]
E2 --> |Final Result| D3[Data]
D1 --> |Consumption| UI
D2 --> |Consumption| UI
D3 --> |Consumption| UI
Processing in Transformers
Here's how transmissions are processed within Transformers:
class ExampleTransformer : Transformer() {
override val handlers: Handlers = handlers {
// Handle incoming Signal
onSignal<SearchSignal> { signal ->
// Can produce Effects
publish(LogEffect("Searching for: ${signal.query}", LogLevel.INFO))
// Can produce Data directly
send(SearchResultsData(searchResults = performSearch(signal.query)))
}
// Handle incoming Effect
onEffect<LogEffect> { effect ->
// Process the effect
logger.log(effect.level, effect.message)
// Can produce other Effects
if (effect.level == LogLevel.ERROR) {
publish(NotificationEffect("Error occurred"))
}
}
}
}
Best Practices
Signal Design
- Keep signals simple and focused
- Use sealed interfaces for related signals
- Include only necessary data
- Make them immutable
// Good
sealed interface UserSignal : Transmission.Signal {
data class Login(val credentials: Credentials) : UserSignal
data object Logout : UserSignal
}
// Avoid
data class UserSignal(
val action: String, // String-based actions are error-prone
val data: Any? // Generic data is not type-safe
) : Transmission.Signal
Effect Design
- Use effects for side effects and intermediate processing
- Chain effects for complex workflows
- Keep effects focused on single responsibilities
// Good - focused effects
data class ValidateInputEffect(val input: String) : Transmission.Effect
data class SaveDataEffect(val data: UserData) : Transmission.Effect
// Better - sealed interface for related effects
sealed interface UserEffect : Transmission.Effect {
data class Validate(val input: String) : UserEffect
data class Save(val data: UserData) : UserEffect
data class Delete(val id: String) : UserEffect
}
Data Design
- Make data complete and ready for consumption
- Include all necessary information for UI
- Keep data immutable
// Good - complete UI state
data class ProfileData(
val user: User,
val isLoading: Boolean,
val error: String?
) : Transmission.Data
// Avoid - incomplete information
data class ProfileData(val userId: String) : Transmission.Data // UI needs more info