VOOZH about

URL: https://www.nuget.org/packages/Marventa.Framework/

⇱ NuGet Gallery | Marventa.Framework 5.2.0


ο»Ώ

πŸ‘ Image
Marventa.Framework 5.2.0

dotnet add package Marventa.Framework --version 5.2.0
 
 
NuGet\Install-Package Marventa.Framework -Version 5.2.0
 
 
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Marventa.Framework" Version="5.2.0" />
 
 
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Marventa.Framework" Version="5.2.0" />
 
Directory.Packages.props
<PackageReference Include="Marventa.Framework" />
 
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Marventa.Framework --version 5.2.0
 
 
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Marventa.Framework, 5.2.0"
 
 
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Marventa.Framework@5.2.0
 
 
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Marventa.Framework&version=5.2.0
 
Install as a Cake Addin
#tool nuget:?package=Marventa.Framework&version=5.2.0
 
Install as a Cake Tool
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

πŸš€ Marventa.Framework

Enterprise .NET Framework - Convention over Configuration

πŸ‘ NuGet
πŸ‘ License: MIT


πŸ“– Table of Contents

  1. Installation
  2. Basic Setup (2 Lines!)
  3. Core - Domain Driven Design (DDD)
  4. Infrastructure - Database & Repository
  5. Behaviors - CQRS with MediatR
  6. Features - Caching
  7. Features - Event Bus
  8. Features - Storage
  9. Features - Search
  10. Features - Notification
  11. Features - Localization (i18n)
  12. Features - Template Engine
  13. Features - Reporting
  14. Features - Payment Gateway
  15. Features - Job Scheduler
  16. Features - Audit Logging
  17. Features - Logging
  18. Security - Authentication
  19. Security - Authorization
  20. Security - Rate Limiting
  21. Infrastructure - Multi-Tenancy
  22. Infrastructure - Health Checks
  23. Infrastructure - API Versioning
  24. Infrastructure - Swagger/OpenAPI
  25. Middleware - Exception Handling
  26. Enterprise Patterns
  27. Advanced Security
  28. Configuration - appsettings.json

1. Installation

dotnet add package Marventa.Framework

2. Basic Setup

Marventa Framework offers 4 flexible integration levels - choose what fits your needs:

⚑ Level 1: Everything (Recommended for Monoliths)

Get started instantly with ALL features (Core + Extended):

var builder = WebApplication.CreateBuilder(args);

// ✨ ONE LINE - ALL features enabled (Core + Extended)
// Includes: CQRS, Repository, Event Sourcing, JWT, Caching, Event Bus, Outbox,
// Notifications, Localization, Templating, Reporting, Payment, Job Scheduler, Audit Logging
builder.Services.AddMarventaFramework(builder.Configuration);

var app = builder.Build();

// ✨ ONE LINE - All middleware configured automatically
app.UseMarventa(builder.Configuration);

app.Run();

🎯 Level 2: Core Only (Recommended for Microservices)

Use only architectural patterns and infrastructure - perfect for microservices:

var builder = WebApplication.CreateBuilder(args);

// ✨ CORE FEATURES ONLY - Perfect for microservices
// Includes: CQRS, Repository, Event Sourcing, JWT, Caching, Event Bus, Outbox,
// Idempotency, Circuit Breaker, API Versioning, Swagger, Health Checks
builder.Services.AddMarventaCore(builder.Configuration);

var app = builder.Build();
app.UseMarventa(builder.Configuration);
app.Run();

πŸ”§ Level 3: Core + Selected Extended Features

Mix and match - add only what you need:

var builder = WebApplication.CreateBuilder(args);

// Start with Core
builder.Services.AddMarventaCore(builder.Configuration);

// Add extended features selectively
builder.Services.AddNotifications(builder.Configuration); // Email, SMS, Push
builder.Services.AddMarventaLocalization(builder.Configuration); // i18n
builder.Services.AddAuditLogging(); // Compliance tracking

var app = builder.Build();
app.UseMarventa(builder.Configuration);
app.Run();

βš™οΈ Level 4: Granular Control (Advanced)

Full control with fluent API:

var builder = WebApplication.CreateBuilder(args);

// Fluent API for precise control
builder.Services.AddMarventaFramework(builder.Configuration, options =>
{
 // Core features
 options.AddCqrs();
 options.AddRepositoryPattern();
 options.AddEventBus();
 options.AddOutboxPattern();
 options.AddJwtAuthentication();
 options.AddCaching();

 // Extended features
 options.AddEmailService();
 options.AddSmsService();
 options.AddAuditLogging();
 options.AddTemplateEngine();
});

var app = builder.Build();
app.UseMarventa(builder.Configuration);
app.Run();

πŸ“Š Quick Comparison

Integration Level Use Case Features Included
Level 1: AddMarventaFramework() Monolith, Full-stack apps Core + Extended (Everything)
Level 2: AddMarventaCore() Microservices Core only (Patterns + Infrastructure)
Level 3: Core + Selective Hybrid, Custom needs Core + Selected extended features
Level 4: Fluent API Advanced, Fine-grained control Pick each feature individually

πŸ”₯ Feature Groupings

Core Features (Production-ready patterns):

  • βœ… CQRS & MediatR
  • βœ… Repository & Specification Pattern
  • βœ… Event Sourcing & Domain Events
  • βœ… Event Bus (RabbitMQ/Kafka/MassTransit)
  • βœ… Transactional Outbox Pattern
  • βœ… Idempotency
  • βœ… JWT + API Key Authentication
  • βœ… Redis Caching
  • βœ… Circuit Breaker & Retry (Polly)
  • βœ… API Versioning & Swagger
  • βœ… Health Checks
  • βœ… Multi-Tenancy
  • βœ… Rate Limiting

Extended Features (Optional enterprise services):

  • πŸ“§ Notifications (Email, SMS, Push)
  • 🌍 Localization (i18n)
  • πŸ“„ Template Engine (Liquid/Simple)
  • πŸ“Š Reporting (PDF, Excel, CSV)
  • πŸ’³ Payment Gateway (Stripe)
  • ⏰ Job Scheduler (Hangfire)
  • πŸ“ Audit Logging

πŸš€ Real-World Examples

E-commerce Monolith:

builder.Services.AddMarventaFramework(builder.Configuration);
// Get everything: CQRS, Events, Payments, Emails, Reporting, etc.

Order Microservice:

builder.Services.AddMarventaCore(builder.Configuration);
// Just patterns: CQRS, Repository, Event Bus, Outbox

Notification Microservice:

builder.Services.AddMarventaCore(builder.Configuration);
builder.Services.AddNotifications(builder.Configuration);
builder.Services.AddTemplateEngine(builder.Configuration);
// Core + Email/SMS/Push + Templates

Payment Microservice:

builder.Services.AddMarventaCore(builder.Configuration);
builder.Services.AddPaymentGateway(builder.Configuration);
builder.Services.AddAuditLogging();
// Core + Stripe + Audit

That's it! The framework automatically:

  • βœ… Registers controllers and JSON serialization
  • βœ… Scans assemblies for MediatR handlers, FluentValidation validators, and Mapster mappings
  • βœ… Configures middleware pipeline in correct order
  • βœ… Maps controller endpoints and health checks
  • βœ… Activates features based on appsettings.json

Advanced: Specify Assemblies to Scan

// Automatically scans calling assembly (recommended)
builder.Services.AddMarventa(builder.Configuration);

// Or explicitly specify assemblies to scan
builder.Services.AddMarventa(builder.Configuration, typeof(Program).Assembly);

// Or scan multiple assemblies
builder.Services.AddMarventa(
 builder.Configuration,
 typeof(Program).Assembly,
 typeof(SomeOtherClass).Assembly
);

3. Core - Domain Driven Design

3.1. Domain - Entity

Purpose: Represents domain objects with identity.

using Marventa.Framework.Core.Domain;

public class Product : Entity<Guid>
{
 public string Name { get; private set; }
 public decimal Price { get; private set; }
 public int Stock { get; private set; }

 private Product() { }

 public static Product Create(string name, decimal price, int stock)
 {
 return new Product
 {
 Id = Guid.NewGuid(),
 Name = name,
 Price = price,
 Stock = stock
 };
 }

 public void UpdateStock(int quantity)
 {
 Stock += quantity;
 }
}

3.2. Domain - Aggregate Root

Purpose: Root entity that manages business rules and dispatches domain events.

public class Order : AggregateRoot<Guid>
{
 private readonly List<OrderItem> _items = new();

 public string OrderNumber { get; private set; }
 public OrderStatus Status { get; private set; }
 public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();

 public static Order Create(string orderNumber)
 {
 var order = new Order
 {
 Id = Guid.NewGuid(),
 OrderNumber = orderNumber,
 Status = OrderStatus.Pending
 };

 order.AddDomainEvent(new OrderCreatedEvent(order.Id));
 return order;
 }

 public void Confirm()
 {
 Status = OrderStatus.Confirmed;
 AddDomainEvent(new OrderConfirmedEvent(Id));
 }
}

3.3. Domain - Value Object

Purpose: Objects without identity, compared by their values.

public class Address : ValueObject
{
 public string Street { get; private set; }
 public string City { get; private set; }
 public string ZipCode { get; private set; }

 public Address(string street, string city, string zipCode)
 {
 Street = street;
 City = city;
 ZipCode = zipCode;
 }

 protected override IEnumerable<object> GetEqualityComponents()
 {
 yield return Street;
 yield return City;
 yield return ZipCode;
 }
}

3.4. Domain - Domain Events

Purpose: Represents events that occur within the domain.

public record ProductCreatedEvent(Guid ProductId, string Name) : DomainEvent;
public record OrderCreatedEvent(Guid OrderId) : DomainEvent;
public record OrderConfirmedEvent(Guid OrderId) : DomainEvent;

3.5. Domain - Auditable Entity

Purpose: Automatically tracks creation and update information.

public class Customer : AuditableEntity<Guid>
{
 public string Name { get; set; }
 public string Email { get; set; }

 // CreatedAt, UpdatedAt, CreatedBy, UpdatedBy tracked automatically!
}

3.6. Application - Result Pattern

Purpose: Type-safe way to return success/failure states.

public async Task<Result<Guid>> CreateProduct(string name, decimal price)
{
 if (price <= 0)
 return Result<Guid>.Failure("Price must be positive");

 var product = Product.Create(name, price, 0);
 await _repository.AddAsync(product);

 return Result<Guid>.Success(product.Id);
}

4. Infrastructure - Database & Repository

4.1. Persistence - Create DbContext

Purpose: Database connection with Entity Framework Core.

using Marventa.Framework.Infrastructure.Persistence;

public class ApplicationDbContext : BaseDbContext
{
 public ApplicationDbContext(
 DbContextOptions<ApplicationDbContext> options,
 IHttpContextAccessor httpContextAccessor)
 : base(options, httpContextAccessor)
 {
 }

 public DbSet<Product> Products => Set<Product>();
 public DbSet<Order> Orders => Set<Order>();
}

Add to Program.cs:

builder.Services.AddDbContext<ApplicationDbContext>(options =>
 options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddScoped<IUnitOfWork>(sp =>
 new UnitOfWork(sp.GetRequiredService<ApplicationDbContext>()));

appsettings.json:

{
 "ConnectionStrings": {
 "DefaultConnection": "Server=localhost;Database=MyDb;Trusted_Connection=true;"
 }
}

4.2. Persistence - Repository Pattern

Purpose: Abstracts database operations.

// Interface
public interface IProductRepository : IRepository<Product, Guid>
{
 Task<Product?> GetByNameAsync(string name);
 Task<List<Product>> SearchAsync(string searchTerm);
}

// Implementation
public class ProductRepository : GenericRepository<Product, Guid>, IProductRepository
{
 public ProductRepository(ApplicationDbContext context) : base(context)
 {
 }

 public async Task<Product?> GetByNameAsync(string name)
 {
 return await _dbSet.FirstOrDefaultAsync(p => p.Name == name);
 }

 public async Task<List<Product>> SearchAsync(string searchTerm)
 {
 return await _dbSet.Where(p => p.Name.Contains(searchTerm)).ToListAsync();
 }
}

Add to Program.cs:

builder.Services.AddScoped<IProductRepository, ProductRepository>();

4.3. Persistence - Unit of Work

Purpose: Manages transactions and dispatches domain events.

public class ProductService
{
 private readonly IProductRepository _repository;
 private readonly IUnitOfWork _unitOfWork;

 public async Task<Result<Guid>> CreateProductAsync(string name, decimal price)
 {
 var product = Product.Create(name, price, 0);
 await _repository.AddAsync(product);

 // Transaction + Domain Events
 await _unitOfWork.SaveChangesAsync();

 return Result<Guid>.Success(product.Id);
 }
}

4.4. Persistence - Data Seeding

Purpose: Seed initial data into database with helper infrastructure.

// Create a seeder
public class UserSeeder : DataSeederBase<ApplicationDbContext>
{
 public UserSeeder(ApplicationDbContext context) : base(context)
 {
 }

 public override int Order => 1; // Execution order

 public override async Task SeedAsync(CancellationToken cancellationToken = default)
 {
 if (await AnyAsync<User>(cancellationToken))
 return;

 var users = new List<User>
 {
 User.Create("admin@example.com", "Admin User"),
 User.Create("user@example.com", "Regular User")
 };

 await AddRangeAsync(users, cancellationToken);
 }
}

Register Seeders:

builder.Services.AddScoped<IDataSeeder, UserSeeder>();
builder.Services.AddScoped<IDataSeeder, ProductSeeder>();

Run Seeders:

// In Program.cs after app.Build()
using (var scope = app.Services.CreateScope())
{
 var seederRunner = scope.ServiceProvider.GetRequiredService<DataSeederRunner>();
 await seederRunner.RunAsync();
}

4.5. Persistence - MongoDB Repository Pattern

Purpose: Complete repository pattern implementation for MongoDB with document-based entities.

Install MongoDB Package:

dotnet add package MongoDB.Driver

Configuration (appsettings.json):

{
 "MongoDB": {
 "ConnectionString": "mongodb://localhost:27017",
 "DatabaseName": "MyDatabase"
 }
}

Create MongoDB Entity:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Marventa.Framework.Core.Domain;

public class Product : MongoEntity<ObjectId>
{
 [BsonElement("name")]
 public string Name { get; set; }

 [BsonElement("price")]
 public decimal Price { get; set; }

 [BsonElement("stock")]
 public int Stock { get; set; }

 [BsonElement("category")]
 public string Category { get; set; }

 [BsonElement("isActive")]
 public bool IsActive { get; set; }

 public static Product Create(string name, decimal price, int stock, string category)
 {
 return new Product
 {
 Id = ObjectId.GenerateNewId(),
 Name = name,
 Price = price,
 Stock = stock,
 Category = category,
 IsActive = true
 };
 }
}

Setup in Program.cs:

// Register MongoDB context and services
builder.Services.AddMongoDb(builder.Configuration);

// Register repository
builder.Services.AddScoped<IMongoRepository<Product, ObjectId>, MongoRepository<Product, ObjectId>>();

Create Repository Interface:

public interface IProductRepository : IMongoRepository<Product, ObjectId>
{
 Task<List<Product>> GetActiveProductsAsync();
 Task<List<Product>> SearchByNameAsync(string searchTerm);
 Task<List<Product>> GetByCategoryAsync(string category);
}

Create Repository Implementation:

using Marventa.Framework.Infrastructure.Persistence.MongoDB;

public class ProductRepository : MongoRepository<Product, ObjectId>, IProductRepository
{
 public ProductRepository(IMongoDatabase database) : base(database)
 {
 }

 public async Task<List<Product>> GetActiveProductsAsync()
 {
 return await FindAsync(p => p.IsActive);
 }

 public async Task<List<Product>> SearchByNameAsync(string searchTerm)
 {
 return await FindAsync(p => p.Name.Contains(searchTerm));
 }

 public async Task<List<Product>> GetByCategoryAsync(string category)
 {
 return await FindAsync(p => p.Category == category && p.IsActive);
 }
}

Usage in Service:

public class ProductService
{
 private readonly IProductRepository _repository;

 public ProductService(IProductRepository repository)
 {
 _repository = repository;
 }

 public async Task<ObjectId> CreateProductAsync(string name, decimal price, int stock, string category)
 {
 var product = Product.Create(name, price, stock, category);
 await _repository.AddAsync(product);
 return product.Id;
 }

 public async Task<Product?> GetProductAsync(ObjectId id)
 {
 return await _repository.GetByIdAsync(id);
 }

 public async Task<List<Product>> GetAllProductsAsync()
 {
 return await _repository.GetAllAsync();
 }

 public async Task<List<Product>> SearchProductsAsync(string searchTerm)
 {
 return await _repository.SearchByNameAsync(searchTerm);
 }

 public async Task UpdateProductAsync(ObjectId id, decimal newPrice, int newStock)
 {
 var product = await _repository.GetByIdAsync(id);
 if (product != null)
 {
 product.Price = newPrice;
 product.Stock = newStock;
 await _repository.UpdateAsync(product);
 }
 }

 public async Task DeleteProductAsync(ObjectId id)
 {
 await _repository.DeleteAsync(id);
 }

 public async Task<long> CountActiveProductsAsync()
 {
 return await _repository.CountAsync(p => p.IsActive);
 }

 public async Task<bool> ProductExistsAsync(string name)
 {
 return await _repository.ExistsAsync(p => p.Name == name);
 }
}

Usage in Controller:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
 private readonly ProductService _productService;

 public ProductsController(ProductService productService)
 {
 _productService = productService;
 }

 [HttpPost]
 public async Task<IActionResult> Create([FromBody] CreateProductRequest request)
 {
 var id = await _productService.CreateProductAsync(
 request.Name,
 request.Price,
 request.Stock,
 request.Category);

 return Ok(new { id = id.ToString() });
 }

 [HttpGet("{id}")]
 public async Task<IActionResult> Get(string id)
 {
 if (!ObjectId.TryParse(id, out var objectId))
 return BadRequest("Invalid ID format");

 var product = await _productService.GetProductAsync(objectId);
 return product != null ? Ok(product) : NotFound();
 }

 [HttpGet]
 public async Task<IActionResult> GetAll()
 {
 var products = await _productService.GetAllProductsAsync();
 return Ok(products);
 }

 [HttpGet("search")]
 public async Task<IActionResult> Search([FromQuery] string term)
 {
 var products = await _productService.SearchProductsAsync(term);
 return Ok(products);
 }

 [HttpPut("{id}")]
 public async Task<IActionResult> Update(string id, [FromBody] UpdateProductRequest request)
 {
 if (!ObjectId.TryParse(id, out var objectId))
 return BadRequest("Invalid ID format");

 await _productService.UpdateProductAsync(objectId, request.Price, request.Stock);
 return NoContent();
 }

 [HttpDelete("{id}")]
 public async Task<IActionResult> Delete(string id)
 {
 if (!ObjectId.TryParse(id, out var objectId))
 return BadRequest("Invalid ID format");

 await _productService.DeleteProductAsync(objectId);
 return NoContent();
 }
}

Available Repository Methods:

  • GetByIdAsync(id) - Get single document by ID
  • GetAllAsync() - Get all documents
  • FindAsync(predicate) - Find documents by expression
  • AddAsync(entity) - Insert single document
  • AddRangeAsync(entities) - Insert multiple documents
  • UpdateAsync(entity) - Update document
  • DeleteAsync(id) - Delete by ID
  • DeleteAsync(entity) - Delete document
  • CountAsync(predicate) - Count documents
  • ExistsAsync(predicate) - Check if document exists
  • GetPagedAsync(pageNumber, pageSize, predicate) - Paginated results

Benefits:

  • βœ… No DbContext needed - lightweight and fast
  • βœ… BSON ObjectId support out of the box
  • βœ… Async/await throughout
  • βœ… Expression-based queries
  • βœ… Full CRUD operations
  • βœ… Pagination support
  • βœ… Integration with Marventa.Framework patterns

5. Behaviors - CQRS with MediatR

Purpose: MediatR is auto-registered with validation/logging/performance behaviors active.

5.1. Create Command

// Command
public record CreateProductCommand(string Name, decimal Price) : IRequest<Result<Guid>>;

// Handler
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Result<Guid>>
{
 private readonly IProductRepository _repository;
 private readonly IUnitOfWork _unitOfWork;

 public async Task<Result<Guid>> Handle(CreateProductCommand request, CancellationToken ct)
 {
 var product = Product.Create(request.Name, request.Price, 0);
 await _repository.AddAsync(product);
 await _unitOfWork.SaveChangesAsync(ct);

 return Result<Guid>.Success(product.Id);
 }
}

// Validator
public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
{
 public CreateProductCommandValidator()
 {
 RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
 RuleFor(x => x.Price).GreaterThan(0);
 }
}

5.2. Create Query

// Query
public record GetProductByIdQuery(Guid Id) : IRequest<Result<ProductDto>>;

// Handler
public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Result<ProductDto>>
{
 private readonly IProductRepository _repository;

 public async Task<Result<ProductDto>> Handle(GetProductByIdQuery request, CancellationToken ct)
 {
 var product = await _repository.GetByIdAsync(request.Id);
 if (product == null)
 return Result<ProductDto>.Failure("Product not found");

 return Result<ProductDto>.Success(new ProductDto(product.Id, product.Name, product.Price));
 }
}

public record ProductDto(Guid Id, string Name, decimal Price);

Usage in Controller:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
 private readonly IMediator _mediator;

 [HttpPost]
 public async Task<IActionResult> Create([FromBody] CreateProductCommand command)
 {
 var result = await _mediator.Send(command);
 return result.IsSuccess ? Ok(result.Value) : BadRequest(result.ErrorMessage);
 }

 [HttpGet("{id}")]
 public async Task<IActionResult> GetById(Guid id)
 {
 var result = await _mediator.Send(new GetProductByIdQuery(id));
 return result.IsSuccess ? Ok(result.Value) : NotFound(result.ErrorMessage);
 }
}

5.3. Validation Behavior

Purpose: Automatically validates all requests with FluentValidation. Auto-active! Just write validator classes.

5.4. Logging Behavior

Purpose: Logs all requests/responses. Auto-active!

5.5. Performance Behavior

Purpose: Warns about requests taking longer than 500ms. Auto-active!


6. Features - Caching

Purpose: Framework supports three caching strategies: InMemory, Redis, and Hybrid (two-level cache).

6.1. InMemory Cache

Configuration (appsettings.json):

{
 "MemoryCache": {
 "SizeLimit": 1024,
 "CompactionPercentage": 0.25,
 "ExpirationScanFrequency": "00:01:00"
 }
}

Usage:

using Marventa.Framework.Features.Caching.Abstractions;

public class ProductService
{
 private readonly ICacheService _cache;

 public async Task<Product?> GetProductAsync(Guid id)
 {
 var cacheKey = $"product:{id}";

 // Try get from cache
 var cached = await _cache.GetAsync<Product>(cacheKey);
 if (cached != null) return cached;

 // Get from database
 var product = await _repository.GetByIdAsync(id);

 // Set cache with expiration
 await _cache.SetAsync(cacheKey, product, TimeSpan.FromHours(1));

 return product;
 }

 public async Task RemoveProductCacheAsync(Guid id)
 {
 await _cache.RemoveAsync($"product:{id}");
 }
}

6.2. Output Cache

Purpose: ASP.NET Core 7+ output caching for HTTP responses.

Configuration:

{
 "OutputCache": {
 "Enabled": true,
 "DefaultExpirationSeconds": 60,
 "VaryByQuery": true,
 "VaryByHeader": false,
 "VaryByHeaderNames": []
 }
}

Add to Program.cs:

builder.Services.AddMarventaOutputCache(builder.Configuration);

Usage in Controllers:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
 // Cache response for 60 seconds (from configuration)
 [OutputCache]
 [HttpGet]
 public async Task<IActionResult> GetAll()
 {
 var products = await _mediator.Send(new GetAllProductsQuery());
 return Ok(products);
 }

 // Custom cache duration
 [OutputCache(Duration = 300)]
 [HttpGet("{id}")]
 public async Task<IActionResult> GetById(Guid id)
 {
 var product = await _mediator.Send(new GetProductByIdQuery(id));
 return Ok(product);
 }
}

6.3. Redis Cache

Configuration (appsettings.json):

{
 "Caching": { "Type": "Redis" },
 "Redis": {
 "ConnectionString": "localhost:6379",
 "InstanceName": "MyApp:"
 }
}

Usage: Same interface as InMemory (ICacheService). Framework automatically switches based on configuration.

6.4. Hybrid Cache

Purpose: Two-level caching - reads from InMemory (L1) first, then Redis (L2). Best of both worlds!

Configuration:

{
 "Caching": { "Type": "Hybrid" },
 "MemoryCache": {
 "SizeLimit": 1024,
 "CompactionPercentage": 0.25
 },
 "Redis": {
 "ConnectionString": "localhost:6379",
 "InstanceName": "MyApp:"
 }
}

How it works:

  • Get: Checks InMemory first, then Redis if not found
  • Set: Writes to both InMemory and Redis
  • Remove: Removes from both caches

Usage: Same ICacheService interface - completely transparent!

6.5. Modular Caching Setup

Add specific cache type:

// Add InMemory cache only
builder.Services.AddInMemoryCaching(builder.Configuration);

// Add Redis cache only
builder.Services.AddRedisCaching(builder.Configuration);

// Add Hybrid cache
builder.Services.AddHybridCaching(builder.Configuration);

// Auto-detect from configuration (used by AddMarventa)
builder.Services.AddMarventaCaching(builder.Configuration);

7. Features - Event Bus

7.1. RabbitMQ Event Bus

Configuration:

{
 "RabbitMQ": {
 "Host": "localhost",
 "Username": "guest",
 "Password": "guest"
 }
}

Publish:

public class OrderCreatedEvent : IntegrationEvent
{
 public Guid OrderId { get; }
 public string OrderNumber { get; }

 public OrderCreatedEvent(Guid orderId, string orderNumber)
 {
 OrderId = orderId;
 OrderNumber = orderNumber;
 }
}

await _eventBus.PublishAsync(new OrderCreatedEvent(orderId, orderNumber));

Subscribe:

public class OrderCreatedEventHandler : IIntegrationEventHandler<OrderCreatedEvent>
{
 public async Task HandleAsync(OrderCreatedEvent @event)
 {
 Console.WriteLine($"Order created: {@event.OrderNumber}");
 }
}

// Add to Program.cs
builder.Services.AddScoped<IIntegrationEventHandler<OrderCreatedEvent>, OrderCreatedEventHandler>();

7.2. Kafka Producer/Consumer

Configuration:

{
 "Kafka": {
 "BootstrapServers": "localhost:9092",
 "GroupId": "myapp-group"
 }
}

Usage:

// Produce
await _kafkaProducer.ProduceAsync("my-topic", new { UserId = 123 });

// Consume
await _kafkaConsumer.ConsumeAsync("my-topic", async message =>
{
 Console.WriteLine($"Received: {message}");
});

7.3. MassTransit Integration

Configuration:

{
 "MassTransit": { "Enabled": "true" },
 "RabbitMQ": {
 "Host": "localhost",
 "Username": "guest",
 "Password": "guest"
 }
}

Usage:

// Consumer
public class OrderCreatedConsumer : IConsumer<OrderCreated>
{
 public async Task Consume(ConsumeContext<OrderCreated> context)
 {
 Console.WriteLine($"Order {context.Message.OrderId}");
 }
}

// Publish
await _publishEndpoint.Publish(new OrderCreated { OrderId = 123 });

8. Features - Storage

8.1. Local File Storage

Configuration:

{
 "LocalStorage": {
 "BasePath": "D:/uploads",
 "BaseUrl": "https://myapp.com/files"
 }
}

Usage:

// Upload
await _storage.UploadAsync(fileStream, "documents/file.pdf");

// Download
var stream = await _storage.DownloadAsync("documents/file.pdf");

// Delete
await _storage.DeleteAsync("documents/file.pdf");

// Get URL
var url = await _storage.GetUrlAsync("documents/file.pdf");

8.2. Azure Blob Storage

Configuration:

{
 "Azure": {
 "Storage": {
 "ConnectionString": "your-connection-string",
 "ContainerName": "uploads"
 }
 }
}

Usage: Same as Local Storage.

8.3. AWS S3 Storage

Configuration:

{
 "AWS": {
 "AccessKey": "your-key",
 "SecretKey": "your-secret",
 "Region": "us-east-1",
 "BucketName": "my-bucket"
 }
}

Usage: Same as Local Storage.


9. Features - Search

9.1. Elasticsearch

Configuration:

{
 "Elasticsearch": {
 "Uri": "http://localhost:9200"
 }
}

Usage:

// Index
await _elasticsearchService.IndexAsync("products", product);

// Search
var results = await _elasticsearchService.SearchAsync<Product>("products", "laptop");

10. Features - Notification

Purpose: Send notifications via Email, SMS, and Push Notifications with multiple provider support.

10.1. Email Services

Supported Providers:

  • SMTP - Standard SMTP with MailKit
  • SendGrid - Cloud-based email delivery
  • AWS SES - Amazon Simple Email Service (placeholder)

Configuration (appsettings.json):

{
 "Notification": {
 "EmailProvider": "Smtp",
 "EnableEmail": true
 },
 "Smtp": {
 "Host": "smtp.gmail.com",
 "Port": 587,
 "EnableSsl": true,
 "Username": "your-email@gmail.com",
 "Password": "your-password",
 "FromEmail": "noreply@yourapp.com",
 "FromName": "Your App",
 "TimeoutSeconds": 30
 }
}

Setup:

// Program.cs - Automatic registration based on configuration
builder.Services.AddNotifications(builder.Configuration);

// Or register specific email provider
builder.Services.AddSmtpEmail(builder.Configuration);
builder.Services.AddSendGridEmail(builder.Configuration);

Usage:

using Marventa.Framework.Features.Notification.Abstractions;
using Marventa.Framework.Features.Notification.Models;

public class UserService
{
 private readonly IEmailService _emailService;

 // Simple email
 public async Task SendWelcomeEmailAsync(string email, string name)
 {
 var result = await _emailService.SendSimpleAsync(
 to: email,
 subject: "Welcome to Our App!",
 body: $"<h1>Hello {name}!</h1><p>Welcome aboard!</p>",
 isHtml: true
 );

 if (result.IsSuccess)
 {
 Console.WriteLine($"Email sent: {result.MessageId}");
 }
 }

 // Advanced email with attachments
 public async Task SendInvoiceEmailAsync(string email, byte[] pdfContent)
 {
 var message = new EmailMessage
 {
 Subject = "Your Invoice",
 HtmlBody = "<p>Please find your invoice attached.</p>",
 Priority = EmailPriority.High
 };

 message
 .AddTo(email)
 .AddCc("accounting@company.com")
 .AddAttachment("invoice.pdf", pdfContent, "application/pdf");

 var result = await _emailService.SendAsync(message);
 }
}

SendGrid Configuration:

{
 "Notification": {
 "EmailProvider": "SendGrid",
 "EnableEmail": true
 },
 "SendGrid": {
 "ApiKey": "SG.your-api-key",
 "FromEmail": "noreply@yourapp.com",
 "FromName": "Your App",
 "SandboxMode": false
 }
}

10.2. SMS Services

Supported Providers:

  • Twilio - Popular SMS service with global coverage
  • Vonage (Nexmo) - Cloud communications platform

Configuration (Twilio):

{
 "Notification": {
 "SmsProvider": "Twilio",
 "EnableSms": true
 },
 "Twilio": {
 "AccountSid": "your-account-sid",
 "AuthToken": "your-auth-token",
 "FromPhoneNumber": "+1234567890",
 "MessagingServiceSid": "optional-messaging-service-sid"
 }
}

Setup:

// Program.cs
builder.Services.AddNotifications(builder.Configuration);

// Or register specific SMS provider
builder.Services.AddTwilioSms(builder.Configuration);
builder.Services.AddVonageSms(builder.Configuration);

Usage:

using Marventa.Framework.Features.Notification.Abstractions;
using Marventa.Framework.Features.Notification.Models;

public class AuthService
{
 private readonly ISmsService _smsService;

 // Simple SMS
 public async Task SendOtpAsync(string phoneNumber, string otp)
 {
 var result = await _smsService.SendSimpleAsync(
 to: phoneNumber,
 body: $"Your OTP code is: {otp}. Valid for 5 minutes."
 );

 if (result.IsSuccess)
 {
 Console.WriteLine($"SMS sent: {result.MessageId}");
 }
 }

 // Advanced SMS with metadata
 public async Task SendOrderNotificationAsync(string phoneNumber, string orderId)
 {
 var message = new SmsMessage
 {
 To = phoneNumber,
 Body = $"Your order #{orderId} has been shipped!",
 Type = SmsType.Transactional
 };

 message.AddMetadata("orderId", orderId);

 var result = await _smsService.SendAsync(message);
 }
}

Vonage Configuration:

{
 "Notification": {
 "SmsProvider": "Vonage",
 "EnableSms": true
 },
 "Vonage": {
 "ApiKey": "your-api-key",
 "ApiSecret": "your-api-secret",
 "FromPhoneNumber": "YourCompany"
 }
}

10.3. Push Notification

Supported Providers:

  • Firebase Cloud Messaging (FCM) - Google's push notification service

Configuration:

{
 "Notification": {
 "PushProvider": "Firebase",
 "EnablePush": true
 },
 "Firebase": {
 "ProjectId": "your-firebase-project-id",
 "ServerKey": "your-server-key",
 "ServiceAccountKeyPath": "path/to/service-account.json",
 "DefaultChannelId": "default-channel"
 }
}

Setup:

// Program.cs
builder.Services.AddNotifications(builder.Configuration);

// Or register specific push provider
builder.Services.AddFirebasePush(builder.Configuration);

Usage:

using Marventa.Framework.Features.Notification.Abstractions;
using Marventa.Framework.Features.Notification.Models;

public class NotificationService
{
 private readonly IPushNotificationService _pushService;

 // Simple push notification
 public async Task SendSimplePushAsync(string deviceToken)
 {
 var result = await _pushService.SendSimpleAsync(
 deviceToken: deviceToken,
 title: "New Message",
 body: "You have a new message!"
 );
 }

 // Advanced push notification
 public async Task SendAdvancedPushAsync(List<string> deviceTokens)
 {
 var message = new PushNotificationMessage
 {
 Title = "Order Update",
 Body = "Your order has been delivered!",
 Icon = "ic_notification",
 ImageUrl = "https://example.com/notification-image.png",
 Sound = "default",
 Priority = PushPriority.High,
 ChannelId = "orders",
 ClickAction = "OPEN_ORDER_ACTIVITY"
 };

 foreach (var token in deviceTokens)
 {
 message.AddDeviceToken(token);
 }

 message
 .AddData("orderId", "12345")
 .AddData("status", "delivered");

 var result = await _pushService.SendAsync(message);
 }

 // Send to topic/channel
 public async Task SendToTopicAsync()
 {
 var result = await _pushService.SendToTopicAsync(
 topic: "news",
 title: "Breaking News",
 body: "Important announcement!"
 );
 }
}

Platform-Specific Features:

  • Android: Channel ID, Color, Icon, Tag
  • iOS: Badge count, Sound
  • Both: Custom data payload, Click actions

11. Features - Localization (i18n)

Multi-language JSON resource support with culture switching:

// appsettings.json
{
 "Localization": {
 "DefaultCulture": "en-US",
 "SupportedCultures": ["en-US", "tr-TR", "de-DE"],
 "ResourcesPath": "Resources/Localization"
 }
}

// Resources/Localization/en-US.json
{
 "Welcome": "Welcome",
 "ProductNotFound": "Product not found"
}

// Resources/Localization/tr-TR.json
{
 "Welcome": "Hoş Geldiniz",
 "ProductNotFound": "Ürün bulunamadı"
}

// Usage
public class ProductService
{
 private readonly ILocalizationService _localization;

 public string GetWelcomeMessage()
 {
 return _localization.GetString("Welcome");
 }

 public void SwitchLanguage(string culture)
 {
 _localization.SetCurrentCulture(culture);
 }
}

Culture Detection:

  • Query string: ?culture=tr-TR
  • Cookie: .AspNetCore.Culture
  • Accept-Language header

12. Features - Template Engine

Dynamic content rendering with Liquid or Simple templates:

// appsettings.json
{
 "Templating": {
 "EngineType": "Simple", // or "Liquid"
 "TemplatesPath": "Templates"
 }
}

// Templates/order-confirmation.liquid
Hello {{ CustomerName }},
Your order #{{ OrderId }} totaling {{ Total | currency }} has been confirmed.

// Usage
public class EmailService
{
 private readonly ITemplateEngine _templateEngine;

 public async Task<string> RenderOrderEmail(Order order)
 {
 return await _templateEngine.RenderTemplateAsync("order-confirmation", new
 {
 CustomerName = order.CustomerName,
 OrderId = order.Id,
 Total = order.TotalAmount
 });
 }
}

13. Features - Reporting

13.1. PDF Report Generation

public class ReportService
{
 private readonly IReportGenerator _pdfGenerator;

 public async Task<byte[]> GeneratePdfReport(List<Product> products)
 {
 return await _pdfGenerator.GenerateAsync(products, new ReportOptions
 {
 Title = "Product Report",
 Orientation = PageOrientation.Landscape
 });
 }
}

13.2. Excel Report Generation

public async Task<byte[]> GenerateExcelReport(List<Order> orders)
{
 return await _excelGenerator.GenerateAsync(orders, new ReportOptions
 {
 SheetName = "Orders",
 AutoFitColumns = true
 });
}

13.3. CSV Report Generation

public async Task<byte[]> GenerateCsvReport(List<Customer> customers)
{
 return await _csvGenerator.GenerateAsync(customers);
}

14. Features - Payment Gateway

Stripe payment abstraction:

// appsettings.json
{
 "Stripe": {
 "SecretKey": "sk_test_...",
 "PublishableKey": "pk_test_...",
 "WebhookSecret": "whsec_..."
 }
}

// Usage
public class CheckoutService
{
 private readonly IPaymentGateway _paymentGateway;

 public async Task<PaymentResponse> ProcessPayment(decimal amount, string currency)
 {
 var result = await _paymentGateway.CreatePaymentAsync(new PaymentRequest
 {
 Amount = amount,
 Currency = currency,
 OrderId = "ORD-12345",
 CustomerEmail = "customer@example.com"
 });

 if (result.IsSuccess)
 {
 // Payment succeeded
 var paymentId = result.PaymentId;
 }

 return result;
 }
}

15. Features - Job Scheduler

Hangfire wrapper with cron support:

public class BackgroundJobService
{
 private readonly IJobScheduler _scheduler;

 // Enqueue immediate job
 public void SendWelcomeEmail(string userId)
 {
 _scheduler.Enqueue(() => SendEmailAsync(userId));
 }

 // Schedule delayed job
 public void SendReminderLater(string userId)
 {
 _scheduler.Schedule(() => SendReminderAsync(userId), TimeSpan.FromHours(24));
 }

 // Recurring job with cron
 public void SetupDailyReport()
 {
 _scheduler.AddOrUpdateRecurringJob(
 "daily-report",
 () => GenerateDailyReportAsync(),
 CronExpressions.Daily // Every day at midnight
 );
 }
}

// Built-in cron expressions
// - CronExpressions.EveryMinute
// - CronExpressions.Hourly
// - CronExpressions.Daily
// - CronExpressions.Weekly
// - CronExpressions.Monthly

16. Features - Audit Logging

Entity change tracking for compliance:

public class ProductService
{
 private readonly IAuditLogger _auditLogger;
 private readonly IRepository<Product> _productRepository;

 public async Task UpdateProduct(Guid id, UpdateProductDto dto)
 {
 var product = await _productRepository.GetByIdAsync(id);
 var oldProduct = product.Clone(); // Keep copy for audit

 product.UpdatePrice(dto.NewPrice);
 product.UpdateStock(dto.NewStock);

 // Audit log - automatically captures:
 // - Changed properties (Price, Stock)
 // - Old values vs New values
 // - User who made change (from JWT claims)
 // - IP address, UserAgent
 // - Timestamp
 await _auditLogger.LogUpdateAsync(oldProduct, product);

 await _productRepository.UpdateAsync(product);
 }

 public async Task<List<AuditLog>> GetProductHistory(Guid productId)
 {
 return await _auditLogger.GetAuditLogsAsync("Product", productId.ToString());
 }
}

Audit Log Model:

public class AuditLog
{
 public Guid Id { get; set; }
 public string? UserId { get; set; }
 public string? Username { get; set; }
 public string Action { get; set; } // Create, Update, Delete
 public string EntityType { get; set; }
 public string? EntityId { get; set; }
 public string? OldValues { get; set; } // JSON
 public string? NewValues { get; set; } // JSON
 public List<string> ChangedProperties { get; set; }
 public string? IpAddress { get; set; }
 public string? UserAgent { get; set; }
 public DateTime Timestamp { get; set; }
}

17. Features - Logging

17.1. Serilog

Configuration:

{
 "ApplicationName": "MyApp",
 "Serilog": {
 "MinimumLevel": "Information",
 "WriteTo": [
 { "Name": "Console" },
 { "Name": "File", "Args": { "path": "logs/log-.txt", "rollingInterval": "Day" } }
 ]
 }
}

Usage:

_logger.LogInformation("Product {ProductId} created", productId);
_logger.LogError(ex, "Failed to create product");

17.2. OpenTelemetry Tracing

Configuration:

{
 "OpenTelemetry": {
 "ServiceName": "MyApp",
 "OtlpEndpoint": "http://localhost:4317"
 }
}

Purpose: Automatically traces HTTP, Database, and External API calls.


18. Security - Authentication

18.1. JWT Authentication Service

Configuration:

{
 "Jwt": {
 "Secret": "your-super-secret-key-at-least-32-characters",
 "Issuer": "MyApp",
 "Audience": "MyApp",
 "ExpirationMinutes": 60
 }
}

Generate Access Token:

using Marventa.Framework.Security.Authentication.Abstractions;

public class AuthService
{
 private readonly IJwtService _jwtService;

 // Simple usage
 public string Login(User user)
 {
 var token = _jwtService.GenerateAccessToken(
 userId: user.Id.ToString(),
 email: user.Email,
 roles: new[] { "Admin" },
 additionalClaims: new Dictionary<string, string>
 {
 ["department"] = "IT",
 ["permission"] = "products.write"
 }
 );

 return token;
 }
}

Validate and Extract Claims:

// Validate token
var principal = _jwtService.ValidateAccessToken(token);
if (principal == null)
{
 // Token invalid or expired
}

// Get user ID from token
var userId = _jwtService.GetUserIdFromToken(token);

// Get all claims
var claims = _jwtService.GetClaimsFromToken(token);

// Check if token expired
var isExpired = _jwtService.IsTokenExpired(token);

// Get remaining lifetime
var remainingTime = _jwtService.GetTokenRemainingLifetime(token);

12.2. Refresh Token Generation

Purpose: Generate cryptographically secure refresh token strings.

Important: The framework only provides token generation. You must implement your own:

  • RefreshToken entity in your domain model
  • Repository for database storage
  • Validation, rotation, and revocation logic

Generate Refresh Token:

using Marventa.Framework.Security.Authentication.Abstractions;

public class AuthService
{
 private readonly IJwtService _jwtService;
 private readonly IRefreshTokenRepository _refreshTokenRepository;
 private readonly IUnitOfWork _unitOfWork;

 public async Task<LoginResponse> LoginAsync(string email, string password)
 {
 // Validate user credentials...

 // Generate tokens
 var accessToken = _jwtService.GenerateAccessToken(user.Id.ToString(), user.Email);
 var refreshTokenString = _jwtService.GenerateRefreshToken();

 // Create your own domain entity
 var refreshToken = new Domain.Entities.RefreshToken
 {
 Token = refreshTokenString,
 UserId = user.Id,
 ExpiresAt = DateTime.UtcNow.AddDays(7),
 CreatedByIp = HttpContext.Connection.RemoteIpAddress?.ToString()
 };

 // Save to your database
 await _refreshTokenRepository.AddAsync(refreshToken);
 await _unitOfWork.SaveChangesAsync();

 return new LoginResponse
 {
 AccessToken = accessToken,
 RefreshToken = refreshTokenString,
 ExpiresAt = refreshToken.ExpiresAt
 };
 }

 public async Task<LoginResponse> RefreshTokenAsync(string refreshToken)
 {
 // Validate from your database
 var token = await _refreshTokenRepository.GetByTokenAsync(refreshToken);
 if (token == null || token.IsExpired || token.IsRevoked)
 {
 throw new UnauthorizedException("Invalid refresh token");
 }

 // Optional: Implement token rotation
 token.RevokedAt = DateTime.UtcNow;
 await _refreshTokenRepository.UpdateAsync(token);

 // Generate new tokens
 var accessToken = _jwtService.GenerateAccessToken(token.UserId.ToString(), user.Email);
 var newRefreshTokenString = _jwtService.GenerateRefreshToken();

 var newRefreshToken = new Domain.Entities.RefreshToken
 {
 Token = newRefreshTokenString,
 UserId = token.UserId,
 ExpiresAt = DateTime.UtcNow.AddDays(7),
 ReplacedByToken = token.Token
 };

 await _refreshTokenRepository.AddAsync(newRefreshToken);
 await _unitOfWork.SaveChangesAsync();

 return new LoginResponse
 {
 AccessToken = accessToken,
 RefreshToken = newRefreshTokenString,
 ExpiresAt = newRefreshToken.ExpiresAt
 };
 }
}

Your Domain Entity Example:

public class RefreshToken : Entity<Guid>
{
 public string Token { get; set; }
 public Guid UserId { get; set; }
 public DateTime ExpiresAt { get; set; }
 public DateTime? RevokedAt { get; set; }
 public string? CreatedByIp { get; set; }
 public string? ReplacedByToken { get; set; }

 public bool IsExpired => DateTime.UtcNow >= ExpiresAt;
 public bool IsRevoked => RevokedAt != null;
 public bool IsActive => !IsExpired && !IsRevoked;
}

12.3. Password Service

Purpose: Secure password hashing with Argon2id (winner of Password Hashing Competition 2015) and strength validation.

Why Argon2id?

  • βœ… 2-6x FASTER than BCrypt
  • βœ… More secure against GPU/ASIC attacks (high memory requirement)
  • βœ… OWASP recommended (2024)
  • βœ… Automatic migration from BCrypt hashes
using Marventa.Framework.Security.Encryption.Abstractions;

public class UserService
{
 private readonly IPasswordService _passwordService;

 // Hash password with Argon2id
 public async Task RegisterAsync(string email, string password)
 {
 // Validate password strength
 var (isValid, errorMessage) = _passwordService.ValidatePasswordStrength(
 password: password,
 minLength: 8,
 requireUppercase: true,
 requireLowercase: true,
 requireDigit: true,
 requireSpecialChar: true
 );

 if (!isValid)
 {
 throw new BusinessException($"Weak password: {errorMessage}");
 }

 // Hash with Argon2id (OWASP settings: m=19456, t=2, p=1)
 var hashedPassword = _passwordService.HashPassword(password);

 // Save user with hashed password...
 }

 // Verify password (supports both Argon2id and legacy BCrypt)
 public async Task<bool> LoginAsync(string email, string password)
 {
 var user = await _userRepository.GetByEmailAsync(email);
 if (user == null)
 return false;

 var isValid = _passwordService.VerifyPassword(password, user.PasswordHash);

 // Automatic migration from BCrypt to Argon2id
 if (isValid && _passwordService.NeedsRehash(user.PasswordHash))
 {
 user.PasswordHash = _passwordService.HashPassword(password);
 await _userRepository.UpdateAsync(user);
 }

 return isValid;
 }

 // Generate secure random password
 public string GenerateTemporaryPassword()
 {
 return _passwordService.GenerateSecurePassword(
 length: 16,
 includeSpecialCharacters: true
 );
 }
}

Hash Format:

Argon2id: $argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>
BCrypt (legacy): $2a$12$<salt+hash>

Performance Comparison: | Algorithm | Hash Time | Security | |-----------|-----------|----------| | Argon2id (current) | 30-50ms | βœ… GPU/ASIC resistant | | BCrypt (legacy) | 100-300ms | ⚠️ Vulnerable to GPU attacks |

Migration is automatic! Existing BCrypt hashes are verified normally, then upgraded to Argon2id on next successful login.

12.4. AES Encryption

Purpose: Symmetric encryption for sensitive data.

using Marventa.Framework.Security.Encryption;

var encryption = new AesEncryption(
 key: "your-32-character-secret-key!",
 iv: "your-16-char-iv"
);

// Encrypt sensitive data
var encrypted = encryption.Encrypt("sensitive data");

// Decrypt
var decrypted = encryption.Decrypt(encrypted);

Use Cases:

  • Encrypting database connection strings
  • Storing sensitive configuration values
  • Protecting PII (Personal Identifiable Information)

19. Security - Authorization

19.1. Permission Based Authorization

[Authorize]
[RequirePermission("products.write")]
public async Task<IActionResult> Create([FromBody] CreateProductCommand command)
{
 var result = await _mediator.Send(command);
 return Ok(result);
}

20. Security - Rate Limiting

Configuration:

{
 "RateLimiting": {
 "Strategy": "IpAddress",
 "RequestLimit": 100,
 "TimeWindowSeconds": 60
 }
}

Purpose: Automatically limits to 100 requests per 60 seconds per IP.

Response Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1633024800

21. Infrastructure - Multi-Tenancy

Configuration:

{
 "MultiTenancy": {
 "Strategy": "Header",
 "HeaderName": "X-Tenant-Id"
 }
}

Usage:

var tenantId = _tenantContext.TenantId;
var tenantName = _tenantContext.TenantName;

var data = _repository.GetAll()
 .Where(x => x.TenantId == tenantId)
 .ToList();

Client Request:

curl -H "X-Tenant-Id: tenant-123" https://api.myapp.com/products

22. Infrastructure - Health Checks

Configuration:

{
 "HealthChecks": {
 "Enabled": "true"
 }
}

Purpose: Creates /health endpoint, automatically monitors Database/Redis/RabbitMQ.

Check:

curl http://localhost:5000/health

23. Infrastructure - API Versioning

Purpose: Provides flexible API versioning strategies.

Configuration:

{
 "ApiVersioning": {
 "Enabled": true,
 "DefaultVersion": "1.0",
 "ReportApiVersions": true,
 "AssumeDefaultVersionWhenUnspecified": true,
 "VersioningType": "UrlSegment",
 "HeaderName": "X-API-Version",
 "QueryStringParameterName": "api-version"
 }
}

Versioning Types:

  • UrlSegment - /api/v1/products (default)
  • QueryString - /api/products?api-version=1.0
  • Header - Header: X-API-Version: 1.0
  • MediaType - Accept: application/json;v=1.0

Usage in Controllers:

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ProductsV1Controller : ControllerBase
{
 [HttpGet]
 public IActionResult GetProducts()
 {
 return Ok(new[] { "Product 1", "Product 2" });
 }
}

[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class ProductsV2Controller : ControllerBase
{
 [HttpGet]
 public IActionResult GetProducts()
 {
 return Ok(new { products = new[] { "Product 1", "Product 2" }, version = "2.0" });
 }
}

Response Headers:

api-supported-versions: 1.0, 2.0
api-deprecated-versions: (none)

24. Infrastructure - Swagger/OpenAPI

Purpose: Auto-configured OpenAPI documentation with JWT support and environment restrictions.

Configuration:

{
 "Swagger": {
 "Enabled": true,
 "Title": "My API",
 "Description": "My API Documentation",
 "Version": "v1",
 "RequireAuthorization": true,
 "EnvironmentRestriction": ["Development", "Staging"],
 "Contact": {
 "Name": "API Support",
 "Email": "support@example.com",
 "Url": "https://example.com/support"
 },
 "License": {
 "Name": "MIT",
 "Url": "https://opensource.org/licenses/MIT"
 }
 }
}

Features:

  • βœ… Automatic JWT Bearer integration
  • βœ… Multi-version support (when API Versioning enabled)
  • βœ… XML comments auto-included
  • βœ… Environment-based restrictions
  • βœ… Swagger UI auto-configured

Access:

# Development/Staging only (based on EnvironmentRestriction)
https://localhost:5001/swagger

Usage in Program.cs:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMarventa(builder.Configuration);

var app = builder.Build();

// Pass IWebHostEnvironment for environment-based Swagger
app.UseMarventa(builder.Configuration, app.Environment);

app.Run();

Controller XML Comments:

/// <summary>
/// Creates a new product
/// </summary>
/// <param name="command">Product creation data</param>
/// <returns>The created product ID</returns>
/// <response code="200">Product created successfully</response>
/// <response code="400">Invalid request</response>
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create([FromBody] CreateProductCommand command)
{
 var result = await _mediator.Send(command);
 return result.IsSuccess ? Ok(result.Value) : BadRequest(result.ErrorMessage);
}

25. Middleware - Exception Handling

Purpose: Catches all exceptions and returns standard format. Auto-active!

Custom Exceptions:

throw new NotFoundException("Product not found");
throw new BusinessException("Insufficient stock");
throw new UnauthorizedException("Invalid credentials");

26. Enterprise Patterns

26.1. Transactional Outbox Pattern

The Outbox Pattern ensures reliable event publishing by storing events in the database in the same transaction as business data.

Setup:

// Program.cs
builder.Services.AddDbContext<YourDbContext>();
builder.Services.AddScoped<DbContext>(sp => sp.GetRequiredService<YourDbContext>());
builder.Services.AddOutbox(); // Registers outbox services and background processor

Usage:

// Domain Entity
public class Product : Entity<Guid>, IHasDomainEvents
{
 public void Create()
 {
 AddDomainEvent(new ProductCreatedEvent(Id, Name, Price));
 }
}

// Event Handler
public class ProductCreatedEventHandler : INotificationHandler<ProductCreatedEvent>
{
 public async Task Handle(ProductCreatedEvent notification, CancellationToken cancellationToken)
 {
 // Event will be published reliably via outbox
 await _eventBus.PublishAsync(notification);
 }
}

How it works:

  1. Domain events saved to OutboxMessages table in same transaction
  2. Background OutboxProcessor polls every 30 seconds
  3. Processes pending messages and publishes events
  4. Marks messages as processed
  5. Cleans up old messages (7-day retention)

26.2. Repository Specification Pattern

Build reusable, composable query specifications for complex queries.

Create Specification:

public class ActiveProductsSpecification : BaseSpecification<Product>
{
 public ActiveProductsSpecification()
 {
 // Filtering
 Criteria = p => p.IsActive && !p.IsDeleted;

 // Eager Loading
 AddInclude(p => p.Category);
 AddInclude(p => p.Supplier);

 // Ordering
 AddOrderBy(p => p.Name);

 // Pagination
 ApplyPaging(0, 20);
 }
}

public class ProductsByPriceRangeSpec : BaseSpecification<Product>
{
 public ProductsByPriceRangeSpec(decimal minPrice, decimal maxPrice)
 {
 Criteria = p => p.Price >= minPrice && p.Price <= maxPrice;
 AddOrderByDescending(p => p.Price);
 }
}

Use Specification:

// In Repository or Command Handler
var spec = new ActiveProductsSpecification();
var products = await _repository.FindAsync(spec);

var priceSpec = new ProductsByPriceRangeSpec(50m, 200m);
var filteredProducts = await _repository.FindAsync(priceSpec);

Available Methods:

  • Criteria - WHERE clause filter
  • AddInclude() / AddInclude(string) - Eager loading
  • AddOrderBy() / AddOrderByDescending() - Sorting
  • ApplyPaging(skip, take) - Pagination

26.3. Idempotency Pattern

Prevent duplicate request processing with distributed caching.

Setup:

// Program.cs
builder.Services.AddDistributedMemoryCache(); // or AddStackExchangeRedisCache
builder.Services.AddIdempotency();

var app = builder.Build();
app.UseRouting();
app.UseIdempotency(); // Must be after UseRouting()

Usage:

// Client sends Idempotency-Key header
POST /api/orders
Headers:
 Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Body: { "productId": "123", "quantity": 2 }

How it works:

  1. Middleware detects Idempotency-Key header
  2. Checks cache for existing response
  3. If found, returns cached response (duplicate detected)
  4. If not found, processes request and caches response
  5. Default 24-hour expiration
  6. Only caches successful responses (2xx status codes)

26.4. Circuit Breaker & Resilience

Add resilience to HTTP clients with Polly integration.

Setup:

// Program.cs
builder.Services.AddResilientHttpClient("PaymentService", "https://api.payment.com")
 .AddHttpMessageHandler(() => new AuthHeaderHandler());

// Or configure manually
builder.Services.AddHttpClient("OrderService")
 .AddPolicyHandler(ResilienceExtensions.GetRetryPolicy(3))
 .AddPolicyHandler(ResilienceExtensions.GetCircuitBreakerPolicy(5, TimeSpan.FromSeconds(30)));

Usage:

public class PaymentService
{
 private readonly IHttpClientFactory _httpClientFactory;

 public async Task<bool> ProcessPayment(PaymentRequest request)
 {
 var client = _httpClientFactory.CreateClient("PaymentService");
 var response = await client.PostAsJsonAsync("/payments", request);
 return response.IsSuccessStatusCode;
 }
}

Policies:

  • Retry Policy: 3 retries with exponential backoff (2s, 4s, 8s)
  • Circuit Breaker: Opens after 5 consecutive failures, stays open for 30s
  • Timeout: Configurable per request

27. Advanced Security

27.1. API Key Authentication

Header-based authentication with role support.

Setup:

// Program.cs
builder.Services.AddApiKeyAuthentication();

// appsettings.json
{
 "Authentication": {
 "ApiKeys": [
 {
 "Key": "your-api-key-12345",
 "Owner": "MobileApp",
 "Roles": "User,Customer"
 },
 {
 "Key": "admin-key-67890",
 "Owner": "BackofficeAdmin",
 "Roles": "Admin,User"
 }
 ]
 }
}

Usage:

[Authorize(AuthenticationSchemes = "ApiKey")]
[ApiController]
public class ProductsController : ControllerBase
{
 [HttpGet]
 [Authorize(Roles = "User")] // Requires User role
 public async Task<IActionResult> GetProducts() { }

 [HttpDelete("{id}")]
 [Authorize(Roles = "Admin")] // Requires Admin role
 public async Task<IActionResult> DeleteProduct(Guid id) { }
}

// Client Request
GET /api/products
Headers:
 X-API-Key: your-api-key-12345

Features:

  • Header name: X-API-Key
  • Claims-based authentication
  • Role-based authorization
  • API key masking in logs (shows first 4 chars)

27.2. Request/Response Logging

Comprehensive logging with automatic sensitive data masking.

Setup:

// Program.cs
builder.Services.Configure<LoggingOptions>(builder.Configuration.GetSection("LoggingOptions"));

var app = builder.Build();
app.UseMiddleware<RequestResponseLoggingMiddleware>(); // Must be early in pipeline

// appsettings.json
{
 "LoggingOptions": {
 "EnableRequestResponseLogging": true,
 "MaxBodyLogSize": 4096,
 "LogRequestHeaders": true,
 "LogRequestBody": true,
 "LogResponseHeaders": true,
 "LogResponseBody": true,
 "SensitiveHeaders": ["Authorization", "X-API-Key", "Cookie"],
 "SensitiveBodyFields": ["password", "token", "secret", "apikey"]
 }
}

Features:

  • Automatic header masking (Authorization, X-API-Key, Cookie)
  • Automatic body field masking (password, token, secret, etc.)
  • Configurable max body size
  • JSON field detection and masking
  • Response time tracking
  • Content-type aware (only logs text-based content)

Example Log Output:

[INFO] HTTP GET /api/users/login
Request Headers: {"User-Agent": "PostmanRuntime/7.32.0", "Authorization": "***MASKED***"}
Request Body: {"username":"john","password":"***MASKED***","apiKey":"***MASKED***"}
Response Status: 200 OK
Response Body: {"success":true,"token":"***MASKED***","user":{...}}
Response Time: 45ms

28. Configuration - appsettings.json

Complete configuration example:

{
 "ApplicationName": "MyApp",

 "ConnectionStrings": {
 "DefaultConnection": "Server=localhost;Database=MyDb;Trusted_Connection=true;"
 },

 "Cors": {
 "AllowedOrigins": ["http://localhost:3000", "https://myapp.com"]
 },

 "ApiVersioning": {
 "Enabled": true,
 "DefaultVersion": "1.0",
 "ReportApiVersions": true,
 "AssumeDefaultVersionWhenUnspecified": true,
 "VersioningType": "UrlSegment"
 },

 "Swagger": {
 "Enabled": true,
 "Title": "My API",
 "Description": "My API Documentation",
 "Version": "v1",
 "RequireAuthorization": true,
 "EnvironmentRestriction": ["Development", "Staging"]
 },

 "Jwt": {
 "Secret": "your-super-secret-key-at-least-32-characters-long",
 "Issuer": "MyApp",
 "Audience": "MyApp",
 "ExpirationMinutes": 60
 },

 "Caching": {
 "Type": "Hybrid"
 },

 "MemoryCache": {
 "SizeLimit": 1024,
 "CompactionPercentage": 0.25,
 "ExpirationScanFrequency": "00:01:00"
 },

 "OutputCache": {
 "Enabled": true,
 "DefaultExpirationSeconds": 60,
 "VaryByQuery": true,
 "VaryByHeader": false,
 "VaryByHeaderNames": []
 },

 "Redis": {
 "ConnectionString": "localhost:6379",
 "InstanceName": "MyApp:"
 },

 "MultiTenancy": {
 "Strategy": "Header",
 "HeaderName": "X-Tenant-Id"
 },

 "RateLimiting": {
 "Strategy": "IpAddress",
 "RequestLimit": 100,
 "TimeWindowSeconds": 60
 },

 "RabbitMQ": {
 "Host": "localhost",
 "VirtualHost": "/",
 "Username": "guest",
 "Password": "guest"
 },

 "Kafka": {
 "BootstrapServers": "localhost:9092",
 "GroupId": "myapp-group"
 },

 "MassTransit": {
 "Enabled": "true"
 },

 "LocalStorage": {
 "BasePath": "D:/uploads",
 "BaseUrl": "https://myapp.com/files"
 },

 "Azure": {
 "Storage": {
 "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=...",
 "ContainerName": "uploads"
 }
 },

 "AWS": {
 "AccessKey": "your-access-key",
 "SecretKey": "your-secret-key",
 "Region": "us-east-1",
 "BucketName": "my-bucket"
 },

 "MongoDB": {
 "ConnectionString": "mongodb://localhost:27017",
 "DatabaseName": "MyDatabase"
 },

 "Elasticsearch": {
 "Uri": "http://localhost:9200"
 },

 "HealthChecks": {
 "Enabled": "true"
 },

 "Serilog": {
 "MinimumLevel": {
 "Default": "Information",
 "Override": {
 "Microsoft": "Warning",
 "System": "Warning"
 }
 },
 "WriteTo": [
 { "Name": "Console" },
 {
 "Name": "File",
 "Args": {
 "path": "logs/log-.txt",
 "rollingInterval": "Day",
 "retainedFileCountLimit": 7
 }
 }
 ]
 },

 "OpenTelemetry": {
 "ServiceName": "MyApp",
 "OtlpEndpoint": "http://localhost:4317"
 }
}

πŸŽ‰ That's It!

Your application now has:

βœ… Core: Domain Driven Design (Entity, Aggregate, ValueObject, DomainEvent) βœ… Behaviors: CQRS (MediatR + FluentValidation + Mapster + Logging + Performance) βœ… Infrastructure: Repository Pattern, Unit of Work, Multi-Tenancy, Health Checks, Data Seeding βœ… API: Swagger/OpenAPI, API Versioning (URL/Query/Header), XML Documentation βœ… Features: Caching, Event Bus (RabbitMQ/Kafka/MassTransit), Storage, Search, Logging βœ… Security: JWT Auth, CORS, Permission Authorization, Rate Limiting, Password Hashing βœ… Middleware: Global Exception Handling with correct pipeline order

With just 2 lines of setup! πŸš€

πŸ†• What's New in v5.2.0 - MongoDB Repository Pattern & .NET 10 Support

πŸ“¦ MongoDB Repository Pattern:

  • Full MongoDB Integration: Complete repository pattern implementation for MongoDB
  • Document-based Entities: Support for MongoDB BSON ObjectId and document attributes
  • Generic Repository: IMongoRepository<TEntity> with async CRUD operations
  • Specification Pattern: MongoDB-specific specification support
  • Unit of Work: Transaction support for MongoDB sessions
  • Flexible Configuration: Easy setup with connection string and database name

πŸš€ .NET 10 Support:

  • Framework Upgrade: Full compatibility with .NET 10.0
  • Performance Improvements: Leveraging latest .NET 10 runtime optimizations
  • C# 13 Features: Support for latest C# language features
  • Modern APIs: Updated to use latest ASP.NET Core 10 APIs

πŸ“š Enhanced Documentation:

  • Added MongoDB setup guide
  • MongoDB repository examples
  • Configuration samples
  • Migration guide from SQL to MongoDB

πŸ†• What's New in v5.1.0 - Complete Enterprise Toolkit

πŸŽ‰ Notification Services:

  • Email Services: SMTP, SendGrid, AWS SES support with attachments and HTML
  • SMS Services: Twilio and Vonage integration for global messaging
  • Push Notifications: Firebase Cloud Messaging for mobile apps
  • Unified Interface: Consistent API across all notification providers
  • Fluent Builders: Easy message construction with builder pattern

🌍 Localization (i18n):

  • Multi-Language Support: JSON-based resource files
  • Culture Detection: Query string, cookie, and header-based culture switching
  • ILocalizationService: Simple API for string translations

πŸ“„ Template Engine:

  • Liquid Templates: DotLiquid integration for advanced templating
  • Simple Templates: Lightweight string interpolation
  • Email Templates: Pre-built email templates

πŸ“Š Reporting:

  • PDF Reports: QuestPDF integration
  • Excel Reports: EPPlus for spreadsheet generation
  • CSV Reports: Fast CSV export

πŸ’³ Payment Gateway:

  • Stripe Integration: Payment processing with webhook support
  • Payment Models: Type-safe payment requests and responses

⏰ Job Scheduler:

  • Hangfire Integration: Background job processing
  • Cron Support: Schedule recurring jobs
  • Dashboard: Built-in monitoring UI

πŸ“ Audit Logging:

  • Entity Change Tracking: Automatic audit trail
  • User Context: Track who made changes
  • IP & UserAgent Logging: Full request context

πŸ†• What's New in v5.0.0 - MAJOR SECURITY AND ARCHITECTURE UPDATE

πŸ”’ Critical Security Fixes:

  • AES-GCM Encryption with Random Nonce: Fixed encryption vulnerability by using AES-GCM with cryptographically random nonce for each operation (previously used static IV with CBC mode)
  • Entity Equality Implementation: Corrected GetHashCode() for transient entities to prevent hash collisions
  • JWT Secret Validation: Added minimum 32-character validation for JWT secrets
  • Domain Events Transaction Safety: Events now dispatched AFTER SaveChanges for transaction consistency

πŸ—οΈ Repository & Database Enhancements:

  • Pagination Support: Added GetPagedAsync() with eager loading support to prevent memory issues
  • N+1 Query Prevention: Include/ThenInclude support in all query methods
  • Soft Delete Global Filters: Automatic filtering of soft-deleted entities via ISoftDeletable
  • ICurrentUserService: Claims-based current user service for audit trails

πŸ“¦ New Enterprise Patterns:

  • Transactional Outbox Pattern: Ensures reliable event publishing with background processor
  • Repository Specification Pattern: Reusable, composable query specifications with filtering, ordering, and pagination
  • Idempotency Pattern: Prevents duplicate request processing with distributed caching
  • Circuit Breaker with Polly: Resilient HTTP client with retry and circuit breaker policies

πŸ” Advanced Security Features:

  • API Key Authentication: Header-based authentication with role support
  • Request/Response Logging: Comprehensive logging with automatic sensitive data masking (passwords, tokens, API keys)
  • Configuration Validation: Startup validation for all configuration options

πŸš€ Infrastructure Improvements:

  • Redis Prefix Removal: Efficient RemoveByPrefixAsync() implementation
  • Performance Middleware: Automatic response time tracking with slow request logging
  • Code Quality: Eliminated all magic strings, improved error handling, enhanced XML documentation

πŸ†• What's New in v4.6.0

  • Password Hashing Migration: Migrated from BCrypt to Argon2id for enhanced security
    • More secure against GPU/ASIC attacks (high memory requirement)
    • OWASP recommended settings (m=19456, t=2, p=1)
    • Automatic backward compatibility with BCrypt hashes
    • Seamless migration on user login

πŸ“„ License

MIT License - See for details.


πŸ“§ Support

For questions, please open an issue on GitHub Issues.

Product Versions Compatible and additional computed target framework versions.
.NET net8.0 net8.0 is compatible.  net8.0-android net8.0-android was computed.  net8.0-browser net8.0-browser was computed.  net8.0-ios net8.0-ios was computed.  net8.0-maccatalyst net8.0-maccatalyst was computed.  net8.0-macos net8.0-macos was computed.  net8.0-tvos net8.0-tvos was computed.  net8.0-windows net8.0-windows was computed.  net9.0 net9.0 is compatible.  net9.0-android net9.0-android was computed.  net9.0-browser net9.0-browser was computed.  net9.0-ios net9.0-ios was computed.  net9.0-maccatalyst net9.0-maccatalyst was computed.  net9.0-macos net9.0-macos was computed.  net9.0-tvos net9.0-tvos was computed.  net9.0-windows net9.0-windows was computed.  net10.0 net10.0 is compatible.  net10.0-android net10.0-android was computed.  net10.0-browser net10.0-browser was computed.  net10.0-ios net10.0-ios was computed.  net10.0-maccatalyst net10.0-maccatalyst was computed.  net10.0-macos net10.0-macos was computed.  net10.0-tvos net10.0-tvos was computed.  net10.0-windows net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.2.0 318 10/13/2025 5.2.0 is deprecated because it is no longer maintained.
5.1.0 328 10/5/2025 5.1.0 is deprecated because it is no longer maintained.
5.0.0 249 10/4/2025 5.0.0 is deprecated because it is no longer maintained.
4.6.0 248 10/3/2025 4.6.0 is deprecated because it is no longer maintained.
4.5.5 268 10/2/2025 4.5.5 is deprecated because it is no longer maintained.
4.5.4 268 10/2/2025 4.5.4 is deprecated because it is no longer maintained.
4.5.3 258 10/2/2025 4.5.3 is deprecated because it is no longer maintained.
4.5.2 256 10/2/2025 4.5.2 is deprecated because it is no longer maintained.
4.5.1 262 10/2/2025 4.5.1 is deprecated because it is no longer maintained.
4.5.0 264 10/2/2025 4.5.0 is deprecated because it is no longer maintained.
4.4.0 272 10/1/2025 4.4.0 is deprecated because it is no longer maintained.
4.3.0 266 10/1/2025 4.3.0 is deprecated because it is no longer maintained.
4.2.0 269 10/1/2025 4.2.0 is deprecated because it is no longer maintained.
4.1.0 255 10/1/2025 4.1.0 is deprecated because it is no longer maintained.
4.0.2 267 10/1/2025 4.0.2 is deprecated because it is no longer maintained.
4.0.1 261 10/1/2025 4.0.1 is deprecated because it is no longer maintained.
4.0.0 344 9/30/2025 4.0.0 is deprecated because it is no longer maintained.
3.5.2 267 9/30/2025 3.5.2 is deprecated because it is no longer maintained.
3.5.1 308 9/30/2025 3.5.1 is deprecated because it is no longer maintained.
3.4.1 309 9/30/2025 3.4.1 is deprecated because it is no longer maintained.
Loading failed