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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Do not treat FeatureFusion as a framework. Install a BuildingBlock only when it
| `BuildingBlocks.Mediator` | CQRS Send + ordered pipeline | [Mediator](src/BuildingBlocks/Mediator/AGENTS.md) |
| `BuildingBlocks.Pagination.EntityFrameworkCore` | Keyset (cursor) paging for EF Core (**1.1.0**: any-width Npgsql row comparison, NULLS, `HasKeysetIndex` + `NullOrder`) | [Pagination.EF](src/BuildingBlocks/Pagination.EntityFrameworkCore/AGENTS.md) |
| `BuildingBlocks.Mcp` | Same logic as HTTP as deny-by-default MCP tools | [Mcp](src/BuildingBlocks/Mcp/AGENTS.md) |
| `BuildingBlocks.Idempotency` | HTTP Idempotency-Key + optional Redis lock | [Idempotency](src/BuildingBlocks/Idempotency/AGENTS.md) |
| `BuildingBlocks.Idempotency` | HTTP Idempotency-Key + optional Redis lock (**1.0.1**: System.Text.Json, package icon) | [Idempotency](src/BuildingBlocks/Idempotency/AGENTS.md) |
| `BuildingBlocks.Telemetry` | Config-driven OpenTelemetry (not a SigNoz SDK) | [Telemetry](src/BuildingBlocks/Telemetry/AGENTS.md) |
| `BuildingBlocks.Aspire.Hosting.SigNoz` | Local AppHost SigNoz only (net10) | [SigNoz](src/BuildingBlocks/Aspire.Hosting.SigNoz/AGENTS.md) |

Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- No hard PackageReference on Npgsql in the nupkg; row comparison requires the host already using Npgsql.EntityFrameworkCore.PostgreSQL.
- Lab: `NameThenPrice` product sort (3-column keyset) + `AddBuildingBlocksPagination` / `UseBuildingBlocksPagination` on FeatureFusion.

## BuildingBlocks.Idempotency [1.0.1] - 2026-09-04

### Changed

- **System.Text.Json** for cache envelope and MVC `ObjectResult` body capture (dropped `Newtonsoft.Json`). Minimal API path already used STJ. Default property names remain PascalCase.
- **Package icon** (`idempotency-icon.png`) for NuGet listing parity with other BuildingBlocks.

### Notes

- Cache JSON written by 1.0.0 (Newtonsoft) and 1.0.1 (STJ) are compatible for `IdempotencyCacheEntry` under default options; clear cache if you see odd replay after upgrading mid-flight.
- Package still depends on `Ulid` and `StackExchange.Redis` (Redis lock); hosts that use the lock must register `IConnectionMultiplexer`.

## BuildingBlocks.Idempotency [1.0.0] - 2026-09-04

### Added
Expand All @@ -45,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Notes

- Distinct from MCP write idempotency (`UseMemoryIdempotency` / `IMcpIdempotencyStore` in BuildingBlocks.Mcp)
- Package depends on `Newtonsoft.Json` (MVC ObjectResult capture), `Ulid`, and `StackExchange.Redis` (Redis lock); hosts that use the lock must register `IConnectionMultiplexer`
- Package depended on `Newtonsoft.Json` (MVC ObjectResult capture), `Ulid`, and `StackExchange.Redis` (Redis lock); hosts that use the lock must register `IConnectionMultiplexer`

## BuildingBlocks.Pagination.EntityFrameworkCore [1.0.0] - 2026-08-29

Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ Release tags (must match the project `<Version>`): `mediator-v*`, `mcp-v*`, `ide

- HTTP `Idempotency-Key` only — do not fold MCP `IMcpIdempotencyStore` into this package.
- Host owns `IDistributedCache` (and `IConnectionMultiplexer` when using the Redis lock).
- Prefer **System.Text.Json** for cache / MVC body capture (no Newtonsoft.Json).
- Lab provenance: Experiments 3 / 4 / 12 under `tests/Lab/IntegrationTests/Experiments/`.
- Release tag: `idempotency-v*` must match `<Version>` (current **1.0.1**).

### BuildingBlocks.Telemetry / SigNoz hosting

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ NuGet packages you can install in **your** hosts. The FeatureFusion API is a sho
|---------|---------|------|------|
| **[BuildingBlocks.Mediator](https://www.nuget.org/packages/BuildingBlocks.Mediator)** | **1.1.0** | CQRS **Send** + ordered pipeline (`ICommand` / `IQuery`, typed behaviors, opt-in traces + metrics) | net8 / net9 / net10 |
| **[BuildingBlocks.Mcp](https://www.nuget.org/packages/BuildingBlocks.Mcp)** | **1.0.0** | Message types → MCP tools on the official SDK (deny-by-default, `McpResult`, HTTP + opt-in stdio) | net8 / net9 / net10 |
| **[BuildingBlocks.Idempotency](https://www.nuget.org/packages/BuildingBlocks.Idempotency)** | **1.0.0** | HTTP **Idempotency-Key** — MVC + Minimal API, 2xx envelope replay, ProblemDetails, optional Redis lock, fingerprint, ActivitySource | net8 / net9 / net10 |
| **[BuildingBlocks.Idempotency](https://www.nuget.org/packages/BuildingBlocks.Idempotency)** | **1.0.1** | HTTP **Idempotency-Key** — MVC + Minimal API, 2xx envelope replay, ProblemDetails, optional Redis lock, fingerprint, ActivitySource | net8 / net9 / net10 |
| **[BuildingBlocks.Pagination.EntityFrameworkCore](https://www.nuget.org/packages/BuildingBlocks.Pagination.EntityFrameworkCore)** | **1.1.0** | Typed keyset (cursor) pagination for EF Core (IR bundled): any-width Npgsql row comparison, `NULLS FIRST/LAST`, `HasKeysetIndex` + `NullOrder` | net8 / net9 / net10 |
| **[BuildingBlocks.Telemetry](https://www.nuget.org/packages/BuildingBlocks.Telemetry)** | **1.0.2** | Config-driven OpenTelemetry (traces, metrics, logs) + `IntegrateMediator` / opt-in `IntegrateMcp` | net8 / net9 / net10 |
| **[BuildingBlocks.Aspire.Hosting.SigNoz](https://www.nuget.org/packages/BuildingBlocks.Aspire.Hosting.SigNoz)** | **1.0.0** | Local-dev Aspire `AddSigNoz()` + `WithSigNozOtlpExporter` | net10 (AppHost) |
Expand Down Expand Up @@ -362,7 +362,7 @@ Cursor HTTP:

ASP.NET Core HTTP **Idempotency-Key** for MVC and Minimal API. Host-owned `IDistributedCache`, **2xx** envelope replay, ProblemDetails on conflicts, optional Redis SET NX lock, opt-in method/path/body fingerprint, per-endpoint TTL, optional ActivitySource. Distinct from MCP write idempotency (`UseMemoryIdempotency` / `IMcpIdempotencyStore` above).

**What's new in 1.0.0:** shared `IdempotencyGate` for MVC + Minimal API, 2xx envelope replay, ProblemDetails, optional Redis lock / fingerprint / telemetry. NuGet and release badges resolve after `idempotency-v1.0.0` is tagged and published.
**What's new in 1.0.1:** NuGet package icon; **System.Text.Json** for cache envelope and MVC `ObjectResult` capture (dropped Newtonsoft.Json). No API surface change from 1.0.0.

```bash
dotnet add package BuildingBlocks.Idempotency
Expand Down Expand Up @@ -718,7 +718,7 @@ Install the packages above in your own hosts, **or** clone this repo and run **F
| Telemetry | **`BuildingBlocks.Telemetry`** in ServiceDefaults; **`BuildingBlocks.Aspire.Hosting.SigNoz`** on AppHost |
| Event bus | RabbitMQ + transactional outbox/inbox, DLQ, dedup hooks |
| Aspire lab | AppHost orchestration for Postgres, Redis, RabbitMQ, Memcached, SigNoz |
| HTTP idempotency | **`BuildingBlocks.Idempotency`** — MVC + Minimal API, 2xx envelope replay, optional Redis lock (`POST /api/v2/Order/order`) |
| HTTP idempotency | **`BuildingBlocks.Idempotency` 1.0.1** — MVC + Minimal API, 2xx envelope replay, System.Text.Json, optional Redis lock (`POST /api/v2/Order/order`) |
| Feature flags (demo) | ASP.NET Core Feature Management + custom filters (claims / VIP) |
| API surface | Versioned controllers + Minimal APIs, FluentValidation patterns |
| Gateway | YARP reverse proxy + Memcached distributed rate limiting |
Expand Down Expand Up @@ -943,7 +943,7 @@ Conditional features via Microsoft.FeatureManagement and custom filters (e.g. VI

### HTTP idempotency (BuildingBlocks.Idempotency)

REST idempotency with `IDistributedCache` status tracking, MVC `[Idempotent]` / Minimal API `WithIdempotency`, and optional Redis lock (`POST /api/v2/Order/order`, smoke `POST /api/v2/idempotency-smoke`). See [BuildingBlocks.Idempotency](#buildingblocksidempotency).
REST idempotency with `IDistributedCache` status tracking, MVC `[Idempotent]` / Minimal API `WithIdempotency`, optional Redis lock, and **System.Text.Json** cache/body serialization (`POST /api/v2/Order/order`, smoke `POST /api/v2/idempotency-smoke`). Package **1.0.1**. See [BuildingBlocks.Idempotency](#buildingblocksidempotency).

- [Idempotency with CQRS](https://www.linkedin.com/feed/update/urn:li:activity:7303686809891356676/)
- [IdempotentFusion project](https://www.linkedin.com/feed/update/urn:li:activity:7309149985307029504/) (historical Lab name)
Expand Down Expand Up @@ -986,7 +986,7 @@ See [Pagination showcase](#pagination-showcase) for the FeatureFusion catalog (`
| **Polling publisher** | `OutBoxWorker` background poll → publish |
| **Dead letter queue** | EventBus DLX / DLQ topology |
| **Message deduplication** | Inbox + `MessageDeduplicationService` |
| **Idempotency** | `BuildingBlocks.Idempotency` — `[Idempotent]` / `WithIdempotency`, cache envelope, optional Redis lock |
| **Idempotency** | `BuildingBlocks.Idempotency` **1.0.1** — `[Idempotent]` / `WithIdempotency`, System.Text.Json cache envelope, optional Redis lock |
| **Feature toggle** | ASP.NET Core Feature Management + custom filters |
| **Rate limiting** | ApiGateway Memcached fixed-window limiter |
| **Circuit breaker / resilience** | Polly `ResiliencePipelineFactory` |
Expand Down
10 changes: 9 additions & 1 deletion docs/building-blocks/idempotency.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

ASP.NET Core HTTP **Idempotency-Key** for MVC and Minimal API: host-owned `IDistributedCache`, optional Redis lock, opt-in fingerprint, ProblemDetails on conflicts, optional ActivitySource.

Canonical consumer docs: [package README](../../src/BuildingBlocks/Idempotency/PACKAGE_README.md). Agent notes: [AGENTS.md](../../src/BuildingBlocks/Idempotency/AGENTS.md).
[![NuGet](https://img.shields.io/nuget/v/BuildingBlocks.Idempotency.svg)](https://www.nuget.org/packages/BuildingBlocks.Idempotency)
[![GitHub Release](https://img.shields.io/github/v/release/Maxofpower/FeatureFusion?filter=idempotency-v*&logo=github&label=GitHub%20Release)](https://github.com/Maxofpower/FeatureFusion/releases?q=idempotency-v)

**Current package version: 1.0.1.** Canonical consumer docs: [package README](../../src/BuildingBlocks/Idempotency/PACKAGE_README.md). Agent notes: [AGENTS.md](../../src/BuildingBlocks/Idempotency/AGENTS.md).

## What's new in 1.0.1

- NuGet package icon
- **System.Text.Json** for cache envelope and MVC `ObjectResult` capture (no Newtonsoft.Json). No public API change from 1.0.0.

## Install

Expand Down
3 changes: 3 additions & 0 deletions docs/building-blocks/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Typed keyset (cursor) pagination. **One NuGet:** `BuildingBlocks.Pagination.EntityFrameworkCore` **1.1.0** (IR + EF Core adapter). Dapper is an in-repo project, not a package.

[![NuGet](https://img.shields.io/nuget/v/BuildingBlocks.Pagination.EntityFrameworkCore.svg)](https://www.nuget.org/packages/BuildingBlocks.Pagination.EntityFrameworkCore)
[![GitHub Release](https://img.shields.io/github/v/release/Maxofpower/FeatureFusion?filter=pagination-v*&logo=github&label=GitHub%20Release)](https://github.com/Maxofpower/FeatureFusion/releases?q=pagination-v)

See ADR [0003](../adr/0003-pagination-keyset.md), the [test matrix](PAGINATION_TEST_MATRIX.md), and [`PACKAGE_README.md`](../../src/BuildingBlocks/Pagination.EntityFrameworkCore/PACKAGE_README.md).

## Install
Expand Down
4 changes: 2 additions & 2 deletions docs/lab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Aspire: Postgres │ Redis │ RabbitMQ │ Memcached │ SigNoz
| Family | Experiments | Outcome |
|--------|-------------|---------|
| Keyset pagination abuse | 1–2 | Careless HTTP/MCP cursor clients characterized |
| HTTP Redis idempotency | 3, 4, 12, 15 | Miss/hit, concurrency, fingerprint, ProcessingTtl lease overlap → **BuildingBlocks.Idempotency 1.0.0** |
| HTTP Redis idempotency | 3, 4, 12, 15 | Miss/hit, concurrency, fingerprint, ProcessingTtl lease overlap → **BuildingBlocks.Idempotency 1.0.1** |
| MCP write / agent-client semantics | 6, 13, 14, 16 | Confirm+replay, regenerated keys amplify, concurrent same-key safe, **IMcpRateLimiter** bounds distinct-key storms |
| Outbox → bus → inbox happy path | 5, 8, 10 | HTTP + MCP parity; outbox row lifecycle |
| Consumer dedup / failure | 7, 9, 11, 17 | Inbox dedup; retry/DLQ; `EnableDeduplication` + `processed_messages` |
Expand All @@ -74,7 +74,7 @@ Do **not** extract: Scenario DSL, one-off test gates, fixed-permit rate limiters

## Package / versioning (current)

- Each packable project owns `<Version>` in its `.csproj` (e.g. `BuildingBlocks.Pagination.EntityFrameworkCore` `1.1.0`, `BuildingBlocks.Mcp` `1.0.0`).
- Each packable project owns `<Version>` in its `.csproj` (e.g. `BuildingBlocks.Pagination.EntityFrameworkCore` `1.1.0`, `BuildingBlocks.Idempotency` `1.0.1`, `BuildingBlocks.Mcp` `1.0.0`).
- Release tags are package-prefixed (`mcp-v*`, `mediator-v*`, `telemetry-v*`, …) and must match `<Version>`.
- Per-package GitHub Actions: `*-yml` CI + `*-release.yml` pack / Trusted Publishing to nuget.org (`idempotency.yml` / `idempotency-release.yml` for this package).
- IntegrationTests / Exp 1–18 are **not** in CI (local Aspire/Docker).
Expand Down
2 changes: 1 addition & 1 deletion docs/linkedin-posts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Umbrella narrative (lab → packages): [Medium](https://medium.com/@m2hweb86/fea
| `cursor-pagination` | Reusable generic bidirectional keyset (cursor) pagination | published | https://www.linkedin.com/feed/update/urn:li:activity:7325068550614708225/ | `src/BuildingBlocks/Pagination.EntityFrameworkCore` + lab `ProductService` | `building-blocks-overview` | Typed `SortKey`, opaque cursors, one EF Core nupkg (**1.1.0**: Npgsql row comparison, NULLS interceptor). Optional `QueryHint` (default none). Lab: MVC `POST /api/v2/Product/products`, Minimal API `GET /api/v2/products-page` (POST kept), Dapper `products-dapper`, MCP `products.list` — one `GetProductsQuery`. |
| `mcp-message-tools` | Message types as MCP tools | published | https://lnkd.in/p/e4-FrM44 | `src/BuildingBlocks/Mcp` + `docs/building-blocks/mcp.md` | `building-blocks-overview` | Deny-by-default `[McpTool]` / `WithMcp` / `MapTool` on the official SDK. Same logic as HTTP. Interim permalink is the umbrella post until a dedicated MCP post ships. Not OpenAPI, not a SOLID linter. |
| `idempotency-cqrs` | Idempotency with CQRS commands | published | https://www.linkedin.com/feed/update/urn:li:activity:7303686809891356676/ | (pattern lab) | `idempotentfusion` | **Lab only, not NuGet.** Command-level idempotency via reusable `IdentifiedCommand<T,R>` + ULID `Idempotency-Key`, intercepting before handlers. Complementary to Mediator — not core v1. |
| `idempotentfusion` | IdempotentFusion (REST API) | published | https://www.linkedin.com/feed/update/urn:li:activity:7309149985307029504/ | `src/BuildingBlocks/Idempotency` + Lab `OrderController` | `idempotency-cqrs` | HTTP Idempotency-Key via `BuildingBlocks.Idempotency` (Redis cache host-owned, optional SET NX lock). Lab sets `UserIdFallback = "123"`. |
| `idempotentfusion` | IdempotentFusion (REST API) | published | https://www.linkedin.com/feed/update/urn:li:activity:7309149985307029504/ | `src/BuildingBlocks/Idempotency` + Lab `OrderController` | `idempotency-cqrs` | HTTP Idempotency-Key via `BuildingBlocks.Idempotency` **1.0.1** (Redis cache host-owned, optional SET NX lock, System.Text.Json). Lab sets `UserIdFallback = "123"`. |
| `eventbus-outbox-inbox` | Lightweight modular EventBus (outbox, inbox, DLQ) | published | https://www.linkedin.com/posts/mhhoseini_a-lightweight-modular-eventbus-implementation-activity-7316381126535663631-6FiI | `src/Lab/EventBus` | `aspire-integration-testing` | **Lab only, not NuGet.** Sibling of Mediator Send — not `INotification`. |
| `aspire-integration-testing` | Integration testing microservices with Aspire | published | https://www.linkedin.com/posts/mhhoseini_integration-testing-microservices-with-aspire-activity-7321088554565029888-Z0a8 | `tests/Lab/IntegrationTests` + `src/Lab/FeatureFusion.AppHost` | `building-blocks-overview` | **Lab only, not NuGet.** Aspire-hosted fixture against real Postgres / Redis / RabbitMQ / Memcached. |
| `ddd-architecture` | Diving into DDD | published | https://www.linkedin.com/posts/mhhoseini_diving-into-ddd-activity-7427983383126896640-Eaaw | lab vertical slices under `src/Lab/FeatureFusion/Features` | `medium-engineering-lab` | **Lab only, not NuGet.** Architecture notes; FeatureFusion is the showcase host, not a DDD framework. |
Expand Down
Loading
Loading