Examples¶
This section provides real-world examples of using DecoWeaver to apply the decorator pattern in different scenarios.
Available Examples¶
Caching¶
Learn how to add caching to your services with decorators:
- Memory Caching - In-memory caching with
IMemoryCache - Distributed Caching - Redis/SQL Server caching
- Conditional Caching - Smart caching strategies
- Cache Invalidation - Managing cache lifetime
Logging¶
Add comprehensive logging to your services:
- Method Call Logging - Log method entry/exit
- Parameter Logging - Log parameters and results
- Structured Logging - Use structured logging
- Performance Logging - Log execution times
Resilience¶
Build resilient services with retry and circuit breaker patterns:
- Retry Logic - Automatic retry with exponential backoff
- Circuit Breaker - Prevent cascading failures
- Timeout - Protect against hanging operations
- Fallback - Graceful degradation
Validation¶
Validate inputs and business rules:
- Input Validation - Validate method parameters
- Business Rules - Apply business logic
- FluentValidation Integration - Use FluentValidation
- Conditional Validation - Context-based validation
Metrics and Telemetry¶
Monitor and measure your services:
- OpenTelemetry - Distributed tracing with OpenTelemetry
- Custom Metrics - Track custom metrics
- Performance Counters - Monitor performance
- Health Checks - Service health monitoring
Complete Example¶
Here's a complete example showing multiple decorators working together:
using Microsoft.Extensions.DependencyInjection;
using DecoWeaver.Attributes;
// Service interface
public interface IOrderService
{
Task<Order> GetOrderAsync(int orderId);
Task<Order> CreateOrderAsync(CreateOrderRequest request);
}
// Implementation
[DecoratedBy<ValidationDecorator>(Order = 1)]
[DecoratedBy<CachingDecorator>(Order = 2)]
[DecoratedBy<LoggingDecorator>(Order = 3)]
[DecoratedBy<MetricsDecorator>(Order = 4)]
public class OrderService : IOrderService
{
private readonly IOrderRepository _repository;
public OrderService(IOrderRepository repository)
{
_repository = repository;
}
public async Task<Order> GetOrderAsync(int orderId)
{
return await _repository.GetByIdAsync(orderId);
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
var order = new Order
{
CustomerId = request.CustomerId,
Items = request.Items,
Total = request.Items.Sum(i => i.Price * i.Quantity)
};
await _repository.SaveAsync(order);
return order;
}
}
// Validation decorator
public class ValidationDecorator : IOrderService
{
private readonly IOrderService _inner;
private readonly IValidator<CreateOrderRequest> _validator;
public ValidationDecorator(
IOrderService inner,
IValidator<CreateOrderRequest> validator)
{
_inner = inner;
_validator = validator;
}
public Task<Order> GetOrderAsync(int orderId)
{
if (orderId <= 0)
throw new ArgumentException("Order ID must be positive", nameof(orderId));
return _inner.GetOrderAsync(orderId);
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
await _validator.ValidateAndThrowAsync(request);
return await _inner.CreateOrderAsync(request);
}
}
// Caching decorator
public class CachingDecorator : IOrderService
{
private readonly IOrderService _inner;
private readonly IMemoryCache _cache;
public CachingDecorator(IOrderService inner, IMemoryCache cache)
{
_inner = inner;
_cache = cache;
}
public async Task<Order> GetOrderAsync(int orderId)
{
var key = $"order:{orderId}";
if (_cache.TryGetValue(key, out Order cached))
return cached;
var order = await _inner.GetOrderAsync(orderId);
_cache.Set(key, order, TimeSpan.FromMinutes(5));
return order;
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
var order = await _inner.CreateOrderAsync(request);
_cache.Remove($"order:{order.Id}");
return order;
}
}
// Logging decorator
public class LoggingDecorator : IOrderService
{
private readonly IOrderService _inner;
private readonly ILogger<LoggingDecorator> _logger;
public LoggingDecorator(IOrderService inner, ILogger<LoggingDecorator> logger)
{
_inner = inner;
_logger = logger;
}
public async Task<Order> GetOrderAsync(int orderId)
{
_logger.LogInformation("Getting order {OrderId}", orderId);
try
{
var order = await _inner.GetOrderAsync(orderId);
_logger.LogInformation("Retrieved order {OrderId}", orderId);
return order;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting order {OrderId}", orderId);
throw;
}
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
_logger.LogInformation(
"Creating order for customer {CustomerId}",
request.CustomerId);
try
{
var order = await _inner.CreateOrderAsync(request);
_logger.LogInformation("Created order {OrderId}", order.Id);
return order;
}
catch (Exception ex)
{
_logger.LogError(
ex,
"Error creating order for customer {CustomerId}",
request.CustomerId);
throw;
}
}
}
// Metrics decorator
public class MetricsDecorator : IOrderService
{
private readonly IOrderService _inner;
private readonly IMeterFactory _meterFactory;
private readonly Counter<long> _getCounter;
private readonly Counter<long> _createCounter;
private readonly Histogram<double> _duration;
public MetricsDecorator(IOrderService inner, IMeterFactory meterFactory)
{
_inner = inner;
_meterFactory = meterFactory;
var meter = meterFactory.Create("OrderService");
_getCounter = meter.CreateCounter<long>("orders.get.count");
_createCounter = meter.CreateCounter<long>("orders.create.count");
_duration = meter.CreateHistogram<double>("orders.duration");
}
public async Task<Order> GetOrderAsync(int orderId)
{
var sw = Stopwatch.StartNew();
try
{
var order = await _inner.GetOrderAsync(orderId);
_getCounter.Add(1, new TagList { { "status", "success" } });
return order;
}
catch
{
_getCounter.Add(1, new TagList { { "status", "error" } });
throw;
}
finally
{
_duration.Record(
sw.Elapsed.TotalMilliseconds,
new TagList { { "method", "get" } });
}
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
var sw = Stopwatch.StartNew();
try
{
var order = await _inner.CreateOrderAsync(request);
_createCounter.Add(1, new TagList { { "status", "success" } });
return order;
}
catch
{
_createCounter.Add(1, new TagList { { "status", "error" } });
throw;
}
finally
{
_duration.Record(
sw.Elapsed.TotalMilliseconds,
new TagList { { "method", "create" } });
}
}
}
// Registration
var services = new ServiceCollection();
// Register dependencies
services.AddLogging();
services.AddMemoryCache();
services.AddMetrics();
services.AddScoped<IValidator<CreateOrderRequest>, CreateOrderRequestValidator>();
services.AddScoped<IOrderRepository, OrderRepository>();
// Register service - decorators automatically applied
services.AddScoped<IOrderService, OrderService>();
// Build and use
var provider = services.BuildServiceProvider();
var orderService = provider.GetRequiredService<IOrderService>();
// This request goes through: Metrics → Logging → Caching → Validation → OrderService
var order = await orderService.GetOrderAsync(123);
Example Structure¶
Each example follows a consistent structure:
- Problem - What problem the decorator solves
- Solution - How the decorator is implemented
- Usage - How to apply the decorator
- Complete Code - Full working example
- Variations - Alternative approaches or extensions
Testing Examples¶
Most examples include test examples showing how to:
- Test decorators in isolation
- Test the full decorator chain
- Mock dependencies
- Verify decorator behavior
Running Examples¶
All examples are runnable with minimal setup:
- Create a new console application
- Install required NuGet packages
- Copy the example code
- Run with
dotnet run
Contributing Examples¶
Have a great DecoWeaver example? We'd love to include it! See the Contributing Guide for how to submit examples.
Next Steps¶
- Start with Logging Examples for basic decorator patterns
- Explore Caching Examples for performance optimization
- Learn Resilience Patterns for production-ready services