Obvs Observable Microservice Bus

repository·master·Indexed 43 days ago

https://github.com/christopherread/obvs

A transport-agnostic microservice bus library for composing decoupled services using messaging and Reactive Extensions (Rx). It supports multiple transport implementations including Kafka, NATS, and NetMQ/ZeroMQ, and provides a fluent API for configuring IServiceBus with support for Commands, Events, and Request/Response patterns.

Tokens
2.3K
Snippets
8
Records
10
Agent score
56%

What's inside Obvs

  1. Define message contracts for Obvs

    master

    Obvs uses a convention-based messaging system. To organize messages, you should define a root message interface that identifies messages belonging to your specific service. You then implement specific message types (Commands, Events, Requests, Responses) by inheriting from both your service's root interface and the appropriate Obvs marker interface.

    • IMessage: The base interface for all messages.
    • ICommand: For actions that trigger a state change.
    • IEvent: For notifications of things that have happened.
    • IRequest: For synchronous-style requests.
    • IResponse: For the result of a request.
    // 1. Define a root message type for your service
    public interface IMyServiceMessage : IMessage { }
    
    // 2. Create specific message types
    public class MyCommand : IMyServiceMessage, ICommand { }
    public class MyEvent : IMyServiceMessage, IEvent { }
    public class MyRequest : IMyServiceMessage, IRequest { }
    public class MyResponse : IMyServiceMessage, IResponse { }
  2. Run Obvs examples using Docker

    master

    To run the provided examples in a containerized environment, use the following commands:

    1. Navigate to the examples directory and start the infrastructure: cd examples && docker-compose up

    2. In a separate terminal, run the client application: cd client && dotnet run -f netcoreapp3.1

    cd examples
    docker-compose up
    
    cd client
    dotnet run -f netcoreapp3.1
  3. Install Obvs.NATS via NuGet

    master

    To use NATS as a transport layer for Obvs, install the Obvs.NATS NuGet package. This package provides the necessary integration to allow Obvs services to communicate over a NATS messaging system.

    # Use the NuGet CLI to install the package
    dotnet add package Obvs.NATS
  4. Explore Obvs.NetMQ sample implementations

    master

    To understand how to implement a publisher and subscriber using the Obvs.NetMQ transport, refer to the following sample projects within the repository:

    • Publisher: Obvs.NetMQ.Tests.Console.Publisher
    • Subscriber: Obvs.NetMQ.Tests.Console.Subscriber

    Additional implementation patterns can be found within the UnitTest projects.

  5. Configure an Obvs ServiceBus using the fluent API

    master

    You can declare a new IServiceBus using the ServiceBus.Configure() fluent builder. This allows you to specify the transport (e.g., ActiveMQ), the message contract scope, naming, queue/topic usage, connection details, and serialization format.

    Key configuration methods:

    • .WithActiveMQEndpoints<T>(): Configures endpoints for messages implementing T.
    • .Named(string): Sets the service name.
    • .UsingQueueFor<T>(): Specifies that a specific message type (like ICommand) should use a queue.
    • .ConnectToBroker(string): Provides the connection string.
    • .SerializedAsJson(): Sets the serialization format.
    • .AsClientAndServer(): Configures the bus to both send and receive messages.
    • .WithEndpoints(IServiceEndpointClient): Adds custom endpoints for external integrations.
    IServiceBus serviceBus = ServiceBus.Configure()
        .WithActiveMQEndpoints<IMyServiceMessage>()
            .Named("MyService")
            .UsingQueueFor<ICommand>()
            .ConnectToBroker("tcp://localhost:61616")
            .SerializedAsJson()
            .AsClientAndServer()
        .Create();
  6. Install Obvs.NetMQ via NuGet

    master

    Obvs.NetMQ provides NetMQ/ZeroMQ transport support for the Obvs microservice bus. It is a Reactive Extensions (RX) wrapper over the ZeroMQ transport protocol, using the NetMQ library for native C# support. You can install it via the NuGet package manager.

    # Use the NuGet package manager to install Obvs.NetMQ
    dotnet add package Obvs.NetMQ
  7. Send commands and publish events

    master

    Once the IServiceBus is created, you can interact with it using Reactive Extensions (Rx) patterns. Commands and Events are handled via the .Commands and .Events properties respectively.

    • Subscribing: Use .Subscribe() on the command or event stream to react to incoming messages.
    • Sending/Publishing: Use await serviceBus.SendAsync(message) for commands and await serviceBus.PublishAsync(message) for events.
    // Handle commands
    serviceBus.Commands.Subscribe(c => Console.WriteLine("Received a command!"));
    await serviceBus.SendAsync(new MyCommand());
    
    // Handle events
    serviceBus.Events.Subscribe(e => Console.WriteLine("Received an event!"));
    await serviceBus.PublishAsync(new MyEvent());
  8. Perform Request/Response patterns

    master

    Obvs supports a request/response pattern using IRequest and IResponse.

    1. Handling a Request: Subscribe to the .Requests stream and use serviceBus.ReplyAsync(request, response) to send a reply.
    2. Getting a Response: Use serviceBus.GetResponses(request) which returns an IObservable<IResponse>. You can then use Rx operators like .OfType<T>(), .Take(1), and .Timeout() to manage the response lifecycle.
    // 1. Server-side: Handle the request and reply
    serviceBus.Requests
          .OfType<MyRequest>()
          .Subscribe(request => serviceBus.ReplyAsync(request, new MyResponse()));
    
    // 2. Client-side: Send request and wait for response
    serviceBus.GetResponses(new MyRequest())
          .OfType<MyResponse>()
          .Take(1)
          .Timeout(TimeSpan.FromSeconds(1))
          .Subscribe(
              r => Console.WriteLine("Received a response!"), 
              err => Console.WriteLine("Oh no!")
          );
  9. Implement a custom IServiceEndpointClient

    master

    To wrap external APIs or non-Obvs messaging systems into your bus, implement the IServiceEndpointClient interface. This allows you to standardize messaging semantics by treating external integrations as custom endpoints.

    You must implement:

    • Events: An IObservable<IEvent> representing incoming external events.
    • SendAsync(ICommand): Logic to call the external system for commands.
    • GetResponses(IRequest): Logic to wrap an external API call into an IObservable<IResponse>.
    • CanHandle(IMessage): A predicate to determine if this endpoint should process a specific message type.
    public class MyCustomEndpoint : IServiceEndpointClient
    {
        Type _serviceType = typeof(IMyCustomServiceMessage);
    
        public IObservable<IEvent> Events
        {
            get { /* subscribe to external MQ broker */ }
        }
    
        public Task SendAsync(ICommand command)
        {
            /* call external API */
        }
    
        public IObservable<IResponse> GetResponses(IRequest request)
        {
            /* call external API and wrap response in observable */
        }
    
        public bool CanHandle(IMessage message)
        {
            return _serviceType.IsInstanceOfType(message);
        }
    }
    
    // Register the endpoint during configuration
    IServiceBus serviceBus = ServiceBus.Configure()
        .WithActiveMQEndpoints<IMyServiceMessage>()
        // ... other config ...
        .WithEndpoints(new MyCustomEndpoint())
        .Create();