From 4489614eec38e8c334e33f34a2073cec945fe8d5 Mon Sep 17 00:00:00 2001 From: Krishna Tolani Date: Fri, 28 Feb 2025 01:12:34 +0530 Subject: [PATCH] Refactored README and added detailed docs in docs/ folder --- README.md | 211 +++++------------------------------ docs/channel-bus-capacity.md | 76 +++++++++++++ docs/channel-bus-key.md | 104 +++++++++++++++++ docs/contributing.md | 110 ++++++++++++++++++ docs/core-concepts.md | 52 +++++++++ docs/use-cases.md | 156 ++++++++++++++++++++++++++ 6 files changed, 525 insertions(+), 184 deletions(-) create mode 100644 docs/channel-bus-capacity.md create mode 100644 docs/channel-bus-key.md create mode 100644 docs/contributing.md create mode 100644 docs/core-concepts.md create mode 100644 docs/use-cases.md diff --git a/README.md b/README.md index b7e59d0..0f1de27 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,47 @@ -# kotlin-channel-event-bus 🔆 +# Kotlin Channel Event Bus -[![maven-central](https://img.shields.io/maven-central/v/io.github.hoc081098/channel-event-bus)](https://search.maven.org/search?q=g:io.github.hoc081098%20channel-event-bus) -[![codecov](https://codecov.io/gh/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/graph/badge.svg?token=o5JcvqkEsR)](https://codecov.io/gh/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus) -[![Build and publish snapshot](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/build.yml/badge.svg)](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/build.yml) -[![Build sample](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/sample.yml/badge.svg)](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/sample.yml) -[![Publish Release](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/publish-release.yml/badge.svg)](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/actions/workflows/publish-release.yml) -[![Kotlin version](https://img.shields.io/badge/Kotlin-2.0.20-blueviolet?logo=kotlin&logoColor=white)](https://github.com/JetBrains/kotlin/releases/tag/v2.0.20) -[![KotlinX Coroutines version](https://img.shields.io/badge/Kotlinx_Coroutines-1.9.0_RC.2-blueviolet?logo=kotlin&logoColor=white)](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/1.9.0-RC.2) -[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fhoc081098%2Fkotlin-channel-event-bus&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) -[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0) -![badge][badge-android] -![badge][badge-jvm] -![badge][badge-js] -![badge][badge-js-ir] -![badge][badge-wasm] -![badge][badge-nodejs] -![badge][badge-linux] -![badge][badge-windows] -![badge][badge-ios] -![badge][badge-mac] -![badge][badge-watchos] -![badge][badge-tvos] -![badge][badge-apple-silicon] +A lightweight and efficient event bus implementation using Kotlin Coroutines and Channels. -

- -

+## Overview -## Multi-keys, multi-producers, single-consumer event bus backed by `kotlinx.coroutines.channels.Channel`s. +This library provides a robust event bus implementation using Kotlin's Channel feature, allowing for efficient communication between different parts of your application while maintaining loose coupling. -- A Kotlin Multiplatform library that provides a simple event bus implementation using - `kotlinx.coroutines.channels.Channel`s. - This is useful for UI applications where you want to send events to communicate between - different parts / scope of your application (e.g. send results from one screen to another). +## Features -- This bus is thread-safe to be used by multiple threads. - It is safe to send events from multiple threads without any synchronization. +- Coroutine-based implementation +- Type-safe event handling +- Configurable channel capacity +- Support for multiple event types +- Thread-safe operation -- `ChannelEvent.Key` will be used to identify a bus for a specific type of events. - Each bus has a `Channel` to send events to and a `Flow` to receive events from. - -- The `Channel` is unbounded (`Channel.UNLIMITED` - default) or conflated `Channel.CONFLATED`. - The `Flow` is cold and only one collector is allowed at a time. - This makes sure all events are consumed. - -## Author: [Petrus Nguyễn Thái Học](https://github.com/hoc081098) - -Like some of my work? Could you buy me a coffee (or more likely a beer)? - -Buy Me A Coffee - -## Docs - -- `0.x release` docs: https://kotlin-multiplatform-foundation.github.io/kotlin-channel-event-bus/docs/0.x -- Snapshot docs: https://kotlin-multiplatform-foundation.github.io/kotlin-channel-event-bus/docs/latest/ - -## Installation +## Quick Start ```kotlin -allprojects { - repositories { - [...] - mavenCentral() - } -} -``` +// Example usage +val eventBus = ChannelEventBus() -```kotlin -implementation("io.github.hoc081098:channel-event-bus:0.1.0") -``` - -### Snapshot - -
- Snapshots of the development version are available in Sonatype's snapshots repository. - -- Kotlin - -```kotlin -allprojects { - repositories { - [...] - maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") - } +// Subscribe to events +eventBus.subscribe { event -> + println("Received user event: $event") } -dependencies { - implementation("io.github.hoc081098:channel-event-bus:0.1.1-SNAPSHOT") -} +// Publish events +eventBus.publish(UserEvent("user_logged_in")) ``` -- Groovy +## Documentation -```groovy -allprojects { - repositories { - [...] - maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/" } - } -} +Detailed documentation is available in the [docs/](docs/) directory: -dependencies { - implementation 'io.github.hoc081098:channel-event-bus:0.1.1-SNAPSHOT' -} -``` +- [Core Concepts](docs/core-concepts.md) +- [Channel Bus Capacity](docs/channel-bus-capacity.md) +- [Channel Bus Key](docs/channel-bus-key.md) +- [Use Cases](docs/use-cases.md) -
+## Contributing -## Basic usage - -```kotlin -// Create your event type -data class AwesomeEvent(val payload: Int) : ChannelEvent { - override val key get() = Key - - companion object Key : ChannelEventKey(AwesomeEvent::class) -} - -// Create your bus instance -val bus = ChannelEventBus() - -// Send events to the bus -bus.send(AwesomeEvent(1)) -bus.send(AwesomeEvent(2)) -bus.send(AwesomeEvent(3)) - -// Receive events from the bus -bus - .receiveAsFlow(AwesomeEvent) // or bus.receiveAsFlow(AwesomeEvent.Key) if you want to be explicit - .collect { e: AwesomeEvent -> println(e) } -``` - -## Supported targets - -- `jvm` / `android`. -- `js` (`IR`). -- `wasmJs`. -- `Darwin` targets: - - `iosArm64`, `iosX64`, `iosSimulatorArm64`. - - `watchosArm32`, `watchosArm64`, `watchosX64`, `watchosSimulatorArm64`, `watchosDeviceArm64`. - - `tvosX64`, `tvosSimulatorArm64`, `tvosArm64`. - - `macosX64`, `macosArm64`. -- `mingwX64` -- `linuxX64`, `linuxArm64`. -- `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, `androidNativeX64`. - -## Sample - -- [Android Compose sample](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/tree/master/sample/app): - an Android app using Compose UI to show how to use the library. - It has two nested navigation graphs: `Register` and `Home`. - - - In `Register`, we have 3 steps (3 screens) to allow the user to input their information, step - by - step. - - A `RegisterSharedViewModel` (bound to `Register` navigation graph scope) is used - to hold the whole state of the registration process. - It observes events from the `ChannelEventBus` and updates the state accordingly. - - - Each step screen has a `ViewModel` to hold the state of the screen and will send events to - the `ChannelEventBus`, - then the `RegisterSharedViewModel` will receive those events and update the state. - - - In the `Home` nav graph, we have 2 screens: `Home` and `Detail`. - - The `Home` screen has a `HomeViewModel` to hold the results received from the `Detail` screen. - Those result events are sent from the `Detail` screen to the `ChannelEventBus`, - and the `HomeViewModel` will receive those events and update the state. - - - The `Detail` screen will send events to the `ChannelEventBus` when the user clicks on the button. - The `HomeViewModel` will receive those events and update the state. - -https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/assets/36917223/80015232-d5b5-4fb2-a779-4e6113ddb8f8 - -## Roadmap - -- [x] Support more targets: `wasm` (depends on supported targets by `kotlinx.coroutines`) (since [0.1.0](https://github.com/Kotlin-Multiplatform-Foundation/kotlin-channel-event-bus/releases/tag/0.1.0)). -- [x] More samples. -- [ ] More docs. -- [ ] More tests. +Contributions are welcome! Please read our [Contributing Guidelines](docs/contributing.md) before submitting changes. ## License -```license - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ -``` - -[badge-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat -[badge-android-native]: http://img.shields.io/badge/support-[AndroidNative]-6EDB8D.svg?style=flat -[badge-wearos]: http://img.shields.io/badge/-wearos-8ECDA0.svg?style=flat -[badge-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat -[badge-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat -[badge-js-ir]: https://img.shields.io/badge/support-[IR]-AAC4E0.svg?style=flat -[badge-nodejs]: https://img.shields.io/badge/-nodejs-68a063.svg?style=flat -[badge-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat -[badge-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat -[badge-wasm]: https://img.shields.io/badge/-wasm-624FE8.svg?style=flat -[badge-apple-silicon]: http://img.shields.io/badge/support-[AppleSilicon]-43BBFF.svg?style=flat -[badge-ios]: http://img.shields.io/badge/-ios-CDCDCD.svg?style=flat -[badge-mac]: http://img.shields.io/badge/-macos-111111.svg?style=flat -[badge-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat -[badge-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. diff --git a/docs/channel-bus-capacity.md b/docs/channel-bus-capacity.md new file mode 100644 index 0000000..7371e0e --- /dev/null +++ b/docs/channel-bus-capacity.md @@ -0,0 +1,76 @@ +# Channel Bus Capacity + +This document explains how channel capacity works in the Kotlin Channel Event Bus and how to configure it for your needs. + +## Understanding Channel Capacity + +Channel capacity refers to the number of events that can be stored in a channel before it suspends the sender. The capacity configuration directly affects how the event bus handles backpressure and memory usage. + +## Available Capacity Modes + +### 1. Unlimited Capacity +```kotlin +val eventBus = ChannelEventBus(capacity = Channel.UNLIMITED) +``` +- No suspension on send operations +- Memory usage grows with queued events +- Best for scenarios where event loss is unacceptable +- Risk of OutOfMemoryError if consumers are slower than producers + +### 2. Buffered Capacity +```kotlin +val eventBus = ChannelEventBus(capacity = 100) +``` +- Fixed-size buffer for events +- Suspends sender when buffer is full +- Provides backpressure handling +- Good balance between memory usage and performance + +### 3. Rendezvous (Zero Capacity) +```kotlin +val eventBus = ChannelEventBus(capacity = Channel.RENDEZVOUS) +``` +- No buffer +- Direct hand-off between sender and receiver +- Strongest form of backpressure +- Best for synchronized producer-consumer scenarios + +### 4. Conflated +```kotlin +val eventBus = ChannelEventBus(capacity = Channel.CONFLATED) +``` +- Keeps only the latest event +- Never suspends on send +- Useful for "latest state" scenarios +- May drop events if consumer is slow + +## Choosing the Right Capacity + +Consider these factors when selecting channel capacity: + +1. **Memory Constraints** + - Higher capacity = More memory usage + - Lower capacity = More suspension/blocking + +2. **Event Loss Tolerance** + - Critical events: Use unlimited or buffered + - State updates: Consider conflated + - Real-time data: Use rendezvous or small buffer + +3. **Performance Requirements** + - High throughput: Use buffered with appropriate size + - Low latency: Use rendezvous or small buffer + - Latest state only: Use conflated + +4. **Producer-Consumer Patterns** + - Many-to-one: Consider larger buffers + - One-to-many: Smaller buffers might suffice + - Balanced: Use rendezvous or small buffer + +## Best Practices + +1. Start with a reasonable buffer size (e.g., 50-100) +2. Monitor memory usage and adjust accordingly +3. Consider using different capacities for different event types +4. Test under expected load conditions +5. Implement monitoring and alerting for buffer overflow situations \ No newline at end of file diff --git a/docs/channel-bus-key.md b/docs/channel-bus-key.md new file mode 100644 index 0000000..824855f --- /dev/null +++ b/docs/channel-bus-key.md @@ -0,0 +1,104 @@ +# Channel Bus Key + +This document explains the concept of channel bus keys and how they are used in the Kotlin Channel Event Bus implementation. + +## What is a Channel Bus Key? + +A channel bus key is a unique identifier used to route events to specific subscribers. It combines the event type with optional metadata to create targeted event delivery paths. + +## Key Components + +### 1. Event Type +```kotlin +interface EventBusKey { + val eventType: KClass +} +``` +- Represents the Kotlin class of the event +- Ensures type safety in event delivery +- Used for automatic subscriber type inference + +### 2. Metadata +```kotlin +data class EventBusKeyMetadata( + val tag: String? = null, + val priority: Int = 0 +) +``` +- Additional information for event routing +- Can include tags for filtering +- May contain priority information + +## Using Channel Bus Keys + +### 1. Simple Type-Based Key +```kotlin +// Define an event +data class UserLoginEvent(val userId: String) + +// Create a key +val loginKey = eventBus.key() + +// Subscribe using the key +eventBus.subscribe(loginKey) { event -> + println("User ${event.userId} logged in") +} +``` + +### 2. Tagged Keys +```kotlin +// Create a tagged key +val adminLoginKey = eventBus.key("admin") + +// Subscribe to admin-only events +eventBus.subscribe(adminLoginKey) { event -> + println("Admin ${event.userId} logged in") +} +``` + +### 3. Priority Keys +```kotlin +// Create a high-priority key +val highPriorityKey = eventBus.key( + metadata = EventBusKeyMetadata(priority = 10) +) +``` + +## Key Benefits + +1. **Type Safety** + - Compile-time type checking + - Prevents event type mismatches + - IDE support for event handling + +2. **Flexible Routing** + - Multiple subscribers per event type + - Filtered subscriptions using tags + - Priority-based event handling + +3. **Clean Architecture** + - Decoupled components + - Clear event flow paths + - Easy to test and maintain + +## Best Practices + +1. **Key Organization** + - Group related keys together + - Use consistent naming conventions + - Document key purposes and usage + +2. **Type Safety** + - Leverage Kotlin's type system + - Avoid type casting in subscribers + - Use sealed classes for event hierarchies + +3. **Performance** + - Reuse keys when possible + - Avoid creating unnecessary keys + - Clean up unused subscriptions + +4. **Testing** + - Mock keys for unit tests + - Verify event routing + - Test priority handling \ No newline at end of file diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..285f997 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,110 @@ +# Contributing Guidelines + +Thank you for considering contributing to the Kotlin Channel Event Bus project! This document provides guidelines and instructions for contributing. + +## Code of Conduct + +This project adheres to a Code of Conduct that all contributors are expected to follow. Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) before contributing. + +## How to Contribute + +### 1. Setting up the Development Environment + +1. Fork the repository +2. Clone your fork: + ```bash + git clone https://github.com/your-username/kotlin-channel-event-bus.git + ``` +3. Add the upstream remote: + ```bash + git remote add upstream https://github.com/original-owner/kotlin-channel-event-bus.git + ``` + +### 2. Making Changes + +1. Create a new branch: + ```bash + git checkout -b feature/your-feature-name + ``` +2. Make your changes +3. Write or update tests +4. Run the test suite: + ```bash + ./gradlew test + ``` +5. Format your code: + ```bash + ./gradlew ktlintFormat + ``` + +### 3. Submitting Changes + +1. Commit your changes: + ```bash + git commit -m "feat: add new feature" + ``` + Follow [Conventional Commits](https://www.conventionalcommits.org/) format. + +2. Push to your fork: + ```bash + git push origin feature/your-feature-name + ``` + +3. Create a Pull Request + +## Pull Request Guidelines + +1. **Title**: Use the format: `type: description` + - Types: feat, fix, docs, style, refactor, test, chore + +2. **Description**: + - Clearly describe the changes + - Reference any related issues + - List breaking changes if any + +3. **Checklist**: + - [ ] Tests added/updated + - [ ] Documentation updated + - [ ] Code follows project style + - [ ] All tests passing + - [ ] No breaking changes (or documented if unavoidable) + +## Code Style + +- Follow Kotlin coding conventions +- Use meaningful variable and function names +- Write clear comments and documentation +- Keep functions focused and concise +- Use appropriate visibility modifiers + +## Testing + +- Write unit tests for new features +- Update existing tests when modifying code +- Aim for high test coverage +- Include edge cases in tests + +## Documentation + +- Update README.md if needed +- Add/update documentation in docs/ +- Include code examples where helpful +- Document breaking changes + +## Review Process + +1. Maintainers will review your PR +2. Address any requested changes +3. Once approved, maintainers will merge +4. Your contribution will be part of the next release + +## Getting Help + +- Create an issue for questions +- Join our community chat +- Read the documentation +- Check existing issues and PRs + +## License + +By contributing, you agree that your contributions will be licensed under the project's Apache License 2.0. \ No newline at end of file diff --git a/docs/core-concepts.md b/docs/core-concepts.md new file mode 100644 index 0000000..71e3620 --- /dev/null +++ b/docs/core-concepts.md @@ -0,0 +1,52 @@ +# Core Concepts + +This document explains the fundamental concepts behind the Kotlin Channel Event Bus implementation. + +## Event Bus + +An event bus is a messaging pattern that enables different components of an application to communicate with each other without having direct references to one another. This promotes loose coupling and better maintainability. + +## Channels in Kotlin + +Channels in Kotlin are a concurrency primitive that enables safe communication between coroutines. They can be thought of as pipes that allow you to send and receive values between different parts of your code. + +### Key Channel Concepts + +1. **Send Operation**: Adding an event to the channel +2. **Receive Operation**: Retrieving an event from the channel +3. **Channel Capacity**: The number of events that can be stored in the channel +4. **Suspension**: Operations that wait for channel space or new events + +## Event Types + +Events are strongly typed messages that flow through the event bus. Each event type represents a specific kind of message or notification in your application. + +Example of defining an event: + +```kotlin +data class UserEvent( + val type: String, + val userId: String? = null, + val timestamp: Long = System.currentTimeMillis() +) +``` + +## Subscribers + +Subscribers are functions or coroutines that process specific types of events. They register with the event bus to receive notifications when events of interest are published. + +## Publishers + +Publishers are components that send events to the event bus. Any part of your application can publish events, and all subscribers listening for that event type will receive them. + +## Flow Control + +The event bus implements flow control mechanisms to handle scenarios such as: +- Backpressure management +- Event buffering +- Error handling +- Event prioritization + +## Thread Safety + +The Channel Event Bus is thread-safe by design, leveraging Kotlin's coroutine framework to handle concurrent operations safely and efficiently. \ No newline at end of file diff --git a/docs/use-cases.md b/docs/use-cases.md new file mode 100644 index 0000000..3e755a7 --- /dev/null +++ b/docs/use-cases.md @@ -0,0 +1,156 @@ +# Use Cases + +This document outlines common use cases and patterns for implementing the Kotlin Channel Event Bus in your applications. + +## 1. User Interface Updates + +### Real-time UI Updates +```kotlin +data class UiStateEvent(val state: UiState) + +// Subscribe to UI updates +eventBus.subscribe { event -> + updateUiComponents(event.state) +} + +// Publish UI changes +viewModel.onStateChange { + eventBus.publish(UiStateEvent(newState)) +} +``` + +## 2. Inter-Module Communication + +### Cross-Module Events +```kotlin +// Module A +data class PaymentProcessedEvent(val orderId: String, val amount: Double) + +// Module B (Order Management) +eventBus.subscribe { event -> + updateOrderStatus(event.orderId, "PAID") +} +``` + +## 3. Background Task Management + +### Long-running Operations +```kotlin +data class BackgroundTaskEvent( + val taskId: String, + val status: TaskStatus, + val progress: Float +) + +// Progress updates +eventBus.subscribe { event -> + updateProgressBar(event.taskId, event.progress) +} +``` + +## 4. Authentication and Session Management + +### User Session Events +```kotlin +sealed class SessionEvent { + data class Login(val user: User) : SessionEvent() + data class Logout(val reason: String) : SessionEvent() + object SessionExpired : SessionEvent() +} + +eventBus.subscribe { event -> + when (event) { + is SessionEvent.Login -> handleLogin(event.user) + is SessionEvent.Logout -> handleLogout(event.reason) + is SessionEvent.SessionExpired -> showSessionExpiredDialog() + } +} +``` + +## 5. Data Synchronization + +### Real-time Data Updates +```kotlin +data class DataSyncEvent( + val entity: T, + val operation: SyncOperation +) + +eventBus.subscribe> { event -> + when (event.operation) { + SyncOperation.CREATE -> createUser(event.entity) + SyncOperation.UPDATE -> updateUser(event.entity) + SyncOperation.DELETE -> deleteUser(event.entity) + } +} +``` + +## 6. Error Handling + +### Global Error Management +```kotlin +data class ErrorEvent( + val error: Throwable, + val severity: ErrorSeverity, + val context: String +) + +eventBus.subscribe { event -> + when (event.severity) { + ErrorSeverity.CRITICAL -> showErrorDialog(event.error) + ErrorSeverity.WARNING -> showToast(event.error.message) + ErrorSeverity.INFO -> logError(event.error) + } +} +``` + +## 7. Feature Flags and Configuration + +### Dynamic Configuration Updates +```kotlin +data class ConfigurationEvent( + val key: String, + val value: Any, + val source: ConfigSource +) + +eventBus.subscribe { event -> + updateFeatureFlag(event.key, event.value) +} +``` + +## 8. Analytics and Logging + +### Event Tracking +```kotlin +data class AnalyticsEvent( + val name: String, + val parameters: Map +) + +eventBus.subscribe { event -> + analyticsTracker.logEvent(event.name, event.parameters) +} +``` + +## Best Practices for Implementation + +1. **Event Design** + - Keep events immutable + - Use sealed classes for related events + - Include necessary context in events + +2. **Subscription Management** + - Clean up subscriptions when no longer needed + - Use appropriate scope for coroutines + - Handle errors in subscribers + +3. **Performance Considerations** + - Use appropriate channel capacity + - Batch events when possible + - Monitor event processing time + +4. **Testing** + - Write unit tests for event handlers + - Mock event bus in tests + - Test error scenarios \ No newline at end of file