Deterministic Architecture for Event-Driven .NET Microservices
- Overview
- Why Franz?
- Subpackages
- Security Principles
- Architecture Overview
- Runtime Request Lifecycle
- Messaging Flow
- Ecosystem Map
- Architecture Enforcement
- Key Features
- Getting Started
- Build & Test
- Changelog
- Roadmap
- Enterprise Adoption & Support
- Contributing
- License
Franz.Common is the foundation of the Franz Framework, a deterministic, factory-driven, AOT-first architecture layer for building event-driven microservices in .NET 10.
It eliminates boilerplate, enforces architectural correctness, and provides DDD, CQRS, messaging, multi-tenancy, resilience, observability, and identity capabilities — designed for scalable, long-lived enterprise systems.
Franz is Kafka-first, but also supports RabbitMQ, Azure Service Bus, MongoDB, CosmosDB, SQL, and more.
Spring Boot for .NET — but deterministic, clean, and transparent.
Franz was created to bring predictability, maintainability, and governance to distributed .NET systems:
- Reduces 80%+ of architectural boilerplate.
- Enforces structural correctness at build time, not at code review.
- Provides consistent architecture across microservices — swap SQL Server for Postgres, Kafka for RabbitMQ, with zero domain changes.
- Offers first-class resilience, observability, and messaging patterns out of the box.
- Minimizes cognitive load through unified abstractions.
- Designed for enterprise requirements (multi-tenancy, identity, auditability).
Franz isn't a library collection — it's a platform with guarantees. The architecture you get from following the pattern is the architecture you'd get from a senior architect reviewing every PR, except enforced automatically at build time.
Franz follows a "batteries-included but modular" philosophy.
Franz.Common→ Core primitives, serialization, DI, functional utilities.
Franz.Common.Business→ DDD aggregates, domain events, factories, pipelines.Franz.Common.Mediator→ Lightweight CQRS mediator with pipelines.
Franz.Common.EntityFramework→ Auditing, soft deletes, domain event dispatching.Franz.Common.MongoDB→ Mongo outbox/inbox.Franz.Common.AzureCosmosDB→ Cosmos outbox/inbox.
Franz.Common.Messaging→ Messaging contracts, envelopes, options.Franz.Common.Messaging.Hosting→ Hosted async listeners.Franz.Common.Messaging.KafkaFranz.Common.Messaging.RabbitMQ
Franz.Common.Http.BootstrapFranz.Common.Http.RefitFranz.Common.Http.IdentityFranz.Common.Http.Messaging
Franz.Common.IdentityFranz.Common.SSO→ Keycloak, OIDC, SAML2, WS-Fed integrations.
Franz enforces strict, deterministic security patterns:
- Mandatory CorrelationId, TraceId, and TenantId propagation.
- Deterministic error filters (no sensitive data leakage).
- Centralized authentication & claims enrichment pipelines.
- Optional strict mode:
- no unregistered controllers
- no unregistered message handlers
- validation-first execution
- Standardized identity flows across OIDC, SAML2, Keycloak, WS-Fed.
These principles make Franz suitable for regulated environments, including public institutions and financial sectors.
flowchart TD
subgraph API Layer
A[HTTP Request] --> B[Franz.Http Pipeline]
B --> C[Correlation + Validation + Error Handling]
C --> D[Controller / Minimal API]
end
subgraph Application Layer
D --> E[Franz.Mediator]
E --> F[Command / Query Handlers]
F --> G[Domain Logic]
end
subgraph Infrastructure Layer
G --> H[(Database)]
G --> I[[Kafka Producer]]
G --> J[[RabbitMQ Producer]]
end
subgraph Messaging Layer
I --> K[[Kafka Broker]]
J --> L[[RabbitMQ Broker]]
K --> M[[Kafka Consumer]]
L --> N[[Rabbit Consumer]]
M --> E
N --> E
end
sequenceDiagram
participant Client
participant API as Franz.Http
participant Mediator as Franz.Mediator
participant Handler
participant Infra as DB / Messaging
Client->>API: HTTP Request
API->>API: Correlation + Validation + Error Filter
API->>Mediator: Dispatch(Request)
Mediator->>Handler: Execute Handler
Handler->>Infra: Query DB / Publish Event
Infra-->>Handler: Response / Ack
Handler-->>Mediator: Result
Mediator-->>API: Standardized Response
API-->>Client: HTTP 200 / 400 / 500
flowchart LR
subgraph Application
A[Command Handler] --> B[Domain Event]
B --> C[(Outbox Store)]
C --> D[Outbox Dispatcher]
end
D -->|Publish| E[(Kafka Broker)]
E --> F[Consumer Service]
F --> G[Message Handler]
G --> H[(Database)]
flowchart LR
Core[Franz.Common]
Business[Business Layer]
Mediator[Mediator]
HttpBoot[Http Bootstrap]
Refit[Refit Integration]
Identity[Identity + SSO]
subgraph Messaging
MsgCore[Messaging Core]
MsgHost[Messaging Hosting]
Kafka[Kafka Integration]
Rabbit[RabbitMQ Integration]
end
subgraph Persistence
EF[EF Core Extensions]
Mongo[MongoDB Outbox]
Cosmos[CosmosDB Outbox]
end
Core --> Business
Core --> Mediator
Core --> HttpBoot
Core --> MsgCore
Core --> Identity
Business --> EF
Mediator --> HttpBoot
HttpBoot --> Refit
MsgCore --> MsgHost
MsgHost --> Kafka
MsgHost --> Rabbit
Core --> Mongo
Core --> Cosmos
Franz includes an optional architecture test suite based on ArchUnitNET — internally referred to as the Franz Tribunal:
- Enforces layer boundaries (Domain → Application → Infrastructure).
- Forbids circular dependencies.
- Enforces immutable DTOs.
- Validates naming conventions: Commands, Queries, Events, Handlers, Controllers.
- Ensures no domain leakage into infrastructure and vice-versa.
- Ensures messaging boundaries are respected.
These rules run in CI on every pull request. A violation fails the build — not a code review comment, a hard stop. This makes Franz suitable for large organizations, where maintaining architectural discipline across many contributors and services is critical, not optional.
Entities, value objects, aggregates, events — all factory-controlled, identity-safe, and persistence-agnostic.
EntityFactory<TKey, TEntity> and AggregateFactory<TAggregate, TEvent> use compiled expression tree delegates cached statically per closed generic type. Constructor resolution happens once at startup — runtime creation is near-native with zero reflection overhead. Misconfigured types are caught at DI registration time via Validate(), not at first use.
FranzMapper detects value objects structurally via base-class inheritance — no [Attribute] decoration required across your domain. Supports immutable records, init-only properties, nested object graphs with true circular-reference detection, and full collection coercion (arrays, HashSet<T>, IReadOnlyList<T>). Zero external mapping library dependency.
Logging, validation, telemetry, resilience, transactions — composable, opt-in, zero hidden middleware.
Outbox/inbox, retries, DLQ, correlation propagation. Supports both service-level topic routing and event-level topic routing — register a Kafka topic per service, or per individual domain event, depending on your consumption pattern.
Serilog, OpenTelemetry, structured logs, mandatory correlation propagation across HTTP, messaging, and pipelines.
Tenant resolution across HTTP, messaging, pipelines.
SQL Server, Postgres, Oracle, MariaDB, MongoDB, CosmosDB — all behind unified abstractions. Swapping a provider is a registration change, not a rewrite.
dotnet add package Franz.Common --version 2.2.15Messaging example:
dotnet add package Franz.Common.Messaging.KafkaMinimal Program.cs wiring:
builder.Host.UseLog();
builder.Services
.AddFranzSerilogAuditPipeline()
.AddFranzEventValidationPipeline()
.AddFranzSerilogLoggingPipeline()
.AddFranzTelemetry(env, config);
builder.Services.AddRelationalDatabase<ApplicationDbContext>(env, config);
builder.Services.AddHttpArchitecture(env, config);
builder.Services.AddFranzMediator(new[] { typeof(CreateOrderCommandHandler).Assembly });
builder.Services.AddFranzResilience(config);That's the full subsystem wiring for logging, persistence, HTTP, mediator, and resilience — no boilerplate beyond what's shown above.
git clone https://github.com/bestacio89/Franz.Common.git
cd Franz.Common
dotnet build
dotnet testKafka integration tests (real broker via Testcontainers, not mocks):
docker-compose up -d
dotnet test --filter Category=IntegrationFull version history lives in changelog.md. Recent highlights:
Changed
To help potential users understand the scope of Franz.Common.Logging at a glance, add this bullet point to the "Features" section of your main project README:
-
Adaptive Environment Logging
-
UseLog()for standard web-based environments. -
UseDesktopLog()for thread-aware, low-noise diagnostic logging in WPF/Avalonia/MAUI apps. -
UseHybridLog()for flexible, configuration-driven logging scenarios.
- GraphQL Adapters and Implementations
- SignalR Adapters and Implementations
Franz is maintained with enterprise environments in mind. For support, consulting, integration guidance, or architectural reviews, please contact the maintainer.
Pull requests welcome — internal contributors preferred. All PRs must include tests, documentation, and comply with Franz Tribunal rules.
MIT License.
