Advanced Patterns Θi Production Strategies - Senior Level
Cuprins
- Architectural Patterns
- Concurrency Patterns
- Data Access Patterns
- Resilience Patterns
- Event-Driven Patterns
- Testing Strategies pentru Senior
Architectural Patterns
1. Layered Architecture - Enterprise Implementation
// β Common mistakes
public class BadLayeredArchitecture
{
// Controllers direct cu DbContext
[ApiController]
public class ProductController : ControllerBase
{
private readonly AppDbContext _db;
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _db.Products.FindAsync(id); // Direct DB access
return Ok(product); // Returning entity instead of DTO
}
}
}
// β
Proper implementation
public class ProperLayeredArchitecture
{
// Layer 1: API Layer
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
private readonly IMapper _mapper;
[HttpGet("{id}")]
[ProducesResponseType(typeof(ProductDTO), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<ProductDTO>> GetProductAsync(int id)
{
try
{
var product = await _productService.GetProductAsync(id);
return Ok(_mapper.Map<ProductDTO>(product));
}
catch (ProductNotFoundException ex)
{
return NotFound(new { message = ex.Message });
}
}
[HttpPost]
[ProducesResponseType(typeof(ProductDTO), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<ProductDTO>> CreateProductAsync(CreateProductRequest request)
{
// Validation through FluentValidation
if (!ModelState.IsValid)
return BadRequest(ModelState);
var product = await _productService.CreateProductAsync(request);
return CreatedAtAction(nameof(GetProductAsync), new { id = product.Id },
_mapper.Map<ProductDTO>(product));
}
}
// Layer 2: Service/Application Layer
public interface IProductService
{
Task<Product> GetProductAsync(int id);
Task<Product> CreateProductAsync(CreateProductRequest request);
Task<Product> UpdateProductAsync(int id, UpdateProductRequest request);
Task<bool> DeleteProductAsync(int id);
}
public class ProductService : IProductService
{
private readonly IProductRepository _repository;
private readonly IEventPublisher _eventPublisher;
private readonly ILogger<ProductService> _logger;
private readonly IValidator<CreateProductRequest> _validator;
public ProductService(
IProductRepository repository,
IEventPublisher eventPublisher,
ILogger<ProductService> logger,
IValidator<CreateProductRequest> validator)
{
_repository = repository;
_eventPublisher = eventPublisher;
_logger = logger;
_validator = validator;
}
public async Task<Product> GetProductAsync(int id)
{
_logger.LogInformation("Fetching product {ProductId}", id);
var product = await _repository.GetByIdAsync(id);
if (product == null)
throw new ProductNotFoundException($"Product {id} not found");
return product;
}
public async Task<Product> CreateProductAsync(CreateProductRequest request)
{
// Validation
var validationResult = await _validator.ValidateAsync(request);
if (!validationResult.IsValid)
throw new ValidationException(validationResult.Errors);
_logger.LogInformation("Creating new product: {ProductName}", request.Name);
var product = new Product
{
Name = request.Name,
Price = request.Price,
CategoryId = request.CategoryId,
CreatedAt = DateTime.UtcNow
};
await _repository.AddAsync(product);
// Publish domain event
await _eventPublisher.PublishAsync(
new ProductCreatedEvent { ProductId = product.Id });
_logger.LogInformation("Product created successfully: {ProductId}", product.Id);
return product;
}
public async Task<Product> UpdateProductAsync(int id, UpdateProductRequest request)
{
var product = await _repository.GetByIdAsync(id);
if (product == null)
throw new ProductNotFoundException();
product.Name = request.Name ?? product.Name;
product.Price = request.Price ?? product.Price;
product.ModifiedAt = DateTime.UtcNow;
await _repository.UpdateAsync(product);
await _eventPublisher.PublishAsync(
new ProductUpdatedEvent { ProductId = product.Id });
return product;
}
public async Task<bool> DeleteProductAsync(int id)
{
var product = await _repository.GetByIdAsync(id);
if (product == null)
return false;
product.IsDeleted = true;
product.DeletedAt = DateTime.UtcNow;
await _repository.UpdateAsync(product);
await _eventPublisher.PublishAsync(
new ProductDeletedEvent { ProductId = product.Id });
return true;
}
}
// Layer 3: Data Access Layer - Repository Pattern
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task<List<Product>> GetAllAsync(ProductSpecification spec);
Task<Product> AddAsync(Product product);
Task<Product> UpdateAsync(Product product);
Task DeleteAsync(int id);
}
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == id && !p.IsDeleted);
}
public async Task<List<Product>> GetAllAsync(ProductSpecification spec)
{
return await _context.Products
.Apply(spec)
.ToListAsync();
}
public async Task<Product> AddAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return product;
}
public async Task<Product> UpdateAsync(Product product)
{
_context.Products.Update(product);
await _context.SaveChangesAsync();
return product;
}
public async Task DeleteAsync(int id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
}
}
// Layer 4: Data Access Layer - DbContext
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Global query filters
modelBuilder.Entity<Product>()
.HasQueryFilter(p => !p.IsDeleted);
// Configure shadow properties
modelBuilder.Entity<Product>()
.Property<DateTime>("CreatedAt");
}
}
}
2. Vertical Slice Architecture
// AlternativΔ la layered - organizare per feature
public class VerticalSliceArchitecture
{
// Feature folder structure:
// Features/
// βββ Products/
// β βββ GetProduct/
// β β βββ GetProductQuery.cs
// β β βββ GetProductQueryHandler.cs
// β β βββ GetProductQueryValidator.cs
// β β βββ ProductDTO.cs
// β βββ CreateProduct/
// β β βββ CreateProductCommand.cs
// β β βββ CreateProductCommandHandler.cs
// β β βββ CreateProductValidator.cs
// β βββ ProductController.cs
// βββ Orders/
// βββ CreateOrder/
// βββ ...
// Query
public class GetProductQuery : IRequest<ProductDTO>
{
public int Id { get; set; }
}
public class GetProductQueryHandler : IRequestHandler<GetProductQuery, ProductDTO>
{
private readonly AppDbContext _context;
private readonly IMapper _mapper;
public async Task<ProductDTO> Handle(GetProductQuery request, CancellationToken cancellationToken)
{
var product = await _context.Products
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == request.Id, cancellationToken);
if (product == null)
throw new ProductNotFoundException();
return _mapper.Map<ProductDTO>(product);
}
}
// Controller
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly IMediator _mediator;
[HttpGet("{id}")]
public async Task<ActionResult<ProductDTO>> GetProduct(int id)
{
var result = await _mediator.Send(new GetProductQuery { Id = id });
return Ok(result);
}
}
}
Concurrency Patterns
1. Optimistic Concurrency Control
public class OptimisticConcurrencyPattern
{
// Entity with concurrency token
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; } // Concurrency token
}
// DbContext configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.RowVersion)
.IsRowVersion();
}
// Update with concurrency handling
public async Task UpdateProductAsync(int id, UpdateProductRequest request)
{
var product = await _context.Products.FindAsync(id);
product.Name = request.Name;
product.Price = request.Price;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
// Handle conflict - versiune veche vs noua
var databaseEntry = ex.Entries.Single();
var clientValues = (Product)databaseEntry.Entity;
var databaseValues = (Product)databaseEntry.CurrentValues.ToObject();
// Strategy 1: Client wins
databaseEntry.OriginalValues.SetValues(databaseValues);
await _context.SaveChangesAsync();
// Strategy 2: Database wins
databaseEntry.CurrentValues.SetValues(databaseValues);
// Strategy 3: Merge
databaseEntry.CurrentValues["Name"] = clientValues.Name;
databaseEntry.OriginalValues.SetValues(databaseValues);
await _context.SaveChangesAsync();
throw new ConcurrencyException("Another user modified this record");
}
}
}
2. Pessimistic Locking
public class PessimisticLockingPattern
{
// Using explicit transactions
public async Task SafeTransferAsync(Account from, Account to, decimal amount)
{
using (var transaction = _context.Database.BeginTransaction(
IsolationLevel.RepeatableRead))
{
try
{
// Lock source account
var sourceAccount = await _context.Accounts
.FromSqlInterpolated($"SELECT * FROM Accounts WITH (UPDLOCK) WHERE Id = {from.Id}")
.FirstOrDefaultAsync();
if (sourceAccount.Balance < amount)
throw new InsufficientFundsException();
sourceAccount.Balance -= amount;
// Lock destination account
var destAccount = await _context.Accounts
.FromSqlInterpolated($"SELECT * FROM Accounts WITH (UPDLOCK) WHERE Id = {to.Id}")
.FirstOrDefaultAsync();
destAccount.Balance += amount;
await _context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
}
Data Access Patterns
1. Unit of Work Pattern
public class UnitOfWorkPattern
{
// Interface
public interface IUnitOfWork : IDisposable, IAsyncDisposable
{
IRepository<Product> Products { get; }
IRepository<Order> Orders { get; }
IRepository<Category> Categories { get; }
Task<int> SaveChangesAsync();
Task BeginTransactionAsync();
Task CommitTransactionAsync();
Task RollbackTransactionAsync();
}
// Implementation
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
private readonly Dictionary<Type, object> _repositories = new();
private IDbContextTransaction _transaction;
public UnitOfWork(AppDbContext context)
{
_context = context;
}
public IRepository<Product> Products =>
GetRepository<Product>();
public IRepository<Order> Orders =>
GetRepository<Order>();
public IRepository<Category> Categories =>
GetRepository<Category>();
private IRepository<T> GetRepository<T>() where T : class
{
var type = typeof(T);
if (!_repositories.ContainsKey(type))
{
_repositories[type] = new Repository<T>(_context);
}
return (IRepository<T>)_repositories[type];
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public async Task BeginTransactionAsync()
{
_transaction = await _context.Database.BeginTransactionAsync();
}
public async Task CommitTransactionAsync()
{
try
{
await SaveChangesAsync();
await _transaction?.CommitAsync();
}
catch
{
await _transaction?.RollbackAsync();
throw;
}
finally
{
_transaction?.Dispose();
}
}
public async Task RollbackTransactionAsync()
{
try
{
await _transaction?.RollbackAsync();
}
finally
{
_transaction?.Dispose();
}
}
public void Dispose()
{
_transaction?.Dispose();
_context?.Dispose();
}
public async ValueTask DisposeAsync()
{
if (_transaction != null)
await _transaction.DisposeAsync();
if (_context != null)
await _context.DisposeAsync();
}
}
// Usage
public class OrderService
{
private readonly IUnitOfWork _unitOfWork;
public async Task CreateOrderWithProductsAsync(CreateOrderRequest request)
{
await _unitOfWork.BeginTransactionAsync();
try
{
var order = new Order { CustomerId = request.CustomerId };
_unitOfWork.Orders.Add(order);
foreach (var line in request.Lines)
{
var product = await _unitOfWork.Products
.GetByIdAsync(line.ProductId);
// Decrease inventory
product.StockQuantity -= line.Quantity;
_unitOfWork.Products.Update(product);
}
await _unitOfWork.CommitTransactionAsync();
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}
}
}
Resilience Patterns
1. Retry with Exponential Backoff
public class RetryPattern
{
private readonly IAsyncPolicy<HttpResponseMessage> _retryPolicy;
public RetryPattern()
{
_retryPolicy = Policy
.Handle<HttpRequestException>()
.Or<TimeoutException>()
.OrResult<HttpResponseMessage>(r =>
r.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable ||
r.StatusCode == System.Net.HttpStatusCode.GatewayTimeout ||
r.StatusCode == System.Net.HttpStatusCode.RequestTimeout)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
Console.WriteLine($"Retry attempt {retryCount} after {timespan.TotalSeconds}s");
});
}
public async Task<T> GetAsync<T>(string url)
{
var response = await _retryPolicy.ExecuteAsync(async () =>
await _httpClient.GetAsync(url));
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(content);
}
}
2. Bulkhead Pattern - Isolation
public class BulkheadPattern
{
public class IsolatedServiceClient
{
private readonly IAsyncPolicy _bulkheadPolicy;
private readonly HttpClient _httpClient;
public IsolatedServiceClient()
{
// Limit concurrent calls to external service
_bulkheadPolicy = Policy.BulkheadAsync(
maxParallelization: 10, // Max 10 concurrent calls
maxQueuingActions: 5); // Queue up to 5 more
}
public async Task<T> GetAsync<T>(string url)
{
return await _bulkheadPolicy.ExecuteAsync(async () =>
{
var response = await _httpClient.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(content);
});
}
}
}
Event-Driven Patterns
1. Domain Events
public class DomainEventsPattern
{
// Domain Event base
public abstract class DomainEvent
{
public DateTime OccurredAt { get; } = DateTime.UtcNow;
public string AggregateId { get; protected set; }
}
// Concrete events
public class OrderCreatedEvent : DomainEvent
{
public string CustomerId { get; set; }
public decimal Total { get; set; }
public List<OrderLineDTO> Lines { get; set; }
}
public class OrderShippedEvent : DomainEvent
{
public string TrackingNumber { get; set; }
public string CarrierName { get; set; }
}
// Publisher
public interface IDomainEventPublisher
{
Task PublishAsync<T>(T @event) where T : DomainEvent;
Task PublishAllAsync(IEnumerable<DomainEvent> events);
}
public class DomainEventPublisher : IDomainEventPublisher
{
private readonly IMediator _mediator;
public async Task PublishAsync<T>(T @event) where T : DomainEvent
{
await _mediator.Publish(@event);
}
public async Task PublishAllAsync(IEnumerable<DomainEvent> events)
{
foreach (var @event in events)
{
await _mediator.Publish(@event);
}
}
}
// Handler
public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent>
{
private readonly IEmailService _emailService;
private readonly ILogger<OrderCreatedEventHandler> _logger;
public async Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("Order created: {OrderId}", notification.AggregateId);
await _emailService.SendOrderConfirmationAsync(
notification.CustomerId,
notification.Total);
}
}
}
2. Outbox Pattern - Garantie Delivery
public class OutboxPattern
{
// Outbox entity
public class OutboxEvent
{
public long Id { get; set; }
public string EventType { get; set; }
public string EventData { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? PublishedAt { get; set; }
}
// Save to outbox
public async Task PublishOrderAsync(Order order, IEnumerable<DomainEvent> events)
{
var transaction = await _context.Database.BeginTransactionAsync();
try
{
_context.Orders.Add(order);
foreach (var @event in events)
{
_context.OutboxEvents.Add(new OutboxEvent
{
EventType = @event.GetType().Name,
EventData = JsonConvert.SerializeObject(@event),
CreatedAt = DateTime.UtcNow
});
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
// Background job - publish outbox events
public class PublishOutboxEventsJob
{
private readonly AppDbContext _context;
private readonly IEventPublisher _eventPublisher;
public async Task ExecuteAsync()
{
var unpublishedEvents = await _context.OutboxEvents
.Where(e => e.PublishedAt == null)
.OrderBy(e => e.CreatedAt)
.ToListAsync();
foreach (var outboxEvent in unpublishedEvents)
{
try
{
var @event = JsonConvert.DeserializeObject(
outboxEvent.EventData,
Type.GetType(outboxEvent.EventType));
await _eventPublisher.PublishAsync(@event);
outboxEvent.PublishedAt = DateTime.UtcNow;
_context.Update(outboxEvent);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
// Log error, retry logic
Console.WriteLine($"Error publishing event: {ex.Message}");
}
}
}
}
}
Testing Strategies pentru Senior
1. Test Pyramid Architecture
public class TestPyramid
{
// Unit Tests (bottom - 70%)
[TestClass]
public class ProductServiceUnitTests
{
private Mock<IProductRepository> _mockRepository;
private ProductService _service;
[TestInitialize]
public void Setup()
{
_mockRepository = new Mock<IProductRepository>();
_service = new ProductService(_mockRepository.Object);
}
[TestMethod]
public async Task GetProduct_WithValidId_ReturnsProduct()
{
// Arrange
var productId = 1;
var product = new Product { Id = productId, Name = "Test" };
_mockRepository
.Setup(r => r.GetByIdAsync(productId))
.ReturnsAsync(product);
// Act
var result = await _service.GetProductAsync(productId);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(productId, result.Id);
}
}
// Integration Tests (middle - 20%)
[TestClass]
public class ProductRepositoryIntegrationTests
{
private DbContext _context;
[TestInitialize]
public void Setup()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new AppDbContext(options);
}
[TestMethod]
public async Task AddProduct_SavesSuccessfully()
{
// Arrange
var product = new Product { Name = "Test Product", Price = 100 };
// Act
_context.Products.Add(product);
await _context.SaveChangesAsync();
// Assert
var saved = await _context.Products.FirstOrDefaultAsync(p => p.Name == "Test Product");
Assert.IsNotNull(saved);
}
}
// End-to-End Tests (top - 10%)
[TestClass]
public class ProductApiEndToEndTests
{
private HttpClient _client;
private WebApplicationFactory<Program> _factory;
[TestInitialize]
public void Setup()
{
_factory = new WebApplicationFactory<Program>();
_client = _factory.CreateClient();
}
[TestMethod]
public async Task CreateProduct_WithValidData_Returns201()
{
// Arrange
var request = new CreateProductRequest { Name = "Test", Price = 100 };
var content = new StringContent(
JsonConvert.SerializeObject(request),
Encoding.UTF8,
"application/json");
// Act
var response = await _client.PostAsync("/api/products", content);
// Assert
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
}
}
}
2. TestBed Pattern - Complex Scenarios
public class TestBedPattern
{
// Setup complex test environment
public class OrderServiceTestBed
{
private readonly IServiceProvider _serviceProvider;
private readonly AppDbContext _context;
private readonly Mock<IEventPublisher> _eventPublisher;
private readonly Mock<IPaymentService> _paymentService;
public OrderServiceTestBed()
{
var services = new ServiceCollection();
// Register all dependencies
services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase(Guid.NewGuid().ToString()));
services.AddScoped<OrderService>();
services.AddScoped<IProductRepository, ProductRepository>();
_eventPublisher = new Mock<IEventPublisher>();
services.AddScoped(sp => _eventPublisher.Object);
_paymentService = new Mock<IPaymentService>();
services.AddScoped(sp => _paymentService.Object);
_serviceProvider = services.BuildServiceProvider();
_context = _serviceProvider.GetRequiredService<AppDbContext>();
SeedData();
}
private void SeedData()
{
_context.Categories.Add(new Category { Id = 1, Name = "Electronics" });
_context.Products.Add(new Product
{
Id = 1,
Name = "Laptop",
Price = 1000,
CategoryId = 1
});
_context.SaveChanges();
}
public async Task<Order> CreateOrderAsync()
{
var orderService = _serviceProvider.GetRequiredService<OrderService>();
return await orderService.CreateOrderAsync(new CreateOrderRequest
{
CustomerId = "CUST1",
Lines = new[] { new { ProductId = 1, Quantity = 2 } }
});
}
public void VerifyEventPublished<T>(Times times = null) where T : DomainEvent
{
times ??= Times.Once();
_eventPublisher.Verify(
p => p.PublishAsync(It.IsAny<T>()),
times);
}
}
}
Production Monitoring Strategies
1. Distributed Tracing
public class DistributedTracingStrategy
{
// Setup OpenTelemetry
public class TraceConfiguration
{
public static void ConfigureTracing(IServiceCollection services)
{
services
.AddOpenTelemetry()
.WithTracing(tracerProvider =>
{
tracerProvider
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSqlClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddJaegerExporter(options =>
{
options.AgentHost = "localhost";
options.AgentPort = 6831;
});
});
}
}
// Custom activity creation
public class CustomActivityService
{
private const string ActivitySourceName = "ProductService";
private readonly ActivitySource _activitySource;
public CustomActivityService()
{
_activitySource = new ActivitySource(ActivitySourceName);
}
public async Task<Product> ProcessProductAsync(int id)
{
using (var activity = _activitySource.StartActivity("ProcessProduct"))
{
activity?.SetTag("product.id", id);
var product = await FetchProductAsync(id);
activity?.SetTag("product.price", product.Price);
return product;
}
}
}
}
2. Metrics Collection
public class MetricsCollectionStrategy
{
public class MetricsService
{
private readonly Counter<int> _requestCounter;
private readonly Histogram<double> _responseTimeHistogram;
private readonly UpDownCounter<int> _activeConnections;
public MetricsService(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("ProductService");
_requestCounter = meter.CreateCounter<int>(
"http.requests",
description: "Total HTTP requests");
_responseTimeHistogram = meter.CreateHistogram<double>(
"http.response_time",
unit: "ms",
description: "HTTP response time");
_activeConnections = meter.CreateUpDownCounter<int>(
"db.connections.active",
description: "Active database connections");
}
public void RecordRequest(string method, double responseTime)
{
_requestCounter.Add(1, new KeyValuePair<string, object>("method", method));
_responseTimeHistogram.Record(responseTime);
}
public void IncrementConnections()
{
_activeConnections.Add(1);
}
public void DecrementConnections()
{
_activeConnections.Add(-1);
}
}
}
Concluzie
DiferenΘiatorii unui Senior Developer:
- Design Thinking - Arquitecturi scalabile Θi maintainabile
- Performance Awareness - Profilering Θi optimizare constant
- Production Readiness - Error handling, monitoring, resilience
- Testing Discipline - PiraΘia testelor corespunzΔtoare
- Mentoring Capacity - ExplicΔ Θi transmite cunoΘtinΘe
- Business Understanding - Nu doar cod, dar Θi context
Date: 2025 C# Version: 12+ .NET Version: 8+