Skip to content
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
9 changes: 8 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
<PackageVersion Include="Paramore.Brighter" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.DynamoDB" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.DynamoDB.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.Firestore" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.MongoDb" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.MsSql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.MySql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.Postgres" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.Spanner" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Inbox.Sqlite" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.DynamoDB" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.DynamoDB.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.Firestore" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.MongoDb" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.MsSql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Locking.MySql" Version="$(BrighterVersion)" />
Expand All @@ -27,25 +30,29 @@
<PackageVersion Include="Paramore.Brighter.MessageScheduler.AWS.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.AWSSQS" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.AWSSQS.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.GcpPubSub" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.Kafka" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.MsSql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.Postgres" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.Redis" Version="10.0.2" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.Redis" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.RMQ.Sync" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.RMQ.Async" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.MessagingGateway.RocketMQ" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.DynamoDB" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.DynamoDB.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.Firestore" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.Hosting" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.MongoDb" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.MsSql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.MySql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.PostgreSql" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.Spanner" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Outbox.Sqlite" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.ServiceActivator.Extensions.Hosting" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Transformers.AWS" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Transformers.AWS.V4" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Transformers.Gcp" Version="$(BrighterVersion)" />
<PackageVersion Include="Paramore.Brighter.Transformers.MongoGridFS" Version="$(BrighterVersion)" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
Expand Down
30 changes: 30 additions & 0 deletions Fluent.Brighter.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Brighter.Redis", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedisSample", "samples\RedisSample\RedisSample.csproj", "{5CA1F284-5C44-44DF-B5EB-3008A4CBD52F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Brighter.GoogleCloud", "src\Fluent.Brighter.GoogleCloud\Fluent.Brighter.GoogleCloud.csproj", "{98813670-F995-41CF-85AB-E12B27AEA35F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GcpSample", "samples\GcpSample\GcpSample.csproj", "{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -313,6 +317,30 @@ Global
{5CA1F284-5C44-44DF-B5EB-3008A4CBD52F}.Release|x64.Build.0 = Release|Any CPU
{5CA1F284-5C44-44DF-B5EB-3008A4CBD52F}.Release|x86.ActiveCfg = Release|Any CPU
{5CA1F284-5C44-44DF-B5EB-3008A4CBD52F}.Release|x86.Build.0 = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|x64.ActiveCfg = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|x64.Build.0 = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|x86.ActiveCfg = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Debug|x86.Build.0 = Debug|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|Any CPU.Build.0 = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|x64.ActiveCfg = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|x64.Build.0 = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|x86.ActiveCfg = Release|Any CPU
{98813670-F995-41CF-85AB-E12B27AEA35F}.Release|x86.Build.0 = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|x64.ActiveCfg = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|x64.Build.0 = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|x86.ActiveCfg = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Debug|x86.Build.0 = Debug|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|Any CPU.Build.0 = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|x64.ActiveCfg = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|x64.Build.0 = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|x86.ActiveCfg = Release|Any CPU
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -339,5 +367,7 @@ Global
{7DB6D8C1-5EDE-4EF1-953B-515C26164F9C} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
{8DC14D04-F8D0-41CB-8964-B75257CEFE68} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{5CA1F284-5C44-44DF-B5EB-3008A4CBD52F} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
{98813670-F995-41CF-85AB-E12B27AEA35F} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{FF0AE58C-CFD7-4004-808A-BEF7B24D4997} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
EndGlobalSection
EndGlobal
224 changes: 215 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,57 @@
# fluent-brighter
A Fluent API to configure Brighter
# Fluent Brighter

[![License](https://img.shields.io/badge/license-GPL-blue.svg)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-512BD4)](https://dotnet.microsoft.com/)

# sample
A fluent, type-safe configuration API for [Paramore.Brighter](https://github.com/BrighterCommand/Brighter) - the command processor and message-oriented middleware framework for .NET.

```c#
services
## 📋 Overview

Fluent Brighter provides an intuitive, strongly-typed builder pattern for configuring Paramore.Brighter's messaging infrastructure, eliminating boilerplate code and making complex configurations easier to read and maintain.

### ✨ Key Features

- **🎯 Type-Safe Configuration** - IntelliSense-driven API with compile-time safety
- **🔌 Multiple Message Brokers** - RabbitMQ, Kafka, AWS SNS/SQS, GCP Pub/Sub, Redis, RocketMQ
- **💾 Transactional Outbox Pattern** - DynamoDB, Firestore, Spanner, MongoDB, SQL Server, PostgreSQL, MySQL, SQLite
- **📥 Idempotent Inbox Pattern** - Prevent duplicate message processing
- **🔒 Distributed Locking** - Coordinate across multiple instances
- **📦 Large Message Storage** - S3, GCS for oversized payloads
- **⏰ Message Scheduling** - AWS EventBridge, Hangfire, Quartz integration
- **🎨 Fluent Syntax** - Readable, maintainable configuration code

## 📦 Installation

Install the base package and your chosen messaging provider(s):

```bash
# Core package (required)
dotnet add package Fluent.Brighter

# Choose your provider(s)
dotnet add package Fluent.Brighter.RMQ.Async # RabbitMQ
dotnet add package Fluent.Brighter.Kafka # Apache Kafka
dotnet add package Fluent.Brighter.AWS.V4 # AWS SNS/SQS
dotnet add package Fluent.Brighter.GoogleCloud # GCP Pub/Sub
dotnet add package Fluent.Brighter.Redis # Redis Streams
dotnet add package Fluent.Brighter.RocketMQ # Apache RocketMQ
dotnet add package Fluent.Brighter.Postgres # PostgreSQL
dotnet add package Fluent.Brighter.SqlServer # SQL Server
dotnet add package Fluent.Brighter.MySql # MySQL
dotnet add package Fluent.Brighter.Sqlite # SQLite
dotnet add package Fluent.Brighter.MongoDb # MongoDB
```

## 🚀 Quick Start

### RabbitMQ Example

```csharp
using Fluent.Brighter;
using Microsoft.Extensions.DependencyInjection;
using Paramore.Brighter.ServiceActivator.Extensions.Hosting;

services
.AddHostedService<ServiceActivatorHostedService>()
.AddFluentBrighter(brighter => brighter
.UsingRabbitMq(rabbitmq => rabbitmq
Expand All @@ -15,21 +61,181 @@ services
.UsePublications(pb => pb
.AddPublication<GreetingEvent>(p => p
.SetTopic("greeting.event.topic")
.CreateTopicIfMissing())
.CreateTopicIfMissing())
.AddPublication<FarewellEvent>(p => p
.SetTopic("farewell.event.topic")
.CreateTopicIfMissing()))
.UseSubscriptions(sb => sb
.AddSubscription<GreetingEvent>(s => s
.SetSubscription("paramore.example.greeting")
.SetSubscriptionName("paramore.example.greeting")
.SetQueue("greeting.event.queue")
.SetTopic("greeting.event.topic")
.SetTimeout(TimeSpan.FromSeconds(200))
.EnableDurable()
.EnableHighAvailability())
.AddSubscription<FarewellEvent>(s => s
.SetSubscription("paramore.example.farewell")
.SetSubscriptionName("paramore.example.farewell")
.SetQueue("farewell.event.queue")
.SetTopic("farewell.event.topic")))
));
```
```

## 📚 More Examples

### AWS SNS/SQS with DynamoDB Outbox

```csharp
services.AddFluentBrighter(brighter => brighter
.UsingAws(aws => aws
.SetConnection(conn => conn
.SetCredentials(new BasicAWSCredentials("key", "secret"))
.SetRegion(RegionEndpoint.USEast1))
.UseSnsPublication(pb => pb
.AddPublication<OrderCreatedEvent>(p => p
.SetTopic("orders-created")
.CreateTopicIfMissing()))
.UseSqsSubscription(sb => sb
.AddSubscription<OrderCreatedEvent>(s => s
.SetSubscriptionName("order-processor")
.SetQueue("order-processing-queue")
.SetTopic("orders-created")
.SetMessagePumpType(MessagePumpType.Proactor)
.SetMakeChannels(OnMissingChannel.Create)))
.UseDynamoDbOutbox("outbox-table")
.UseDynamoDbInbox("inbox-table")
.UseDynamoDbOutboxArchive()));
```

### Google Cloud Platform (GCP) with Firestore

```csharp
services.AddFluentBrighter(brighter => brighter
.UsingGcp(gcp => gcp
.SetProjectId("my-gcp-project")
.UsePubSubPublication(pb => pb
.AddPublication<UserRegisteredEvent>(p => p
.SetTopicAttributes(t => t.SetName("user-registered-topic"))
.SetSource("https://example.com/users"))
.AddPublication<UserDeletedEvent>(p => p
.SetTopicAttributes(t => t.SetName("user-deleted-topic"))
.SetSource("https://example.com/users")))
.UsePubSubSubscription(sb => sb
.AddSubscription<UserRegisteredEvent>(s => s
.SetSubscriptionName("user-registration-handler")
.SetTopicAttributes(t => t.SetName("user-registered-topic"))
.SetNoOfPerformers(5))
.AddSubscription<UserDeletedEvent>(s => s
.SetSubscriptionName("user-deletion-handler")
.SetTopicAttributes(t => t.SetName("user-deleted-topic"))
.SetNoOfPerformers(3)))
.UseFirestoreOutbox("outbox")
.UseFirestoreInbox("inbox")
.UseFirestoreOutboxArchive(cfg => cfg
.SetMinimumAge(TimeSpan.FromDays(7))
.SetBatchSize(100))));
```

### Apache Kafka

```csharp
services.AddFluentBrighter(brighter => brighter
.UsingKafka(kafka => kafka
.SetBootstrapServers("localhost:9092")
.UsePublications(pb => pb
.AddPublication<PaymentProcessedEvent>(p => p
.SetTopic("payments-processed")
.SetMessageIdHeaderKey("message-id")))
.UseSubscriptions(sb => sb
.AddSubscription<PaymentProcessedEvent>(s => s
.SetSubscriptionName("payment-notification-service")
.SetTopic("payments-processed")
.SetGroupId("payment-consumers")
.SetNoOfPerformers(10)))));
```

### PostgreSQL with Outbox Pattern

```csharp
services.AddFluentBrighter(brighter => brighter
.UsingRabbitMq(rabbitmq => /* ... RabbitMQ config ... */)
.Producers(producer => producer
.UsePostgresOutbox(cfg => cfg
.SetConnectionString("Host=localhost;Database=brighter;")
.SetTableName("outbox")))
.Subscriptions(sub => sub
.UsePostgresInbox(cfg => cfg
.SetConnectionString("Host=localhost;Database=brighter;")
.SetTableName("inbox")))
.UseOutboxSweeper(cfg => cfg
.SetTimerInterval(TimeSpan.FromSeconds(30))
.SetBatchSize(100)));
```

### SQL Server with Distributed Locking

```csharp
services.AddFluentBrighter(brighter => brighter
.UsingRabbitMq(rabbitmq => /* ... RabbitMQ config ... */)
.Producers(producer => producer
.UseSqlServerOutbox(cfg => cfg
.SetConnectionString("Server=localhost;Database=Brighter;")
.SetTableName("Outbox"))
.UseSqlServerDistributedLock(cfg => cfg
.SetConnectionString("Server=localhost;Database=Brighter;")
.SetTableName("DistributedLock"))));
```

## 🎯 Key Concepts

### Outbox Pattern
Ensures reliable message publishing by storing messages in a database transaction with your business data, then publishing them asynchronously.

### Inbox Pattern
Prevents duplicate message processing by tracking received messages in a database, ensuring idempotency.

### Distributed Locking
Coordinates message processing across multiple service instances to prevent concurrent execution.

### Outbox Sweeper
Background service that publishes messages from the outbox and archives old messages.

### Large Message Storage
Stores large message payloads (luggage) in cloud storage (S3, GCS) and passes references through the message broker.

## 🔧 Configuration Options

### Message Pump Types
- **Reactor** - Synchronous, single-threaded message processing
- **Proactor** - Asynchronous, concurrent message processing

### Channel Creation
- **Create** - Automatically create topics/queues if missing
- **Assume** - Assume topics/queues already exist
- **Validate** - Validate existence and throw if missing

### Subscription Options
- Number of performers (concurrent handlers)
- Timeout settings
- Retry policies
- Dead letter queues
- Message ordering
- Acknowledgment modes

## 📖 Documentation

For detailed documentation on Paramore.Brighter, visit:
- [Paramore.Brighter Documentation](https://brightercommand.github.io/Brighter/)
- [Paramore.Brighter GitHub](https://github.com/BrighterCommand/Brighter)

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📄 License

This project is licensed under the GPL 3 License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

Built on top of the excellent [Paramore.Brighter](https://github.com/BrighterCommand/Brighter) framework by Ian Cooper and contributors.

12 changes: 12 additions & 0 deletions samples/GcpSample/Commands/FarewellEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Paramore.Brighter;

namespace GcpSample.Commands;

public class FarewellEvent(string farewell) : Event(Uuid.New())
{
public FarewellEvent() : this(string.Empty)
{
}

public string Farewell { get; set; } = farewell;
}
10 changes: 10 additions & 0 deletions samples/GcpSample/Commands/GreetingEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Paramore.Brighter;

namespace GcpSample.Commands;

public class GreetingEvent(string greeting) : Event(Uuid.New())
{
public GreetingEvent() : this(string.Empty) { }

public string Greeting { get; set; } = greeting;
}
19 changes: 19 additions & 0 deletions samples/GcpSample/GcpSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Paramore.Brighter.ServiceActivator.Extensions.Hosting" />
<PackageReference Include="Serilog.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Fluent.Brighter.GoogleCloud\Fluent.Brighter.GoogleCloud.csproj" />
</ItemGroup>
</Project>
Loading