View on GitHub

Acquaintance

Internal messaging framework for loosely-coupled .NET applications.

Acquaintance

Acquaintance is a .NET library for intra-process communications. It’s an in-memory message bus that loosely-coupled parts of your application can use to communicate with each other. At the heart, Acquaintance implements three basic communication patterns: Publish/Subscribe, Request/Response and Scatter/Gather. On top of these three basic patterns, a number of other patterns, features and workflows are built to help support medium- and large-size projects.

Get Acquaintance from nuget:

Install-Package Acquaintance

When you’re ready to code, create a Message Bus:

    var messageBus = new MessageBusBuilder().Build();

Publish/Subscribe

Publish/Subscribe is a pattern where you have events of interest being generated by one part of the system and other parts need to be aware of them. Published messages are delivered to all interested subscribers.

    // Create a subscription
    messageBus.Subscribe<MyEvent>(s => s
        .WithTopic("test event")
        .Invoke(e => Console.WriteLine(e.Message)));

    // Publish a message
    messageBus.Publish("test event", new MyEvent {
        Message = "Hello World"
    });

Request/Response

Request/Response is essentially an abstracted method call or local RPC mechanism. One part of your code makes a request and a single listener fulfills it. This can be an improvement over Dependency Injection or Service Location patterns in some cases, such as when you only need a calculation result and do not want to manage and maintain the object hierarchy necessary to calculate that result.

    // Setup a Listener
    messageBus.Listen<MyRequest, MyResponse>(l => l
        .WithTopic("test")
        .Invoke(req => new MyResponse {
            Message = "Hello " + req.Message
        }));

    // Send a request and wait for the response
    var response = messageBus.RequestWait<MyRequest, MyResponse>("test",
        new MyRequest { Message = "World" });
    Console.WriteLine(response.Message);

Scatter/Gather

Scatter/Gather is conceptually similar to Request/Response except the channel may have many listeners or participants. Internally and in terms of API the two are significantly different, however. Because scatter/gather requests must wait on an unknown number of participants providing responses asynchronously, waiting and timeouts require significant planning and consideration. For best performance, when you do scatter/gather make sure you know how many responses you’d like to receive and how long are you willing to wait to get them.

    // Setup a Participant
    messageBus.Participate<MyRequest, MyResponse>(p => p
        .WithTopic("test")
        .Invoke(req => new[] { new MyResponse {
            Message = "Hello " + req.Message"
        }}));

    // Scatter the request message to all participants
    var scatter = messageBus.Scatter<MyRequest, MyResponse>("test",
        new MyRequest { Message = "World" });

    var responses = scatter.GatherResponses();
    foreach (var response in responses)
        Console.WriteLine(response.Response.Message);

Use-Cases

Here are some common use-cases which Acquaintance was explicitly designed to handle:

  1. Simplifying complex constructor signatures which may arise from use of Dependency Injection, and avoiding the creation of many redundant adaptor types.
  2. Providing serialized access to resources which are not intrinsically thread-safe, such as sockets and files
  3. Round-Robin dispatch and predicate-based routing of events and requests to multiple handlers
  4. Serving as an easy wiring mechanism for loosely-coupled and plugin-based applications
  5. Serving as an intermediate step for monolithic applications which are in the process of being decomposed into microservices
  6. Serving as an easy bridge for external resources, queues, message brokers and Enterprise Service Buses
  7. Acting as the communication mechanism for domain events in a Domain-Driven solution
  8. Simplify the distribution of work among many threads while avoiding many common pitfalls of multi-threaded programming

Additional Features

Acquaintance provides several features which are extensions of the three basic messaging patterns above:

  1. Built-In Unit testing functionality with ability to mock channels and expect messages
  2. Message timer to generate application heartbeats on a set schedule
  3. Automatic polling of asynchronous event sources
  4. Automatic delivery retry with the Outbox pattern

Design Goals

Acquaintance follows several design goals and principles:

  1. Make good use of appropriate patterns, including Gang-of-Four, Messaging and Architectural patterns
  2. Be lockless and non-blocking
  3. Optimize for the common case, both in terms of performance and usability
  4. Use good, fluent interfaces to help features be usable and discoverable
  5. Be flexible and extensible to support a wide variety of projects and use-cases

Contraindications

There are situations where using Acquaintance is appropriate and situations where it is not. See the page on Contraindications for a discussion of these.