{"id":16703145,"url":"https://github.com/pchalamet/fbus","last_synced_at":"2025-10-10T20:33:25.924Z","repository":{"id":47200496,"uuid":"270087387","full_name":"pchalamet/fbus","owner":"pchalamet","description":"small and lean .net service-bus","archived":false,"fork":false,"pushed_at":"2024-01-30T22:02:34.000Z","size":297,"stargazers_count":42,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-27T14:06:35.012Z","etag":null,"topics":["rabbitmq","scaleout","servicebus","sharding","testable","worker"],"latest_commit_sha":null,"homepage":"","language":"F#","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/pchalamet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2020-06-06T19:50:55.000Z","updated_at":"2024-02-08T08:27:33.000Z","dependencies_parsed_at":"2024-10-12T19:07:14.776Z","dependency_job_id":"ce3ad19e-9417-4f0a-aecb-9714d50fd4ff","html_url":"https://github.com/pchalamet/fbus","commit_stats":{"total_commits":207,"total_committers":1,"mean_commits":207.0,"dds":0.0,"last_synced_commit":"22b196171c3d7b12ef353992534b39b2ef8bf34c"},"previous_names":[],"tags_count":81,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchalamet%2Ffbus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchalamet%2Ffbus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchalamet%2Ffbus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchalamet%2Ffbus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pchalamet","download_url":"https://codeload.github.com/pchalamet/fbus/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244865859,"owners_count":20523419,"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":["rabbitmq","scaleout","servicebus","sharding","testable","worker"],"created_at":"2024-10-12T19:07:12.332Z","updated_at":"2025-10-10T20:33:25.918Z","avatar_url":"https://github.com/pchalamet.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ✨ FBus\n\n[![Build status](https://github.com/pchalamet/fbus/workflows/build/badge.svg)](https://github.com/pchalamet/fbus/actions?query=workflow%3Abuild) \n\nFBus is a lightweight service-bus implementation written in F#.\n\nIt comes with default implementation for:\n* Publish (broadcast), Send (direct) and Reply (direct)\n* Conversation follow-up using headers (ConversationId and MessageId)\n* RabbitMQ (with dead-letter support)\n* Generic Host support with dependency injection\n* System.Text.Json serialization\n* Full testing capabilities using In-Memory mode\n* Persistent queue/buffering across activation\n* Sharding\n\nFeatures that won't be implemented in FBus:\n* Sagas: coordination is a big topic by itself - technically, everything required to handle this is available (ConversationId and MessageId). This can be handled outside of a service-bus.\n\n# 📦 NuGet packages\n\nPackage | Status | Description\n--------|--------|------------\nFBus | [![Nuget](https://img.shields.io/nuget/v/FBus)](https://nuget.org/packages/FBus) | Core package\nFBus.RabbitMQ | [![Nuget](https://img.shields.io/nuget/v/FBus.RabbitMQ)](https://nuget.org/packages/FBus.RabbitMQ) | RabbitMQ transport\nFBus.RabbitMQ7 | [![Nuget](https://img.shields.io/nuget/v/FBus.RabbitMQ7)](https://nuget.org/packages/FBus.RabbitMQ7) | RabbitMQ v7 transport\nFBus.Json | [![Nuget](https://img.shields.io/nuget/v/FBus.Json)](https://nuget.org/packages/FBus.Json) | System.Text.Json serializer\nFBus.GenericHost | [![Nuget](https://img.shields.io/nuget/v/FBus.GenericHost)](https://nuget.org/packages/FBus.GenericHost) | Generic Host support\nFBus.QuickStart | [![Nuget](https://img.shields.io/nuget/v/FBus.QuickStart)](https://nuget.org/packages/FBus.QuickStart) | All FBus packages to quick start a project\n\n# Requirements\nStarting version 0.25.0, you **must** install plugin `rabbitmq_consistent_hash_exchange` if you plan to use RabbitMQ transport.\n\n# 📚 Api\n\n## Messages\nIn order to exchange messages using FBus, you have first to define messages. There are 2 types:\n* events: messages that are broadcasted (see `Publish`)\n* commands: messages that are sent to one client (see `Send` or `Reply`)\n\nIt's advised to shared same definitions for types across publishers and consumers. If you do not share types, ensure you expose types using same namespaces.\n\nTo avoid mistakes, messages are marked with a dummy interface.\n\n### Events\n```\ntype EventMessage =\n    { msg: string }\n    interface FBus.IMessageEvent\n````\n\n### Commands\n```\ntype CommandMessage =\n    { msg: string }\n    interface FBus.IMessageCommand\n```\n### Sharding\nFor sharding scenarios, messages must also implement the `IMessageKey` interface:\n```\ntype IMessageKey = \n    abstract Key: string with get\n```\n**NOTE:** the default routing key is an empty string.\n\n## Builder\nPrior using the bus, a configuration must be built:\n\nFBus.Builder | Description | Default\n-------------|-------------|--------\n`configure` | Start configuration with default parameters. |\n`withName` | Change service name. Used to identify a bus client (see `IBusInitiator.Send` and `IBusConversation.Send`) | Name based on computer name, pid and random number.\n`withShard` | Name the shard. Name must be provided to enable sharding. | None\n`withConcurrency` | Allow parallel processing. | 1 |\n`withTransport` | Transport to use. | None\n`withContainer` | Container to use | None\n`withSerializer` | Serializer to use | None\n`withConsumer` | Add message consumer | None\n`withHook` | Hook on consumer message processing | None\n`withRecovery` | Connect to dead letter for recovery only | false\n`build` | Returns a new bus instance (`FBus.IBusControl`) based on configuration | n/a\n\n**NOTE:** bus clients are ephemeral by default - this is useful if you just want to connect to the bus for spying or sending commands for eg :-) Assigning a name (see `withName`) makes the client public so no queues are deleted upon exit.\n\n## Bus\n`IBusControl` is the primary interface to control the bus:\n\nIBusControl | Description | Comments\n------------|-------------|---------\n`Start` | Start the bus. Returns `IBusInitiator` | Must be called before sending messages. Start accepts a resolve context which can be used by the container.\n`Stop` | Stop the bus. | Bus can be restarted later on.\n`Dispose` | Dispose the bus instance. | Bus can't be reused.\n\nOnce bus is started, `IBusInitiator` is available:\n\nIBusInitiator | Description\n--------------|------------\n`Publish` | Broadcast an event message to all subscribers.\n`Send` | Send a command message to given client.\n\n**NOTE:** a new conversation is started when using this interface.\n\n## Consumer\nA consumer processes incoming messages: a context is provided (`IBusConversation`) and a message.\n\n`IBusConversation` provides information to handlers and means to interact with the bus:\n\nIBusConversation | Description\n-----------------|------------\n`Sender` | Name of the client.\n`ConversationId` | Id of the conversation (identifier is flowing from initiator to subsequent consumers).\n`MessageId` | Id the this message.\n`Reply` | Provide a shortcut to reply to sender with an event.\n`Publish` | Broadcast an event message to all subscribers.\n`Send` | Send a command message to given client.\n\n**NOTE:** the current conversation is used when using this interface.\n\nThere are two kind of handlers:\n\n### Class\nImplement `IBusConsumer` interface on the class to handle synchronous message handling. Multiple implementation are allowed as of F# 5.0.\n\nUse `withConsumer` to register synchronous handlers and asynchronous handlers.\n\n```\ntype IBusConsumer\u003c't\u003e =\n    abstract Handle: IBusConversation -\u003e 't -\u003e unit\n\ntype IAsyncBusConsumer\u003c't\u003e =\n    abstract HandleAsync: IBusConversation -\u003e 't -\u003e Task\n```\n\n\n## InMemory\nFBus provides InMemory implementation for transport, serializer and activator. They only exist to help testing or to easily prototype.\n\nFBus.InMemory | Description | Comments\n--------------|-------------|---------\n`useTransport` | Register InMemory transport |\n`useSerializer` | Register marshal by reference serializer | Object is preserved and passed by reference.\n`useContainer` | Register default activator (see `System.Activator`) | Default constructor must exist.\n\n**NOTE:** InMemory serializer does leak messages. This is by design.\n\n## Testing\nFBus can work in-memory, this is especially useful when unit-testing. Prior running a test in-memory, an `FBus.Testing.Session` instance has to be created. Multiple sessions can be created, they are completely isolated.\n\nFBus.Testing.Session | Description | Comments\n---------------------|-------------|---------\n`Use` | Configure FBus for unit-testing | Configure transport, serializer and activator.\n`WaitForCompletion` | Wait for all messages to be processed | This method blocks until completion.\n`ClearCache` | Clear InMemory serializer cache | Shall not be used unless necessary.\n\n## Thread safety\nFBus is thread safe as all existing extensions in this repository.\n\n# 🛠️ Extensibility\nFollowing extension points are supported:\n* Transports: which middleware is transporting messages.\n* Serializers: how messages are exchanged on the wire.\n* Containers: where and how consumers are allocated and hosted.\n* Hooks: handlers in case of failures.\n\n## Messages\nThere are 2 types of messages:\n* events: messages that are broadcasted (see `Publish`)\n* commands: messages that are sent to one client (understand `Send`)\n\nIn order to avoid mistakes, messages are marked with a marker interface.\n\nFor events:\n```\ntype EventMessage =\n    { msg: string }\n    interface FBus.IMessageEvent\n````\n\nFor commands:\n```\ntype CommandMessage =\n    { msg: string }\n    interface FBus.IMessageCommand\n```\n\n## Transports\nTwo transports are available out of the box: RabbitMQ and InMemory. Still, it's possible to easily add new middlewares.\n\nSee `FBus.IBusTransport`.\n\n## Containers\nContainers are in charge of activating and running consumers.\n\nSee `FBus.IBusContainer`.\n\n## Serializers\nSerializers transform objects into byte streams and vis-versa without relying on native middleware capabilities.\n\nSee `FBus.IBusSerializer`.\n\n## Consumers\nConsumers can be configured at will. There is one major restriction: only one handler per type is supported. If you want several subscribers, you will have to handle delegation. Handler can support async.\n\nSee `FBus.IBusConsumer\u003c\u003e` and `FBus.IAsyncBusConsumer\u003c\u003e`.\n\n## Hooks\nAllow one to observe errors while processing messages.\n\nSee `FBus.IBusHook`.\n\nFBus.IBusHook | Description | Comments\n--------------|-------------|---------\n`OnStart` | Invoked after the bus is started - use to start additional services if required |\n`OnStop` | Invoked before the bus is stopped - use to stop additional services if required |\n`OnBeforeProcessing` | Invoked before processing a message | Must not throw - can return an IDisposable object released once processing is done.\n`OnError` | Invoked on error | Must not throw.\n\n## Available extensions\n\n### RabbitMQ (package FBus.RabbitMQ)\n\nThis package comes in 2 flavors:\n- `FBus.RabbitMQ`: support for RabbitMQ v6 client\n- `FBus.RabbitMQ7`: support for RabbitMQ v7 client\n\n**NOTE**:\nYou **must** install plugin `rabbitmq_consistent_hash_exchange` if you plan to use RabbitMQ transport.\n\nFBus.RabbitMQ | Description | Comments\n--------------|-------------|---------\n`useDefaults` | Configure RabbitMQ as transport | Endpoint is set to `amqp://guest:guest@localhost`.\n`useWith` | Configure RabbitMQ as transport with provided URI |\n\nTransport leverages exchanges (one for each message type) to distribute messages across consumers (subscribing a queue).\n\nIt supports only a simple concurrency model:\n* no concurrency at bus level for receive. This does not mean you can't have concurrency, you just have to handle it explicitely: you have to create multiple bus instances in-process and it's up to you to synchronize correctly among threads if required.\n* Sending is a thread safe operation - but locking happens behind the scene to access underlying channel/connection.\n* Automatic recovery is configured on connection.\n\nThe default implementation use following settings:\n* messages are sent as persistent\n* a consumer fetches one message at a time and ack/nack accordingly\n* message goes to dead-letter on error\n* prefetch size is 0\n* prefetch count is 10\n\n### Json (package FBus.Json)\n\nFBus.Json | Description | Comments\n----------|-------------|---------\n`useDefaults` | Configure System.Text.Json as serializer | FSharp.SystemTextJson](https://github.com/Tarmil/FSharp.SystemTextJson) is used to deal with F# types.\n`useWith` | Same as `useDefaults` but with provided configuration options |\n\n### QuickStart (package FBus.QuickStart)\n\nFBus.QuickStart | Description | Comments\n`configure` | Configure FBus with RabbitMQ, Json and In-Memory Activator. |\n\n### GenericHost\n\nFBus.GenericHost | Description | Comments\n-----------------|-------------|---------\n`AddFBus` | Inject FBus in GenericHost container | `FBus.IBusControl` and `FBus.IBusInitiator` are available in injection context. \n\n**NOTE:** consumers are scoped (see `IServiceProvider.CreateScope()`).\n\n# ⚽ Samples\n\n## In-Process console\n### Client\n```\nopen FBus\nopen FBus.Builder\n\nuse bus = FBus.QuickStart.configure() |\u003e build\n\nlet busInitiator = bus.Start()\nbusInitiator.Send \"hello from FBus !\"\n```\n\n### Server\n```\nopen FBus\nopen FBus.Builder\n\ntype MessageConsumer() =\n    interface IConsumer\u003cMessage\u003e with\n        member this.Handle context msg = \n            printfn \"Received message: %A\" msg\n\nuse bus =\n    FBus.QuickStart.configure() \n    |\u003e withConsumer\u003cMessageConsumer\u003e \n    |\u003e build\nbus.Start() |\u003e ignore\n```\n\n## Server (generic host)\n```\n...\nlet configureBus builder =\n    builder \n    |\u003e withName \"server\"\n    |\u003e withConsumer\u003cMessageConsumer\u003e\n    |\u003e Json.useDefaults\n    |\u003e RabbitMQ.useDefaults\n\nHost.CreateDefaultBuilder(argv)\n    .ConfigureServices(fun services -\u003e services.AddFBus(configureBus) |\u003e ignore)\n    .UseConsoleLifetime()\n    .Build()\n    .Run()\n```\n\n# 🏭 Build it\nA makefile is available:\n* `make [build]`: build FBus\n* `make test`: build and test FBus\n\nIf you prefer to build using your IDE, solution file is named `fbus.sln`.\n\n# 📜 Licensing\nThe project is licensed under MIT.\n\nFor more information on the license see the [license file](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchalamet%2Ffbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpchalamet%2Ffbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchalamet%2Ffbus/lists"}