Panama.Canal
6.0.2
See the version list below for details.
dotnet add package Panama.Canal --version 6.0.2
NuGet\Install-Package Panama.Canal -Version 6.0.2
<PackageReference Include="Panama.Canal" Version="6.0.2" />
paket add Panama.Canal --version 6.0.2
#r "nuget: Panama.Canal, 6.0.2"
// Install Panama.Canal as a Cake Addin #addin nuget:?package=Panama.Canal&version=6.0.2 // Install Panama.Canal as a Cake Tool #tool nuget:?package=Panama.Canal&version=6.0.2
Panama - A fluent dotnet core framework for developing event-based microservices
Panama is a dotnet core framework based on the command query responsibility segregation design pattern (CQRS overview).
The framework is organized around ICommand
, IQuery
, IValidate
, IRollback
interfaces:
ICommand
Commands are objects that can change the state of a domain. A chain of responsibilities in the form of multiple ICommand
objects can be scaffolded on a handler to form a comprehensive domain event:
var result = await _provider
.GetRequiredService<IHandler>()
.Add(new User() { Email: diran.ogunlana@gmail.com })
.Command<SaveUser>()
.Command<PublishUser>()
.Invoke();
These objects inherit from the ICommand
interface and should only encapsulate a single business rule:
public class SaveUser : ICommand
{
private UserDbContext _store;
public SaveUser(UserDbContext store)
{
_store = store;
}
public bool Execute(IContext context)
{
var user = data.GetSingle<User>();
_store.Entry(user).State = EntityState.Modified;
_store.SaveChangesAsync();
}
}
IQuery
Query objects represent readonly data store operations such as retreiving a collection of entities by a condition from a data store:
public class GetUser : IQuery
{
private UserDbContext _store;
public GetUser(UserDbContext store)
{
_store = store;
}
public bool Execute(IContext context)
{
//get id from payload
var id = context.KvpGetSingle<string, string>("User.Id");
var user = context.Users
.Where(s => s.Id == id)
.FirstOrDefault();
//save queried user to handler context for processing down stream
context.Data.Add(user);
}
}
IValidate
Validators are objects that perform prerequisite operations against the handler context prior to query, command, or rollback operations:
public class EmailNotNullOrEmpty : IValidation
{
public bool Execute(IContext context)
{
var models = context.DataGet<User>();
if (models == null)
throw new ValidationException("User(s) cannot be found.");
foreach (var user in models)
if (string.IsNullOrEmpty(user.Email))
throw new ValidationException($"{user.Name} email is not valid.");
}
}
IRollback
Rollback object perform the restoration operations against the domain in the event of an exception in processing the handler context. Take note of the use of the Snapshot
filter on the context which creates a serialized copy of the object to preserve its original state:
public class RollbackUser : IRollback
{
private UserDbContext _store;
public RollbackUser(UserDbContext store)
{
_store = store;
}
public bool Execute(IContext context)
{
//get cached snapshot of previous state from handler context
var existing = context.SnapshotGetSingle<User>();
//save snapshot version e.g. prior to current changes
_store.Entry(user).State = EntityState.Modified;
await _store.SaveChangesAsync();
}
}
Here is an example of a save domain event with validation and rollback capabilities:
var result = await _provider
.GetRequiredService<IHandler>()
.Add(new User() {
Email = "diran.ogunlana@gmail.com",
FirstName = "Diran" })
.Validate<UserEmailNotNullOrEmpty>()
.Validate<UserFirstNameNotNullOrEmpty>()
.Query<GetUserByEmail>()
.Command<CreateUserSnapshot>()
.Command<SaveUser>()
.Command<PublishUser>()
.Rollback<RollbackUser>()
.Invoke();
Getting Started
Default Configuration:
services.AddPanama(configuration: builder.Configuration);
For native logging support, add the Microsoft.Extensions.DependencyInjection
and Microsoft.Extensions.Logging
nuget packages with the following configuration:
services.AddLogging(loggingBuilder => {
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
// configure Logging with NLog, ex:
// loggingBuilder.AddNLog(_configuration);
});
appsettings.json
or environment variables can be for initial options configurations:
{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
},
"AllowedHosts": "*",
"Panama": {
"Canal": {
"Options": {
"Scope": [CLUSTER_NAMESPACE]
}
}
}
}
Panama Canal - A transactional event bus framework for distributed microservices
The Canal provides a transactional messaging framework with saga support for dotnet core. The Canal integrates with native dotnet core DI, logging (dotnet core logging) and Entity Framework (dotnet ef core). Messages can be published using polling or event stream. Multiple message brokers can be configured and scoped for auto-scaling scenarios.
Panama Canal Architecture At A Glance:
Panama Canal Implementation At A Glance:
public class SaveWeatherForecast : ICommand
{
private readonly IGenericChannelFactory _factory;
private readonly WeatherForecastDbContext _store;
public PublishWeatherForecast(
IGenericChannelFactory factory,
WeatherForecastDbContext store)
{
_store = store;
_factory = factory;
}
public async Task Execute(IContext context)
{
var model = context.Data.DataGet<WeatherForecast>();
using (var channel = _factory.CreateChannel<DatabaseFacade, IDbContextTransaction>(_store.Database, context.Token))
{
_store.Entry(model).State = EntityState.Modified;
_store.SaveChangesAsync();
await context.Bus()
.Channel(channel)
.Token(context.Token)
.Topic("forecast.created")
.Data(model)
.Post();
await channel.Commit();
}
}
}
In the above example, a
IChannel
which creates a transaction to emit the forecast message if and only if the forecast was stored successfully via theCommit()
function.
A saga can be used for more exhaustive use cases:
var model = context.Data.DataGet<WeatherForecast>();
using (var channel = _factory.CreateChannel<DatabaseFacade, IDbContextTransaction>(_store.Database, context.Token))
{
...
await context.Saga<CreateWeatherForcastSaga>()
.Channel(channel)
.Data(model)
.Start();
await channel.Commit();
}
see CreateWeatherForcast for the full implementation sample
Getting Started
Default Services Configuration:
UseDefaultStore
andUseDefaultBroker
are in-memory services used in unit testing scenarios
services.AddPanama(
configuration: builder.Configuration,
setup: options => {
options.UseCanal(canal => {
canal.UseDefaultStore();
canal.UseDefaultBroker();
canal.UseDefaultScheduler();
});
});
NOTE: Panama Canal must have a store and atleast one broker service configured
MySql & RabbitMQ Services Configuration:
services.AddPanama(
configuration: builder.Configuration,
setup: options => {
options.UseCanal(canal => {
canal.UseMySqlStore();
canal.UseRabbitMq();
canal.UseDefaultScheduler();
});
});
appsettings.json
or environment variables can be for initial options configurations:
{
"ConnectionStrings": {
"MySql": [DB_CONNECTION]
},
"Logging": {
"LogLevel": {
"Default": "Debug"
}
},
"AllowedHosts": "*",
"Panama": {
"Canal": {
"Options": {
"Scope": [CLUSTER_NAMESPACE]
},
"Brokers": {
"RabbitMQ": {
"Options": {
"Port": [RABBIT_PORT],
"Host": [RABBIT_HOST],
"Username": [RABBIT_USERNAME],
"Password": [RABBIT_PASSWORD]
}
}
},
"Stores": {
"MySql": {
"Options": {
"Port": [DB_PORT],
"Host": [DB_HOST],
"Username": [DB_USERNAME],
"Password": [DB_PASSWORD],
"Database": [DB_NAME]
}
}
}
}
}
}
For inquiries and support, contact: Diran Ogunlana
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Microsoft.Extensions.Configuration.Binder (>= 6.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 6.0.0)
- Microsoft.Extensions.ObjectPool (>= 6.0.0)
- Newtonsoft.Json (>= 13.0.3)
- Panama (>= 6.0.1)
- Polly (>= 7.2.3)
- Quartz (>= 3.6.2)
- Quartz.Extensions.DependencyInjection (>= 3.6.2)
- Quartz.Extensions.Hosting (>= 3.6.2)
- Stateless (>= 5.13.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Panama.Canal:
Package | Downloads |
---|---|
Panama.Canal.MySQL
A MySql store implementation for the Panama Canal transactional messaging framework |
|
Panama.Canal.RabbitMQ
A RabbitMq broker implementation for the Panama Canal transactional messaging framework |
GitHub repositories
This package is not used by any popular GitHub repositories.
fixed saga descriptor bug when no saga is present