Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added RabbitMQ as a message broker. #44

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Source/BSN.Commons/BSN.Commons.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.32" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.32" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="Polly" Version="8.0.0" />
<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
<PackageReference Include="System.Text.Json" Version="6.0.6" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator;
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts;
using IEvent = BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts.IEvent;

namespace BSN.Commons.Infrastructure.MessageBroker.EventAggregator
{
/// <summary>
/// Manages event subscriptions for the in-memory event aggregator.
/// </summary>
public partial class InMemoryEventAggregatorSubscriptionManager : IEventAggregatorSubscriptionManager
{
/// <summary>
/// Initializes a new instance of the <see cref="InMemoryEventAggregatorSubscriptionManager"/> class.
/// </summary>
public InMemoryEventAggregatorSubscriptionManager()
{
_eventReceivers = new Dictionary<string, IEventReceiver>();
_eventsType = new List<Type>();
}

/// <summary>
/// Adds a subscription for a specific event type.
/// </summary>
/// <typeparam name="TEvent">The type of event to subscribe to.</typeparam>
/// <param name="eventReceiver">The event receiver to register.</param>
/// <exception cref="ArgumentException"></exception>
public void AddSubscription<TEvent>(IEventReceiver eventReceiver) where TEvent : IEvent
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
string eventName = GetEventKey<TEvent>();

RegisterEventReceiver(eventReceiver, eventName);

if (!_eventsType.Contains(typeof(TEvent)))
{
_eventsType.Add(typeof(TEvent));
}
}

/// <summary>
/// Removes a subscription for a specific event by name.
/// </summary>
/// <param name="eventName">The name of the event to unsubscribe from.</param>
/// <exception cref="ArgumentException"></exception>
public void RemoveSubscription(string eventName)
{
RemoveEventReceiver(eventName);
}

/// <summary>
/// Retrieves the event receiver for a specific event type.
/// </summary>
/// <typeparam name="T">The type of event to retrieve the event receiver for.</typeparam>
/// <returns>The event receiver for the specified event type.</returns>
public IEventReceiver GetEventReceiverForEvent<T>() where T : IEvent
{
string key = GetEventKey<T>();

return _eventReceivers[key];
}

/// <summary>
/// Retrieves the event receiver for a specific event by name.
/// </summary>
/// <param name="eventName">The name of the event to retrieve the event receiver for.</param>
/// <returns>The event receiver for the specified event name.</returns>
/// <exception cref="ArgumentException"></exception>
public IEventReceiver GetEventReceiverForEvent(string eventName)
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
if (string.IsNullOrEmpty(eventName))
{
throw new ArgumentException(
$"Event name can not be null or empty.");
}

return _eventReceivers[eventName];
}

/// <summary>
/// Gets the unique key for an event type.
/// </summary>
/// <typeparam name="T">The type of event to get the key for.</typeparam>
/// <returns>The unique key for the event type.</returns>
public string GetEventKey<T>()
{
return typeof(T).FullName;
}

/// <summary>
/// Checks if there are subscriptions for a specific event type.
/// </summary>
/// <typeparam name="T">The type of event to check for subscriptions.</typeparam>
/// <returns>True if there are subscriptions for the event type; otherwise, false.</returns>
public bool HasSubscriptionsForEvent<T>() where T : IEvent
{
string key = GetEventKey<T>();

return HasSubscriptionsForEvent(key);
}

/// <summary>
/// Checks if there are subscriptions for a specific event by name.
/// </summary>
/// <param name="eventName">The name of the event to check for subscriptions.</param>
/// <returns>True if there are subscriptions for the event; otherwise, false.</returns>
/// <exception cref="ArgumentNullException"></exception>
public bool HasSubscriptionsForEvent(string eventName)
{
if (string.IsNullOrEmpty(eventName))
{
throw new ArgumentException(
$"Event name can not be null or empty.");
}

return _eventReceivers.ContainsKey(eventName);
}

/// <summary>
/// Clears all event subscriptions.
/// </summary>
public void Clear() => _eventReceivers.Clear();

/// <summary>
/// Retrieves the event type by name.
/// </summary>
/// <param name="eventName">The name of the event type to retrieve.</param>
/// <returns>The event type corresponding to the provided name.</returns>
/// <exception cref="ArgumentException"></exception>
public Type GetEventTypeByName(string eventName)
{
if (string.IsNullOrEmpty(eventName))
{
throw new ArgumentException(
$"Event name can not be null or empty.");
}

return _eventsType.SingleOrDefault(t => t.FullName == eventName);
}

public bool IsEmpty => !_eventReceivers.Keys.Any();

private void RegisterEventReceiver(IEventReceiver eventReceiver, string eventName)
{
if (_eventReceivers.ContainsKey(eventName))
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ArgumentException(
$"Receiver {nameof(eventReceiver)} already registered for '{eventName}'", nameof(eventReceiver));
}

_eventReceivers[eventName] = eventReceiver;
}

private void RemoveEventReceiver(string eventReceiverName)
{
if (string.IsNullOrEmpty(eventReceiverName))
{
throw new ArgumentException(
$"Event receiver name can not be null or empty.");
}

if (_eventReceivers.ContainsKey(eventReceiverName))
{
_eventReceivers.Remove(eventReceiverName);

Type eventType = _eventsType.SingleOrDefault(e => e.FullName == eventReceiverName);

if (eventType != null)
{
_eventsType.Remove(eventType);
}
}
else
{
throw new ArgumentException(
$"Event Receiver \"{eventReceiverName}\" does not exist.");
}
}

private IEventReceiver FindEventReceiver(string eventName)
{
if (string.IsNullOrEmpty(eventName))
{
throw new ArgumentException(
$"Event name can not be null or empty.");
}

if (!HasSubscriptionsForEvent(eventName))
{
return null;
}

return _eventReceivers[eventName];
}

private readonly Dictionary<string, IEventReceiver> _eventReceivers;

private readonly List<Type> _eventsType;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.EventModels;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an abstract base class for event objects with associated data in the Event Aggregator pattern.
/// This class is a fundamental building block for implementing event-driven architectures.
/// For more information on the Event Aggregator pattern, please refer to the documentation at:
/// <see href="Source/BSN.Commons/Infrastructure/MessageBroker/README.md"/>.
/// </summary>
/// <typeparam name="T">The type of data model associated with the event. Data models provide structured information
/// that can be associated with events and used to convey additional context or details.</typeparam>
public abstract class EventBase<T> : IEvent<T> where T : IEventDataModel
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Gets or sets the data model associated with the event. Data models are used to encapsulate event-specific information,
/// making it possible to transmit complex data along with events in a structured and organized manner.
/// </summary>
public T DataModel { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.EventModels;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for events with associated data models of type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of data model associated with the event.</typeparam>
/// <summary>
/// Represents an interface for events with associated data models of type <typeparamref name="T"/>.
/// In event-driven architectures, events provide a mechanism for loosely coupling components by
/// allowing them to communicate without needing to be aware of each other's existence. This interface
/// serves as a foundational concept for defining events with specific data models, enabling structured
/// and contextual information to be transmitted along with the events.
/// </summary>
/// <typeparam name="T">The type of data model associated with the event. Data models encapsulate relevant
/// information that can provide additional context or details related to the event.</typeparam>
public interface IEvent<T> : IEvent where T : IEventDataModel
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Gets the data model associated with the event. The data model serves as a container for structured
/// information, allowing events to convey specific details or context. By including data models in events,
/// components can make use of this information for various purposes, such as processing, logging, or decision-making.
/// </summary>
T DataModel { get; }
}

/// <summary>
/// Represents a generic interface for events. In event-driven systems, events play a central role in facilitating
/// communication and coordination among various components. This interface defines the basic contract for events
/// without specifying a particular data model, providing flexibility for a wide range of event types and purposes.
/// </summary>
public interface IEvent
{

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for an event aggregator, combining the fundamental functionality of a publisher, subscriber, and disposable object.
/// In architectural design, an event aggregator is a crucial component used to facilitate communication and coordination among various parts
/// of a software system. It acts as a centralized hub for the distribution of events, allowing different components to publish and subscribe to
/// events without having direct knowledge of each other. By merging the roles of publisher, subscriber, and IDisposable, this interface encapsulates
/// key responsibilities within the event-driven architecture.
/// </summary>
public interface IEventAggregator : IPublisher, ISubscriber, IDisposable
soroshsabz marked this conversation as resolved.
Show resolved Hide resolved
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Threading.Tasks;
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.EventModels;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for an event receiver, a critical component in an event-driven architecture responsible for handling events with associated data models.
/// Event receivers play a pivotal role in an event-driven system, as they are responsible for processing and responding to events, which often carry valuable data that needs to be acted upon.
/// </summary>
public interface IEventReceiver
{
/// <summary>
/// Handles an event with an associated data model of type <typeparamref name="T"/>.
/// This method is invoked when an event is received, allowing the receiver to process the event and the associated data model.
/// </summary>
/// <typeparam name="T">The type of data model associated with the event.</typeparam>
/// <param name="event">The event to handle, which carries the associated data model.</param>
/// <returns>A task representing the asynchronous handling of the event, which is crucial for non-blocking and responsive event processing.</returns>
Task Handle<T>(IEvent<T> @event) where T : IEventDataModel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Threading.Tasks;
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.EventModels;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for an event publisher, a fundamental component in an event-driven architecture, responsible for publishing events.
/// In an event-driven system, publishers play a central role in broadcasting events to interested subscribers, enabling efficient communication and decoupling between system components.
/// </summary>
public interface IPublisher
{
/// <summary>
/// Publishes an event of type <typeparamref name="TEventModel"/>.
/// This method is responsible for broadcasting an event to all interested subscribers within the system.
/// </summary>
/// <typeparam name="TEventModel">The type of event model to publish.</typeparam>
/// <param name="event">The event to publish, carrying the data model associated with the event.</param>
void Publish<TEventModel>(IEvent<TEventModel> @event) where TEventModel : IEventDataModel;

/// <summary>
/// Asynchronously publishes an event of type <typeparamref name="TEventModel"/>.
/// Asynchronous event publishing allows for responsive communication within the system without blocking the publisher's operations.
/// </summary>
/// <typeparam name="TEventModel">The type of event model to publish.</typeparam>
/// <param name="event">The event to publish, carrying the data model associated with the event.</param>
/// <returns>A task representing the asynchronous publishing of the event, ensuring non-blocking event propagation and system responsiveness.</returns>
Task PublishAsync<TEventModel>(IEvent<TEventModel> @event) where TEventModel : IEventDataModel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.EventModels;

namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for an event subscriber, a key component in an event-driven architecture, responsible for managing event subscriptions.
/// In an event-driven system, subscribers express their interest in specific events and their associated data models, enabling them to react to relevant changes or notifications.
/// </summary>
public interface ISubscriber
{
/// <summary>
/// Checks if a subscription exists for a specific event type with associated data model.
/// </summary>
/// <typeparam name="TEvent">The type of event to check for a subscription.</typeparam>
/// <typeparam name="TEventDataModel">The type of data model associated with the event.</typeparam>
/// <returns><c>true</c> if a subscription exists; otherwise, <c>false</c>.</returns>
bool HasSubscriptionForEvent<TEvent, TEventDataModel>() where TEvent : IEvent<TEventDataModel> where TEventDataModel : IEventDataModel;

/// <summary>
/// Subscribes an event receiver to handle events of a specific type with associated data model.
/// </summary>
/// <typeparam name="TEvent">The type of event to subscribe to.</typeparam>
/// <typeparam name="TEventDataModel">The type of data model associated with the event.</typeparam>
/// <param name="eventReceiver">The event receiver to subscribe.</param>
void Subscribe<TEvent, TEventDataModel>(IEventReceiver eventReceiver) where TEvent : IEvent<TEventDataModel> where TEventDataModel : IEventDataModel;

/// <summary>
/// Unsubscribes an event receiver from handling events of a specific type with associated data model.
/// </summary>
/// <typeparam name="TEvent">The type of event to unsubscribe from.</typeparam>
/// <typeparam name="TEventDataModel">The type of data model associated with the event.</typeparam>
/// <param name="eventReceiver">The event receiver to unsubscribe.</param>
void UnSubscribe<TEvent, TEventDataModel>(IEventReceiver eventReceiver) where TEvent : IEvent<TEventDataModel> where TEventDataModel : IEventDataModel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace BSN.Commons.Infrastructure.MessageBroker.EventContracts.EventAggregator.Contracts
{
/// <summary>
/// Represents an interface for an unprecedented event, which is an event that occurs unexpectedly or outside of the normal flow.
/// </summary>
public interface IUnprecedentedEvent
{

}
}
Loading
Loading