{"id":21706848,"url":"https://github.com/smyrgeorge/actor4k","last_synced_at":"2025-12-27T23:44:08.925Z","repository":{"id":209494969,"uuid":"724199398","full_name":"smyrgeorge/actor4k","owner":"smyrgeorge","description":"A small actor system written in kotlin using Coroutines.","archived":false,"fork":false,"pushed_at":"2025-04-07T08:23:51.000Z","size":3013,"stargazers_count":47,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T16:14:44.379Z","etag":null,"topics":["actor","actor-framework","actor-model","actor-system","actors","actorsystem","cluster","coroutine","coroutines","grpc","kotlin","kotlin-coroutines","raft","raft-consensus-algorithm","sharding"],"latest_commit_sha":null,"homepage":"https://smyrgeorge.github.io","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smyrgeorge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"smyrgeorge"}},"created_at":"2023-11-27T15:42:34.000Z","updated_at":"2025-04-07T08:23:52.000Z","dependencies_parsed_at":"2023-11-30T09:44:23.253Z","dependency_job_id":"7d77f7c8-0328-40e7-90ed-6123be3be13d","html_url":"https://github.com/smyrgeorge/actor4k","commit_stats":null,"previous_names":["smyrgeorge/actor4k"],"tags_count":47,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smyrgeorge%2Factor4k","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smyrgeorge%2Factor4k/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smyrgeorge%2Factor4k/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smyrgeorge%2Factor4k/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smyrgeorge","download_url":"https://codeload.github.com/smyrgeorge/actor4k/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248594190,"owners_count":21130316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor","actor-framework","actor-model","actor-system","actors","actorsystem","cluster","coroutine","coroutines","grpc","kotlin","kotlin-coroutines","raft","raft-consensus-algorithm","sharding"],"created_at":"2024-11-25T22:14:31.218Z","updated_at":"2025-12-27T23:44:08.917Z","avatar_url":"https://github.com/smyrgeorge.png","language":"Kotlin","funding_links":["https://github.com/sponsors/smyrgeorge"],"categories":[],"sub_categories":[],"readme":"# actor4k\n\n![Build](https://github.com/smyrgeorge/actor4k/actions/workflows/ci.yml/badge.svg)\n![Maven Central](https://img.shields.io/maven-central/v/io.github.smyrgeorge/actor4k)\n![GitHub License](https://img.shields.io/github/license/smyrgeorge/actor4k)\n![GitHub commit activity](https://img.shields.io/github/commit-activity/w/smyrgeorge/actor4k)\n![GitHub issues](https://img.shields.io/github/issues/smyrgeorge/actor4k)\n[![Kotlin](https://img.shields.io/badge/kotlin-2.3.0-blue.svg?logo=kotlin)](http://kotlinlang.org)\n\n![](https://img.shields.io/static/v1?label=\u0026message=Platforms\u0026color=grey)\n![](https://img.shields.io/static/v1?label=\u0026message=Jvm\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=Linux\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=macOS\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=Windows\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=iOS\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=Android\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=wasmJs\u0026color=blue)\n![](https://img.shields.io/static/v1?label=\u0026message=wasmWasi\u0026color=blue)\n\nA small actor system written in kotlin using Coroutines.\n\n📖 [Documentation](https://smyrgeorge.github.io/actor4k/)\n\n🏠 [Homepage](https://smyrgeorge.github.io/) (under construction)\n\n## Actor Model\n\nThe actor model is a design paradigm for building concurrent systems where the basic unit of computation, known as an\nactor, encapsulates its own state and behavior and interacts with others solely through asynchronous message passing.\nEach actor processes messages sequentially, which simplifies managing state changes and avoids common pitfalls like race\nconditions and deadlocks that arise with traditional multithreading approaches.\n\nThis model is particularly useful for highly concurrent, distributed, and fault-tolerant systems. Its scalability and\nresilience come from the ability to isolate errors within individual actors through supervision strategies, making it a\nfitting choice for applications such as real-time data processing, microservices architectures, and any system that\nrequires robust fault isolation and maintainability.\n\n## Supported Features\n\n### Core Features\n\n- **Actor Lifecycle Management**: Creation, activation, and graceful shutdown of actors\n- **Message Passing Patterns**:\n    - `tell` (fire-and-forget) for asynchronous communication\n    - `ask` (request-response) for synchronous communication with timeout support\n- **Actor References**: Type-safe references to actors for communication\n- **Back-Pressure Control**: Configurable mailbox capacity to handle high message volumes\n- **Error Isolation**: Errors in one actor don't affect others\n- **Stashing**: Ability to temporarily store messages for later processing\n- **Statistics Tracking**: Monitor actor performance and message processing\n\n### Actor Types\n\n- **Generic Actor**: Base actor implementation with lifecycle hooks\n- **Behavior Actor**: Actors that can change their behavior dynamically at runtime\n- **Router Actor**: Distributes messages to worker actors using various routing strategies\n    - Random routing\n    - Round-robin routing\n    - Broadcast routing\n    - First-available routing\n\n### Cluster Support\n\n- **Distributed Actor Communication**: Seamless communication between actors across multiple nodes\n- **Remote Actor References**: References to actors on remote nodes\n- **Node Management**: Configuration and coordination of cluster nodes\n- **RPC Communication**: Remote procedure calls between nodes using WebSockets\n- **Message Serialization**: Efficient serialization of messages for network transport\n\n### Multiplatform Support\n\n- **JVM**: Support for Java Virtual Machine\n- **Native**: Support for Linux, macOS, Windows\n- **Mobile**: Support for iOS and Android\n- **WebAssembly**: Support for wasmJs and wasmWasi\n\n### State of the project\n\n**Note:** This project is still under heavy development, so you might encounter some incompatibilities along the way.\n\n**What’s Next?**\n\n- **Enhance the basic actor API by adding:**\n    - Timers\n- **Expand the cluster capabilities**\n    - Node configuration loader\n    - Dynamic node management and discovery\n- **Other**\n    - Event Sourcing tooling (Persistent Actor)\n\n## Actors in Cluster\n\nCluster support in the **actor4k** provides essential capabilities for building robust, scalable, and highly available\nsystems. By enabling actors to seamlessly communicate and coordinate across multiple nodes, clustering significantly\nenhances fault tolerance, ensures workload distribution efficiency, and maintains stable performance in distributed and\nhigh-concurrency environments. This functionality is currently under active development; you can follow its progress and\ncontribute to its advancement through the [actor4k-cluster](actor4k-cluster) module.\n\n## Usage\n\n```kotlin\nimplementation(\"io.github.smyrgeorge:actor4k:x.y.z\")\n```\n\n### Start up the Actor System\n\n**actor4k** tries to be multiplatform compatible, which means there is no support for reflection.\nTherefore, we must pass the factories to the registry for each Actor class in our project (see the example below).\n\n```kotlin\n// Create the Logger Factory.\nval loggerFactory = SimpleLoggerFactory()\n// Create the Actor Registry.\nval registry = SimpleActorRegistry(loggerFactory)\n    .factoryFor(AccountActor::class) { key -\u003e\n        AccountActor(key) // You can define how an Actor is created.\n        // You can also pass other arguments to the Actor at this point like, for example,\n        // AccountActor(key, arg1, ...)\n        // This can be very helpful with dependency injection scenarios.\n    }\n\n// Start the actor system.\nActorSystem\n    .register(loggerFactory)\n    .register(registry) // You can override the registry implementation here.\n    .start()\n```\n\n### Let's define an Actor!\n\n```kotlin\nclass AccountActor(key: String) : Actor\u003cProtocol, Protocol.Response\u003e(key) {\n    override suspend fun onBeforeActivate() {\n        // Optional override.\n        log.info(\"[${address()}] onBeforeActivate\")\n    }\n\n    override suspend fun onActivate(m: Protocol) {\n        // Optional override.\n        log.info(\"[${address()}] onActivate: $m\")\n    }\n\n    override suspend fun onReceive(m: Protocol): Behavior\u003cProtocol.Response\u003e {\n        log.info(\"[${address()}] onReceive: $m\")\n        return when (m) {\n            is Protocol.Ping -\u003e Behavior.Reply(Protocol.Pong(\"Pong!\"))\n        }\n    }\n\n    override suspend fun onShutdown() {\n        // Optional override.\n        log.info(\"[${address()}] onShutdown\")\n    }\n\n    sealed interface Protocol : ActorProtocol {\n        sealed class Message\u003cR : ActorProtocol.Response\u003e : Protocol, ActorProtocol.Message\u003cR\u003e()\n        sealed class Response : ActorProtocol.Response()\n\n        data class Ping(val message: String) : Message\u003cPong\u003e()\n        data class Pong(val message: String) : Response()\n    }\n}\n```\n\nThis example shows a basic actor implementation. If you need an actor that can change its behavior dynamically at\nruntime (for implementing state machines or context-dependent processing), check out\nthe [Behavior Actor](#behavior-actor) section below.\n\nNow let's send some messages:\n\n```kotlin\n// [Create/Get] the desired actor from the registry.\nval actor: ActorRef = ActorSystem.get(AccountActor::class, \"ACC0010\")\n// [Tell] something to the actor (asynchronous operation).\nactor.tell(Protocol.Req(message = \"[tell] Hello World!\")).getOrThrow()\n// [Ask] something to the actor (synchronous operation).\nval res = actor.ask(Protocol.Req(message = \"[ask] Ping!\")).getOrThrow()\nprintln(res)\n```\n\nSee all the available examples [here](examples/src).\n\n### Actor registry\n\nThe actor registry is a central component within an actor system that is responsible for managing the lifecycle of actor\ninstances. It maintains a mapping between unique actor addresses and their corresponding instances, ensuring that each\nactor can be efficiently retrieved and managed. Additionally, the registry stores factory functions for various actor\ntypes, which are used to dynamically create new actors when requested. By handling registration, retrieval, and cleanup\nof actors in a thread-safe manner, the actor registry plays a crucial role in supporting the scalability and reliability\nof the overall system.\n\n```kotlin\n// [Create/Get] the desired actor from the registry.\nval actor: ActorRef = ActorSystem.get(AccountActor::class, \"ACC0010\")\n```\n\n### Detached actors\n\nA detached actor is an actor instance manually created by directly invoking its constructor, rather than being\nautomatically instantiated and managed by the actor registry. This means that it stands apart from the typical lifecycle\nmanagement and messaging infrastructure of the actor system until it is explicitly integrated. Detached actors are\nuseful in scenarios where you need fine-grained control over the actor's initialization or when they play a specialized\nrole that doesn't require the full orchestration provided by the system's registry mechanisms.\n\n```kotlin\n// [Create] the desired actor.\n// We also need to manually [activate] the actor.\nval detached = AccountActor(\"DETACHED\").apply { activate() }\ndetached.tell(Protocol.Req(message = \"[ask] Ping!\"))\n// This actor will never close until we call the shutdown method.\ndetached.shutdown()\n```\n\nBefore diving deeper, note that there are several quick actor (detached) builders available:\n\n- `actorOf` – Create and activate a lightweight, stateful actor with lifecycle hooks and typed protocol/responses.\n- `simpleActorOf` – Convenience wrapper over `actorOf` that always replies to with`SimpleResponse\u003cState\u003e`.\n- `routerActorOf` – Build a `RouterActor` with a routing strategy and N worker actors.\n- `simpleRouterActorOf` – Convenience wrapper over `routerActorOf` that always replies to with`SimpleResponse\u003cState\u003e`.\n\n#### ActorOf Builder (Detached)\n\nThe `actorOf` builder provides a lightweight, functional approach to creating actors without needing to define a full\nactor class. This is particularly useful for simple actors, prototyping, or when you want to avoid the\noverhead of registering actors in the actor registry.\n\n```kotlin\n// Define your protocol\nsealed interface CounterProtocol : ActorProtocol {\n    sealed class Message\u003cR : ActorProtocol.Response\u003e : CounterProtocol, ActorProtocol.Message\u003cR\u003e()\n    sealed class Response : ActorProtocol.Response()\n\n    data object GetValue : Message\u003cCurrentValue\u003e()\n    data object Increment : Message\u003cCurrentValue\u003e()\n    data object Decrement : Message\u003cCurrentValue\u003e()\n    data class CurrentValue(val value: Int) : Response()\n}\n\n// Create an actor with state\nval counter = actorOf\u003cInt, CounterProtocol, CounterProtocol.Response\u003e(initial = 0) { state, message -\u003e\n    when (message) {\n        is CounterProtocol.GetValue -\u003e Unit\n        is CounterProtocol.Increment -\u003e state.value += 1\n        is CounterProtocol.Decrement -\u003e state.value -= 1\n    }\n    Behavior.Reply(CounterProtocol.CurrentValue(state.value))\n}\n```\n\nThis approach is ideal for:\n\n- Simple stateful actors that don't require complex class hierarchies\n- Rapid prototyping and experimentation\n- Unit testing scenarios where you need lightweight actor instances\n- Cases where you want to avoid actor registry dependencies\n\n#### SimpleActorOf Builder (Detached)\n\nThe `simpleActorOf` helper builds on top of `actorOf` to make the most common case—single-state update with a simple\nresponse—concise. Instead of defining a full `ActorProtocol` with typed replies, you can model messages as\n`SimpleMessage\u003cT\u003e` and always receive a `SimpleResponse\u003cT\u003e` where `T` is your state type.\n\n```kotlin\n// Define lightweight messages\ndata object GetValue : SimpleMessage\u003cInt\u003e()\ndata object Increment : SimpleMessage\u003cInt\u003e()\ndata object Decrement : SimpleMessage\u003cInt\u003e()\n\n// Create a simple stateful actor\nval counter = simpleActorOf(initial = 0) { state, message -\u003e\n    when (message) {\n        is GetValue -\u003e state              // read-only\n        is Increment -\u003e state + 1         // mutate by returning the new state\n        is Decrement -\u003e state - 1\n        else -\u003e state\n    }\n}\n\n// Ask returns SimpleResponse\u003cInt\u003e\nval afterInc = counter.ask(Increment).getOrThrow()\nprintln(afterInc.value) // 1\n```\n\n## Actor types\n\n### Generic Actor\n\nThe generic actor serves as an abstract foundation for creating actors tailored specifically to your application's\nrequirements. Defined through the [Actor](actor4k/src/commonMain/kotlin/io/github/smyrgeorge/actor4k/actor/Actor.kt)\nabstract class, it provides a structured way to implement essential behaviors needed in concurrent environments.\nBy extending the `Actor` base class, you gain access to built-in lifecycle hooks such as:\n\n- **`onBeforeActivate`**: opens a hook prior to activation, ideal for initial configuration or asynchronous\n  preparations.\n- **`onActivate`**: a method triggered upon receiving the first initialization message, enabling state initialization\n  or custom setup logic.\n- **`onReceive`**: a central message handler, mandatory for defining an actor’s response logic.\n- **`onShutdown`**: a finalization hook useful for closing resources, saving state, or performing cleanup procedures\n  while gracefully shutting down.\n\nEach actor instance encapsulates its unique state and interacts exclusively through asynchronous message passing,\nensuring thread-safe operation and simplifying concurrency management.\n\n#### Message Stashing\n\nThe Actor system provides a message stashing mechanism that allows actors to temporarily defer processing of messages\nthat cannot be handled in the current state. This is particularly useful for implementing state-dependent message\nprocessing logic.\n\nKey features of message stashing:\n\n- **Stashing Messages**: When an actor receives a message it cannot process in its current state, it can call the\n  `stash` method to temporarily store the message in a dedicated stash queue.\n- **Unstashing Messages**: The actor can later retrieve all stashed messages using the `unstashAll()` method, which\n  moves them back to the actor's mailbox for processing.\n- **Stash Capacity**: The stash has the same capacity as the actor's mailbox, controlled by the `capacity` property.\n- **Statistics Tracking**: The actor keeps track of stashed messages through the `stashedMessages` counter in its\n  statistics.\n\nMessage stashing is particularly useful in scenarios where:\n\n- An actor needs to change its behavior before processing certain messages\n- Messages arrive in an order different from what the actor expects\n- The actor is waiting for a specific condition or resource to become available\n\n#### Back-Pressure\n\nThe [Actor](actor4k/src/commonMain/kotlin/io/github/smyrgeorge/actor4k/actor/Actor.kt) class includes a `capacity`\nproperty, which determines the maximum number of messages allowed in an actor's mailbox. By default, this is set to\nunlimited. Adjusting this property enables you to control mailbox capacity explicitly. If the mailbox reaches its\nmaximum capacity, any following attempts to send messages will suspend until space becomes available. Leveraging this\nbehavior provides an effective way to implement a back-pressure strategy within your actor-based application.\n\n### Router Actor\n\nA [Router Actor](actor4k/src/commonMain/kotlin/io/github/smyrgeorge/actor4k/actor/impl/RouterActor.kt) is an actor\ndesigned to distribute received messages among multiple worker actors according to a defined routing strategy. This\npattern simplifies concurrent and parallel message handling, effectively supporting greater scalability and throughput.\nThe RouterActor provides mechanisms for dynamic management and structured communication patterns with its worker\nactors, encapsulating common strategies useful in concurrent systems.\n\n#### Key Features and Responsibilities\n\nThe `RouterActor` extends the foundational `Actor` abstraction, managing several essential roles:\n\n- **Message Routing**: It efficiently forwards messages it receives to its registered worker actors based on a selected\n  routing strategy.\n- **Worker Actor Management**:\n    - Initiates and manages the lifecycle of worker actors, ensuring they are appropriately activated, maintained, and\n      gracefully shut down.\n\n#### Supported Routing Strategies:\n\nThe Router Actor provides four routing strategies:\n\n- **RANDOM**: Chooses a random worker to deliver the message.\n- **BROADCAST**: Delivers the message to all workers. Note that the `ask` operation cannot be used with this strategy.\n- **ROUND_ROBIN**: Sends the message to workers in a circular order, balancing the load evenly.\n- **FIRST_AVAILABLE**: Routes the message to the first worker that becomes available. This strategy requires\n  workers to register their availability with the RouterActor.\n\n#### Worker and Protocol Classes\n\nThe RouterActor implementation includes:\n\n- **Worker**: An abstract class that extends Actor and processes messages of specific request and response types.\n  Workers have an internal mechanism to signal their availability, which is particularly important for the\n  FIRST_AVAILABLE routing strategy.\n- **Protocol**: An abstract class that defines the communication structure for messages and responses within the\n  actor-based messaging system.\n\nCheck an example [here](examples/src/jvmMain/kotlin/io/github/smyrgeorge/actor4k/examples/RouterActorMain.kt).\n\n### Behavior Actor\n\nA [Behavior Actor](actor4k/src/commonMain/kotlin/io/github/smyrgeorge/actor4k/actor/impl/BehaviorActor.kt) is an actor\nthat can change its behavior dynamically based on the current state or message received. This pattern enables an actor\nto respond differently to the same message types depending on its current state, making it ideal for implementing state\nmachines or actors that need to adapt their processing logic based on previous interactions.\n\n#### Key Features and Capabilities\n\nThe `BehaviorActor` extends the foundational `Actor` abstraction, providing several powerful capabilities:\n\n- **Dynamic Behavior Changes**: It allows for changing the actor's message processing logic at runtime using the\n  `become` method, enabling state-dependent responses.\n- **Behavior Encapsulation**: Behaviors are encapsulated as functions that process messages, making it easy to define\n  and switch between different processing strategies.\n- **State Machine Implementation**: The ability to change behaviors makes it straightforward to implement state machines\n  where the actor's response depends on its current state.\n\n#### Using BehaviorActor\n\nTo use a BehaviorActor, you need to:\n\n1. **Define Behaviors**: Create functions that process messages and return appropriate responses.\n2. **Switch Behaviors**: Use the `become` method to change the actor's current behavior based on conditions or messages.\n3. **Initialize**: Set an initial behavior that the actor will use when it starts processing messages.\n\n#### Utility Methods\n\nThe BehaviorActor provides utility methods to simplify behavior management:\n\n- **`become`**: Changes the actor's current behavior to a new function.\n\n#### Example Implementation\n\nThe BehaviorActor can be used to implement actors that need to change their behavior based on their state or the\nmessages they receive. For example, an account actor might switch between normal and echo behaviors:\n\n```kotlin\nclass AccountBehaviourActor(key: String) : BehaviorActor\u003cProtocol, Protocol.Response\u003e(key) {\n\n    override suspend fun onActivate(m: Protocol) {\n        // Set the default behavior here.\n        become(normal)\n    }\n\n    sealed interface Protocol : ActorProtocol {\n        sealed class Message\u003cR : ActorProtocol.Response\u003e : Protocol, ActorProtocol.Message\u003cR\u003e()\n        sealed class Response : ActorProtocol.Response()\n\n        data class Ping(val message: String) : Message\u003cPong\u003e()\n        data class Pong(val message: String) : Response()\n        data class SwitchBehavior(val behavior: String) : Message\u003cBehaviorSwitched\u003e()\n        data class BehaviorSwitched(val message: String) : Response()\n    }\n\n    companion object {\n        private val normal: suspend (AccountBehaviourActor, Protocol) -\u003e Behavior\u003cProtocol.Response\u003e = { ctx, m -\u003e\n            ctx.log.info(\"[${ctx.address()}] normalBehavior: $m\")\n\n            when (m) {\n                is Protocol.Ping -\u003e Behavior.Reply(Protocol.Pong(\"Pong!\"))\n                is Protocol.SwitchBehavior -\u003e {\n                    ctx.become(echo)\n                    Behavior.Reply(Protocol.BehaviorSwitched(\"Switched to echo behavior\"))\n                }\n            }\n        }\n\n        private val echo: suspend (AccountBehaviourActor, Protocol) -\u003e Behavior\u003cProtocol.Response\u003e = { ctx, m -\u003e\n            ctx.log.info(\"[${ctx.address()}] echoBehavior: $m\")\n\n            when (m) {\n                is Protocol.Ping -\u003e Behavior.Reply(Protocol.Pong(\"Echo: ${m.message}\"))\n                is Protocol.SwitchBehavior -\u003e {\n                    ctx.become(normal)\n                    Behavior.Reply(Protocol.BehaviorSwitched(\"Switched to normal behavior\"))\n                }\n            }\n        }\n    }\n}\n```\n\nCheck a complete\nexample [here](examples/src/commonMain/kotlin/io/github/smyrgeorge/actor4k/examples/AccountBehaviourActor.kt).\n\n## Build\n\n```shell\n./gradlew build\n```\n\nYou can also build for specific targets.\n\n```shell\n./gradlew build -Ptargets=macosArm64,macosX64\n```\n\nTo build for all available targets, run:\n\n```shell\n./gradlew build -Ptargets=all\n```\n\n## Links and References\n\n- https://kotlinlang.org/docs/coroutines-guide.html\n- https://doc.akka.io/docs/akka/current/general/actor-systems.html\n- https://en.wikipedia.org/wiki/Actor_model\n- https://www.baeldung.com/kotlin/suspend-functions-from-java\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmyrgeorge%2Factor4k","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmyrgeorge%2Factor4k","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmyrgeorge%2Factor4k/lists"}