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
7 changes: 7 additions & 0 deletions src/SquidStd.Aws.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ client at that endpoint instead of the regional AWS endpoint.
|------------------|-----------------------------------------------------------------------------------|
| `AwsConfigEntry` | Region, optional credentials and an optional endpoint override (e.g. LocalStack). |

## Related

- Article: [AWS abstractions](https://tgiachi.github.io/squid-std/articles/aws-abstractions.html)
- [`SquidStd.Secrets.Aws`](https://tgiachi.github.io/squid-std/articles/secrets-aws.html) - AWS Secrets Manager provider using this config
- [`SquidStd.Messaging.Sqs`](https://tgiachi.github.io/squid-std/articles/messaging-sqs.html) - SQS messaging provider using this config
- [`SquidStd.Storage.S3`](https://tgiachi.github.io/squid-std/articles/storage-s3.html) - S3 storage provider using this config

## License

MIT - part of [SquidStd](https://github.com/tgiachi/squid-std).
89 changes: 76 additions & 13 deletions src/SquidStd.Caching/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<h1 align="center">SquidStd.Caching</h1>

In-memory backend for SquidStd.Caching. Provides an `IMemoryCache`-backed `ICacheProvider` with
absolute TTL and eviction, wired to the shared typed `ICacheService` facade - registered with a
single `AddInMemoryCache()` call. Ideal for single-process apps, tests, and local dev.
In-memory backend for SquidStd.Caching. Provides an `IMemoryCache`-backed `ICacheProvider` with absolute
TTL and eviction, wired to the shared typed `ICacheService` facade - registered with a single
`AddInMemoryCache()` call.

The contract lives in `SquidStd.Caching.Abstractions`, so code written against `ICacheService` moves
unchanged to a distributed cache: use this in-memory provider for single-process apps, tests, and local
dev, then swap in `SquidStd.Caching.Redis` for production. Values are serialized as JSON in both cases,
so cached shapes behave the same across providers.

## Install

Expand All @@ -12,29 +17,87 @@ dotnet add package SquidStd.Caching

## Usage

Register through the bootstrap - the provider is an `ISquidStdService`, so `StartAsync` / `StopAsync`
manage its lifecycle for you:

```csharp
using DryIoc;
using SquidStd.Caching.Abstractions.Interfaces;
using SquidStd.Caching.Extensions;
using SquidStd.Services.Core.Extensions;
using SquidStd.Services.Core.Services.Bootstrap;

var bootstrap = SquidStdBootstrap.Create(o => o.ConfigName = "myapp");
bootstrap.ConfigureServices(c => c.RegisterCoreServices().AddInMemoryCache());
await bootstrap.StartAsync();
```

### Get / set with TTL

```csharp
var cache = bootstrap.Resolve<ICacheService>();

await cache.SetAsync("greeting", "hello", TimeSpan.FromMinutes(5));
var value = await cache.GetAsync<string>("greeting"); // "hello", or default on a miss

var exists = await cache.ExistsAsync("greeting"); // true
var removed = await cache.RemoveAsync("greeting"); // true if the key existed
```

Omitting the TTL on `SetAsync` falls back to `CacheOptions.DefaultTtl` (no expiry when that is `null`).

var container = new Container();
container.AddInMemoryCache("memory://localhost?defaultTtlSeconds=300&keyPrefix=app:");
### Cache-aside with `GetOrSetAsync`

var cache = container.Resolve<ICacheService>();
await cache.SetAsync("user:1", new { Name = "squid" });
var user = await cache.GetAsync<object>("user:1");
`GetOrSetAsync` returns the cached value, or runs the factory, stores the result, and returns it on a
miss - the standard cache-aside pattern in one call:

```csharp
public sealed record UserProfile(string Id, string Name);

var profile = await cache.GetOrSetAsync(
$"user:{id}",
async ct => await LoadProfileFromDatabaseAsync(id, ct),
TimeSpan.FromMinutes(10)
);
```

## Configuration

`AddInMemoryCache` takes an optional `CacheOptions`. Options are code-only - this package registers no
YAML config section.

```csharp
c.AddInMemoryCache(new CacheOptions
{
DefaultTtl = TimeSpan.FromMinutes(5),
KeyPrefix = "app:"
});
```

| Property | Default | Purpose |
|--------------|----------------|------------------------------------------------------------------|
| `DefaultTtl` | `null` | TTL applied when a set call passes none; `null` means no expiry. |
| `KeyPrefix` | `string.Empty` | Prefix prepended to every key. |

A connection-string overload is also available:
`AddInMemoryCache("memory://local?defaultTtlSeconds=300&keyPrefix=app:")`.

`CacheMetricsProvider` is registered as `ICacheMetrics` / `IMetricProvider` and exposes aggregate hit,
miss, set, and remove counters plus the hit ratio to the metrics collection system.

## Key types

| Type | Purpose |
|-------------------------------|-----------------------------------------|
| `CacheRegistrationExtensions` | `AddInMemoryCache(...)` registration. |
| `InMemoryCacheProvider` | `IMemoryCache`-backed `ICacheProvider`. |
| Type | Purpose |
|-------------------------------|------------------------------------------------------------|
| `CacheRegistrationExtensions` | `AddInMemoryCache(...)` registration. |
| `InMemoryCacheProvider` | `IMemoryCache`-backed `ICacheProvider`. |
| `ICacheService` | Typed facade from `SquidStd.Caching.Abstractions`: get/set/TTL, `GetOrSetAsync`, exists/remove. |
| `CacheOptions` | Default TTL and key prefix configuration. |

## Related

- Article: [Caching](https://tgiachi.github.io/squid-std/articles/caching.html)
- Tutorial: [Caching](https://tgiachi.github.io/squid-std/tutorials/caching.html)
- Production backend: [SquidStd.Caching.Redis](https://tgiachi.github.io/squid-std/articles/caching-redis.html)

## License

Expand Down
87 changes: 75 additions & 12 deletions src/SquidStd.Messaging.RabbitMq/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<h1 align="center">SquidStd.Messaging.RabbitMq</h1>

RabbitMQ transport for SquidStd.Messaging. Implements `IQueueProvider` on top of the RabbitMQ client,
so the same `IMessageQueue` API publishes to and consumes from a real broker. Registered with a single
RabbitMQ transport for SquidStd.Messaging. Implements `IQueueProvider` and `ITopicProvider` on top of the
RabbitMQ client, so the same `IMessageQueue` / `IMessageTopic` API that runs in-memory during tests
publishes to and consumes from a real broker in production - registered with a single
`AddRabbitMqMessaging(...)` call.

The provider declares durable **quorum queues** and publishes persistent messages, so queued work survives
broker restarts. Each queue gets a companion dead-letter queue (`<queue>.dlq` by default), wired via
`x-delivery-limit` so the broker itself moves a message there after `MaxDeliveryAttempts` failed
deliveries. Connections are created with automatic recovery enabled, and consumers apply the configured
prefetch via basic QoS. Topics map to fan-out exchanges with an exclusive, auto-delete queue per
subscriber - topic delivery is live fan-out, not durable.

## Install

```bash
Expand All @@ -12,29 +20,84 @@ dotnet add package SquidStd.Messaging.RabbitMq

## Usage

Register through the bootstrap - the providers are `ISquidStdService`s, so `bootstrap.StartAsync()` opens
the broker connection and `StopAsync` closes it:

```csharp
using DryIoc;
using SquidStd.Messaging.Abstractions.Interfaces;
using SquidStd.Messaging.RabbitMq.Data.Config;
using SquidStd.Messaging.RabbitMq.Extensions;
using SquidStd.Services.Core.Extensions;
using SquidStd.Services.Core.Services.Bootstrap;

var bootstrap = SquidStdBootstrap.Create(o => o.ConfigName = "myapp");

bootstrap.ConfigureServices(c => c.RegisterCoreServices().AddRabbitMqMessaging(new RabbitMqOptions
{
HostName = "rabbit.internal",
UserName = "app",
Password = "secret"
}));

await bootstrap.StartAsync();
```

Publishing and consuming are identical to the in-memory provider - the contracts come from
`SquidStd.Messaging.Abstractions`:

var container = new Container();
container.AddRabbitMqMessaging("rabbitmq://guest:guest@localhost:5672/");
```csharp
var queue = bootstrap.Resolve<IMessageQueue>();

using (queue.Subscribe("orders", new OrderListener())) // IQueueMessageListenerAsync<OrderPlaced>
{
await queue.PublishAsync("orders", new OrderPlaced("order-1"));
}

var queue = container.Resolve<IMessageQueue>();
await queue.PublishAsync("orders", new { Id = 1 });
var topic = bootstrap.Resolve<IMessageTopic>();
using var sub = topic.Subscribe<OrderPlaced>("order-events", (order, _) =>
{
Console.WriteLine($"topic saw {order.Id}");
return Task.CompletedTask;
});
```

## Configuration

`AddRabbitMqMessaging(RabbitMqOptions options, MessagingOptions? messagingOptions = null)` takes the
connection settings plus the shared messaging options (delivery attempts, dead-letter suffix). Options are
code-only - this package registers no YAML config section.

| `RabbitMqOptions` property | Default | Purpose |
|----------------------------|---------------|-----------------------------------------------------|
| `HostName` | `"localhost"` | Broker host. |
| `Port` | `5672` | Broker port. |
| `VirtualHost` | `"/"` | Virtual host. |
| `UserName` | `"guest"` | User name. |
| `Password` | `"guest"` | Password. |
| `Uri` | `null` | AMQP URI; when set it overrides the fields above. |
| `PrefetchCount` | `10` | Consumer prefetch (basic QoS) per subscriber. |

A connection-string overload is also available:
`AddRabbitMqMessaging("rabbitmq://app:secret@rabbit.internal:5672/myvhost?prefetch=20&maxDeliveryAttempts=5")`.

`MessagingMetricsProvider` is registered as `IMessagingMetrics` / `IMetricProvider`; the provider reports
published, delivered, and failed counts plus subscriber counts per queue.

## Key types

| Type | Purpose |
|-------------------------------------------|-------------------------------------------|
| `RabbitMqMessagingRegistrationExtensions` | `AddRabbitMqMessaging(...)` registration. |
| `RabbitMqQueueProvider` | RabbitMQ-backed `IQueueProvider`. |
| `RabbitMqOptions` | Connection + prefetch configuration. |
| Type | Purpose |
|-------------------------------------------|----------------------------------------------------------|
| `RabbitMqMessagingRegistrationExtensions` | `AddRabbitMqMessaging(...)` registration. |
| `RabbitMqQueueProvider` | Quorum-queue `IQueueProvider` with broker-side DLQ. |
| `RabbitMqTopicProvider` | Fan-out exchange `ITopicProvider`. |
| `RabbitMqOptions` | Connection + prefetch configuration. |

## Related

- Article: [Messaging with RabbitMQ](https://tgiachi.github.io/squid-std/articles/messaging-rabbitmq.html)
- Article: [Messaging](https://tgiachi.github.io/squid-std/articles/messaging.html)
- Tutorial: [Messaging](https://tgiachi.github.io/squid-std/tutorials/messaging.html)
- Siblings: [SquidStd.Messaging (in-memory)](https://tgiachi.github.io/squid-std/articles/messaging.html), [SquidStd.Messaging.Sqs](https://tgiachi.github.io/squid-std/articles/messaging-sqs.html)

## License

Expand Down
111 changes: 99 additions & 12 deletions src/SquidStd.Messaging/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
<h1 align="center">SquidStd.Messaging</h1>

In-memory transport for SquidStd.Messaging. Provides a channel-backed `IQueueProvider` with per-queue
buffering, round-robin delivery to subscribers, retry/dead-letter handling, and metrics - registered
with a single `AddInMemoryMessaging()` call. Ideal for single-process apps, tests, and local dev.
In-memory transport for SquidStd.Messaging. Provides channel-backed `IQueueProvider` and `ITopicProvider`
implementations behind the shared `IMessageQueue` / `IMessageTopic` facades: per-queue buffering,
round-robin (competing-consumers) delivery, retry and dead-letter handling, topic fan-out, and metrics -
all registered with a single `AddInMemoryMessaging()` call.

Queues deliver each message to exactly one subscriber; topics fan every message out to all subscribers.
The contracts live in `SquidStd.Messaging.Abstractions`, so code written against `IMessageQueue` and
`IMessageTopic` moves unchanged to a real broker: use this in-memory provider for single-process apps,
tests, and local dev, then swap in `SquidStd.Messaging.RabbitMq` or `SquidStd.Messaging.Sqs` for
production.

## Install

Expand All @@ -12,28 +19,108 @@ dotnet add package SquidStd.Messaging

## Usage

Register through the bootstrap - the providers are `ISquidStdService`s, so `StartAsync` / `StopAsync`
manage their lifecycle for you:

```csharp
using DryIoc;
using SquidStd.Messaging.Abstractions.Interfaces;
using SquidStd.Messaging.Extensions;
using SquidStd.Services.Core.Extensions;
using SquidStd.Services.Core.Services.Bootstrap;

var bootstrap = SquidStdBootstrap.Create(o => o.ConfigName = "myapp");
bootstrap.ConfigureServices(c => c.RegisterCoreServices().AddInMemoryMessaging());
await bootstrap.StartAsync();
```

### Queues (competing consumers)

Each queue message goes to exactly one subscriber. Implement a listener and subscribe it; dispose the
subscription to unsubscribe.

```csharp
public sealed record OrderPlaced(string Id);

public sealed class OrderListener : IQueueMessageListenerAsync<OrderPlaced>
{
public Task HandleAsync(OrderPlaced message, CancellationToken cancellationToken)
{
Console.WriteLine($"queue handled {message.Id}");
return Task.CompletedTask;
}
}

var queue = bootstrap.Resolve<IMessageQueue>();

using (queue.Subscribe("orders", new OrderListener()))
{
await queue.PublishAsync("orders", new OrderPlaced("order-1"));
}
```

var container = new Container();
container.AddInMemoryMessaging();
A synchronous `IQueueMessageListener<TMessage>` overload of `Subscribe` is also available.

var queue = container.Resolve<IMessageQueue>();
await queue.PublishAsync("orders", new { Id = 1 });
### Topics (fan-out)

Every subscriber receives every message published to the topic.

```csharp
var topic = bootstrap.Resolve<IMessageTopic>();

using (topic.Subscribe<OrderPlaced>("order-events", (order, _) =>
{
Console.WriteLine($"topic saw {order.Id}");
return Task.CompletedTask;
}))
{
await topic.PublishAsync("order-events", new OrderPlaced("order-2"));
}
```

## Configuration

`AddInMemoryMessaging` takes an optional `MessagingOptions`. Options are code-only - this package
registers no YAML config section.

```csharp
c.AddInMemoryMessaging(new MessagingOptions
{
MaxDeliveryAttempts = 5,
DeadLetterQueueSuffix = ".dead",
RetryDelay = TimeSpan.FromMilliseconds(250)
});
```

| Property | Default | Purpose |
|-------------------------|---------|-------------------------------------------------------------|
| `MaxDeliveryAttempts` | `3` | Delivery attempts before a message is dead-lettered. |
| `DeadLetterQueueSuffix` | `".dlq"`| Suffix appended to a queue name to form its dead-letter queue. |
| `RetryDelay` | `Zero` | Delay before a failed message is re-enqueued. |

A connection-string overload is also available:
`AddInMemoryMessaging("memory://local?maxDeliveryAttempts=5&retryDelayMs=250&deadLetterSuffix=.dead")`.

When a listener throws, the message is re-enqueued (after `RetryDelay`) until `MaxDeliveryAttempts` is
reached, then moved to `<queue><DeadLetterQueueSuffix>` - subscribe to that queue to inspect failures.
`MessagingMetricsProvider` is registered as `IMessagingMetrics` / `IMetricProvider` and tracks published,
delivered, retried, failed, and dead-lettered counts per queue.

## Key types

| Type | Purpose |
|-----------------------------------|-------------------------------------------|
| `MessagingRegistrationExtensions` | `AddInMemoryMessaging(...)` registration. |
| `InMemoryQueueProvider` | Channel-based in-memory `IQueueProvider`. |
| Type | Purpose |
|-----------------------------------|------------------------------------------------------------|
| `MessagingRegistrationExtensions` | `AddInMemoryMessaging(...)` registration. |
| `InMemoryQueueProvider` | Channel-based in-memory `IQueueProvider` with retry/DLQ. |
| `InMemoryTopicProvider` | In-memory `ITopicProvider` (fan-out). |
| `IMessageQueue` / `IMessageTopic` | Typed facades from `SquidStd.Messaging.Abstractions`. |
| `MessagingOptions` | Retry, dead-letter, and delivery-attempt configuration. |

## Related

- Article: [Messaging](https://tgiachi.github.io/squid-std/articles/messaging.html)
- Article: [Messaging abstractions](https://tgiachi.github.io/squid-std/articles/messaging-abstractions.html)
- Tutorial: [Messaging](https://tgiachi.github.io/squid-std/tutorials/messaging.html)
- Production transports: [SquidStd.Messaging.RabbitMq](https://tgiachi.github.io/squid-std/articles/messaging-rabbitmq.html), [SquidStd.Messaging.Sqs](https://tgiachi.github.io/squid-std/articles/messaging-sqs.html)

## License

Expand Down
7 changes: 7 additions & 0 deletions src/SquidStd.Persistence.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ dotnet add package SquidStd.Persistence.Abstractions
| `JournalEntityOperationType` | `Upsert` / `Remove` journal operation discriminator. |
| `SnapshotSaveStartedEvent` / `SnapshotSaveCompletedEvent` | Snapshot lifecycle events (`IEvent`). |

## Related

- Article: [Persistence abstractions](https://tgiachi.github.io/squid-std/articles/persistence-abstractions.html)
- Tutorial: [Persistence](https://tgiachi.github.io/squid-std/tutorials/persistence.html)
- [`SquidStd.Persistence`](https://tgiachi.github.io/squid-std/articles/persistence.html) - the binary persistence engine implementing these contracts
- [`SquidStd.Persistence.MessagePack`](https://tgiachi.github.io/squid-std/articles/persistence-messagepack.html) - MessagePack entity descriptors for the engine

## License

MIT - part of [SquidStd](https://github.com/tgiachi/squid-std).
Loading
Loading