0% found this document useful (0 votes)
14 views13 pages

Messaging Framework Overview

The Messaging Framework is designed to facilitate asynchronous messaging in distributed applications, providing a unified interface for various messaging brokers while ensuring flexibility for future changes. It supports key messaging patterns such as Publisher-Subscriber, Request-Response, and CQRS, and is built on the .NET Core stack, compatible with major operating systems. The framework emphasizes extensibility, allowing custom implementations for error handling, serialization, and service registration.

Uploaded by

vortexvisionsai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views13 pages

Messaging Framework Overview

The Messaging Framework is designed to facilitate asynchronous messaging in distributed applications, providing a unified interface for various messaging brokers while ensuring flexibility for future changes. It supports key messaging patterns such as Publisher-Subscriber, Request-Response, and CQRS, and is built on the .NET Core stack, compatible with major operating systems. The framework emphasizes extensibility, allowing custom implementations for error handling, serialization, and service registration.

Uploaded by

vortexvisionsai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Messaging Framework

Background
Real-world application should follow strong requirements for its
availability and scalability - such requirements would define distributed
nature of the application itself. Each part should be designed as self-
sufficient application then, but, putting all together, their should behave
as single one, at the end. Drawing analogy with human world - it's like a
sport team.

Regardless subject area, internal communication effectiviness start


coming to the first plan - depending of implementation, its could lead to
success, it could be a bottleneck, but it couldn't disappear from this
model.

Requirements
The most natural communication approach - is asynchronous
messaging. There are a lot of solutions already done - either messaging
brokers or either orchestration frameworks.

Messaging Framework provides an abstraction layer for application


development, wraps messaging brokers functionality and exposes single
interface for utilizing common messaging patterns in applications
development, regardless messaging broker.

Main idea, is to provide enough level of flexibility for application design


to allow move to another messaging broker, at some point of time
(considering application growth, business requirements changes, market
evolution, etc), with minimum efforts needed from developers. There is
no requirement for being able to switch between brokers in a real-time
(but it actually supported) but there is a strict requirement - to be able
to utilize messaging broker features at its maximum.

1/13
Key requirements and responsibilities:

publish messages to messaging entity;


subscribe to and receive messages from messaging entity;
support messages metadata;
implement common messaging patterns;
disregard messaging broker infrastructure/topology/configuration;
operate with minimum needed access permissions;
expose extensible SDK.

Messaging brokers:

RabbitMQ
Kafka
MSMQ
Azure Service Bus
Azure Events Hub
HTTP / REST

Messaging Framework uses latest .NET Core stack, supports all major OS
platforms (Linux, Mac OS and Windows) and fully compatible with .NET
Full Framework (version 4.6.1 or greater).

Market Solutions

NServiceBus Mass Transit Rhino

Messaging Brokers

RabbitMQ + + -

Kafka + - -

MSMQ + + +

+ + -
Azure Service Bus

Azure Events Hub - - -

2/13
HTTP + - -

Messaging Patterns

Publisher-Subscriber + + +

Request-Response + + +

CQRS + + -

Saga + + -

Features

Single Interface - + -

Supports any Topology - - +

Commercial License + - -

Supports .Net Core - + -

Easy Integratable - + -

Design
Contract

There are two main abstractions in Messaging Framework:

IMessagingBroker - represents an abstraction of messaging


broker client (e.g. Kafka client, RabbitMQ client, etc)
IMessagingEntity - represents an object in the transport layer
that can accept and deliver messages (e.g. topic, queue, exchange,
etc)

And a few helping ones:

IMessagingSubscription - represents an entity messages


subscription

3/13
Message - represents a message and its metadata
MessagingBrokerFactory - represents factory that manages
messaging broker type factories

Code snippet for them:

public interface IMessagingBroker : IDisposable


{
IMessagingEntity GetEntity(string entityUri);
}

public interface IMessagingEntity


{
string EntityName { get; }
IMessagingBroker Broker { get; }

Task PublishAsync(Message message);


Task<IMessagingSubscription>
SubscribeAsync(Func<Message, Task> messageHandler);
}

public interface IMessagingSubscription


{
string SubscriptionId { get; }
string EntityName { get; }
Task CancelAsync();
}

public sealed class Message


{
public string this[string name] { get; }
public Stream BodyStream { get; }
}

public static class MessagingBrokerFactory


{
public static IMessagingBroker GetBroker(string
uri) { ... }

4/13
public static void RegisterBrokerFactory(string
schema, Func<string, IMessagingBroker> factory) { ...
}
}

Messaging Patterns
Publisher - Subscriber

This is the most basic and natural messaging pattern. It defines two
roles:

Publisher - the guy which publishing messages (like events,


commands, queries, at cetera) to some messaging entity
Subscriber - the guy which receiving messages from some
messaging entity (in most cases another messaging entity than for
publisher)

Neither Publisher, neither Subscriber have no idea of any details,


about each other (like: ip address, version, configuration, ...); neither
about any routing actions or any transformations of the message that are
being executed by broker - its tightly decouples internal application
components.

Sample of message publishing to core.events entity:

5/13
var eventsEntity = broker.GetEntity("core.events");

await eventsEntity.PublishAsync(messageBuilder =>


messageBuilder.SetContent("Hello world!")
);

Sample of subscribing and handling messages from core.events entity:

var eventsEntity = broker.GetEntity("core.events");

await eventsEntity.SubscribeAsync(message =>


Task.CompletedTask // no-op handler
);

Shared Subscription

Each subscription by default creates new respective entry on messaging


broker (this case is also valid on subscribing multiple times to the same
messaging entity, from same application), but there are a lot of cases
where better to have single subscription entry on messaging broker,
making a selection of appropriate handler on application side (to be able
to share same messaging broker subscription entry).

Messaging Framework utilizes this pattern - it exposes an extension


methods for messaging entity, which allow to register message-filtering
predicate, alongside with handler. At the end of the day, when handling
message, the predicates will be executed one by one (last registered -
first executed).

There are two supported behaviors:

the first handler, whose predicate passes, will be executed;


all handlers, whose predicates passes, will be executed.

6/13
// First handler, whose predicate passes, will be
executed
Task<IMessagingSubscription>
SubscribeAsync(Func<Message, bool> filter,
Func<Message, Task> handler);

// All handlers, whose predicate passes, will be


executed
Task<IMessagingSubscription>
SubscribeMultipleAsync(Func<Message, bool> filter,
Func<Message, Task> handler);

Typed Messaging

Utilizing approach of shared subscription, another messaging behavior


has been introduced - sharing same messaging entity for publish /
subscribe on messages by their actual type.

Basically, when it comes to real development, message body will contain


serialized representation of some DTO (data transfer object). Messaging
Framework wraps serialization / deserializaton and message filtering
routines internally and exposes convenient extension methods for
messaging entity:

Task PublishAsync<TData>(TData data);


Task<IMessagingSubscription> SubscribeAsync<TData>
(Func<TData, Task> messageHandler);

Sample of publishing the message with serialized


JobProgressUpdateEvent object instance:

var eventsEntity = broker.GetEntity("core.events");

7/13
var jobProgressUpdateEvent = new
JobProgressUpdateEvent { JobId = 1, Progress = 0.11
};
await
eventsEntity.PublishAsync<JobProgressUpdateEvent>
(jobProgressUpdateEvent);

Sample of handling messages with JobProgressUpdateEvent objects:

var eventsEntity = broker.GetEntity("core.events");

await
eventsEntity.SubscribeAsync<JobProgressUpdateEvent>
(jobProgressUpdateEvent =>
Task.CompletedTask // no-op handler
);

Competing Consumers

High loaded application is expected to handle a large number of


requests. Rather than process each request synchronously, a common
technique is to pass them through a messaging system to another
service (a consumer service) that handles them asynchronously. Passing
theese requests to the same messaging channel and enabling multiple
concurrent consumers for theirs processing: optimizes system
throughput, improves scalability and availability, and to balances the
workload.

8/13
Request - Response

This pattern could be explained as Remote Procedure Call in terms of


Messaging Framework. The main idea is to make a synchronous call to
another service, in other words: execute some code remotely and
synchronously.

Service makes a request and waits for response:


publishes message with actual request data to messaging entity;
subscribes for messages from messaging entity used for responses on
first call.
Handler handles message with initial request and sending a
response
subscribes for request messages from respective entity;
publishes response messages back.

Code snippet for Request - Response extension methods for messaging

9/13
entity:

// Publish message with request


Task InvokeAsync<TRequest>(TRequest request);
Task<TResponse> InvokeAsync<TRequest, TResponse>
(TRequest request);

// Subscribes to request messages and publishes


response
Task<IMessagingSubscription>
RegisterHandlerAsync<TRequest>(Action<TRequest>
handler);
Task<IMessagingSubscription>
RegisterHandlerAsync<TRequest>(Func<TRequest, Task>
handler);
Task<IMessagingSubscription>
RegisterHandlerAsync<TRequest, TResponse>
(Func<TRequest, TResponse> handler);
Task<IMessagingSubscription>
RegisterHandlerAsync<TRequest, TResponse>
(Func<TRequest, Task<TResponse>> handler);

Sample of Service code:

var jobsEntity = broker.GetEntity("core.jobs");

var cancelJobRequest = new CancelJobRequest { JobId =


1, Reason = "User cancel" };
var cancelJobResponse = await
jobsEntity.InvokeAsync<CancelJobRequest,
CancelJobResponse>(cancelJobRequest);

Sample of Handler code:

10/13
var jobsEntity = broker.GetEntity("core.jobs");

await
jobsEntity.RegisterHandlerAsync<CancelJobRequest,
CancelJobResponse>(cancelJobRequest =>
new CancelledJobResponse { JobId =
cancelJobRequest.JobId }
);
...

class CancelledJobResponse : CancelJobResponse { ...


}

Command and Query Responsibility Segregation

Another good example of utilizing Request - Response extensions is


implementing CRQS pattern (Command and Query Responsibility
Segregation). The main idea around CQRS is to segregate operations that
reads data and operations that updates data, by using separate
interfaces. This can maximize performance, scalability and security,
supporting the evolution of the system over time through higher
flexibility and prevents update commands from causing merge conflicts
at the domain level.

Extensibility

11/13
Service Provider

Extensibility of Messaging Framework is based on extension methods


feature of .NET Framework 3.0.

Extension methods enable you to "add" methods to existing types


without creating a new derived type, recompiling, or otherwise
modifying the original type. Extension methods are a special kind
of static method, but they are called as if they were instance
methods on the extended type. For client code written in C#, F#
and Visual Basic, there is no apparent difference between calling
an extension method and the methods that are actually defined in
a type.

MSDN: https://docs.microsoft.com/en-us/dotnet/csharp/programming-
guide/classes-and-structs/extension-methods

Implementation of described messaging patterns often utilizes specific


features of messaging brokers or, in other hand, requires custom
implementation from Messagin Framework wrappers on top of them.

Each instance of IMessagingBroker has its own instance of Service


Provider. Users can provide a custom implementation of any service and
enable it for all or only for selected messaging brokers.

Sample for registration of custom IErrorHandlingStrategy


implementation:

broker.AddServiceFactory<IErrorHandlingStrategy>(_ =>
new CustomErrorHandlingStrategy());

It can also be registered as a singleton:

var errorHandlingStartegy = new

12/13
CustomErrorHandlingStrategy();

broker.AddServiceFactory<IErrorHandlingStrategy>(_ =>
errorHandlingStartegy);

Custom Serialization

JSON considered as default format for message, however Messaging


Framework supports custom serializers / deserializers:

implement IMessageWriter for message serialization


implement IMessageReader for message deserialization

Register factory of custom message reader/writer in broker service


provider, using AddServiceFactory extension method for
IMessagingBroker.

Custom Error Handling

Message handling exceptions should be caught by Messaging Framework


consumers - this is something that is tightly application-specific.
However message handling exceptions still could be caught by
IErrorHandlingStrategy service - default implementation publish a
special message with exception details to a predefined error entity:
error.

13/13

You might also like