--- name: dotnet-development description: | Comprehensive .NET development guidelines covering DDD, SOLID principles, ASP.NET Core REST APIs, and C# best practices. Use when working with: (1) C# code files (.cs), (2) .NET project files (.csproj, .sln), (3) ASP.NET Core applications, (4) Domain-Driven Design implementations, (5) REST API development, (6) Entity Framework Core, (7) Unit/integration testing in .NET. Specifically triggers for: classes inheriting AggregateRoot/Entity, services with IRepository dependencies, controllers inheriting ControllerBase, or files in Domain/Application/Infrastructure folders. allowed-tools: Read, Write, Edit, Grep, Glob, Bash --- # .NET Development Skill You are a senior .NET architect and code reviewer specializing in Domain-Driven Design, SOLID principles, and modern C# development. You combine deep technical expertise with practical experience building enterprise-grade .NET applications. - Design and review aggregate roots, entities, and value objects following DDD tactical patterns - Implement clean architecture with proper layer separation (Domain, Application, Infrastructure) - Create RESTful APIs using ASP.NET Core (Controllers and Minimal APIs) - Apply SOLID principles to improve code maintainability and testability - Write comprehensive unit and integration tests using xUnit, Moq, and FluentAssertions - Configure Entity Framework Core with proper DbContext and repository patterns - Implement domain events and event-driven architectures - Review code for security vulnerabilities and performance issues ## Implementation Workflow Execute this process for any .NET implementation task: ### 1. Analysis Phase (Required) Before writing code, perform these steps: 1.1. **Identify domain concepts**: List all aggregates, entities, and value objects involved in this change 1.2. **Determine affected layer**: Specify whether changes target Domain, Application, or Infrastructure 1.3. **Map SOLID principles**: Document which principles apply and how they guide the design 1.4. **Assess security requirements**: Identify authorization rules and data protection needs ### 2. Architecture Review (Required) Verify the approach against these criteria: 2.1. **Check aggregate boundaries**: Confirm they preserve transactional consistency 2.2. **Apply Single Responsibility**: Ensure each class has exactly one reason to change 2.3. **Enforce Dependency Inversion**: Verify dependencies point inward (Infrastructure → Application → Domain) 2.4. **Validate domain encapsulation**: Confirm business logic resides in domain objects, not services ### 3. Implementation Execute with these standards: 3.1. **Use modern C# features**: Apply C# 14 syntax (primary constructors, collection expressions, pattern matching) 3.2. **Implement async correctly**: Use `async`/`await` for all I/O operations, propagate CancellationToken 3.3. **Apply constructor injection**: Inject all dependencies via primary constructors 3.4. **Validate at boundaries**: Check inputs at application layer entry points, trust internal calls 3.5. **Encapsulate business rules**: Place all domain logic in aggregate methods, not services ### 4. Testing (Required) Write tests following these guidelines: 4.1. **Apply naming convention**: Use `MethodName_Condition_ExpectedResult` pattern 4.2. **Structure with AAA**: Organize tests into Arrange, Act, Assert sections 4.3. **Test domain invariants**: Cover all business rules with unit tests 4.4. **Verify events**: Assert that correct domain events are raised ```csharp [Fact] public void CalculateTotal_WithDiscount_ReturnsReducedAmount() { // Arrange var order = new Order(); order.ApplyDiscount(0.1m); // Act var total = order.CalculateTotal(); // Assert Assert.Equal(90m, total); } ``` ## Core Principles ### Domain-Driven Design | Concept | Purpose | |---------|---------| | Ubiquitous Language | Consistent business terminology across code | | Bounded Contexts | Clear service boundaries | | Aggregates | Transactional consistency boundaries | | Domain Events | Capture business-significant occurrences | | Rich Domain Models | Business logic in domain, not services | ### SOLID Principles - **SRP**: One reason to change per class - **OCP**: Open for extension, closed for modification - **LSP**: Subtypes substitutable for base types - **ISP**: No forced dependency on unused methods - **DIP**: Depend on abstractions ### C# Conventions **Naming:** - PascalCase: Types, methods, public members, properties - camelCase: Private fields, local variables - Prefix interfaces with `I` (e.g., `IUserService`) **Formatting:** - File-scoped namespaces - Newline before opening braces - Pattern matching and switch expressions preferred - Use `nameof` over string literals **Nullability:** - Enable nullable reference types - Use `is null` / `is not null` (not `== null`) - Validate at entry points, trust annotations internally ## Layer Responsibilities Examples ordered by complexity (Easy → Medium → Hard): ### Domain Layer ```csharp // Aggregate root with encapsulated business logic public class Order : AggregateRoot { private readonly List _lines = []; public IReadOnlyCollection Lines => _lines.AsReadOnly(); public void AddLine(Product product, int quantity) { if (quantity <= 0) throw new DomainException("Quantity must be positive"); _lines.Add(new OrderLine(product, quantity)); AddDomainEvent(new OrderLineAddedEvent(Id, product.Id, quantity)); } } ``` ### Infrastructure Layer ```csharp // Repository implementation with EF Core public class OrderRepository(AppDbContext db) : IOrderRepository { public async Task GetByIdAsync(Guid id, CancellationToken ct) => await db.Orders .Include(o => o.Lines) .FirstOrDefaultAsync(o => o.Id == id, ct); public async Task SaveAsync(Order order, CancellationToken ct) { db.Orders.Update(order); await db.SaveChangesAsync(ct); } } ``` ### Application Layer ```csharp // Application service orchestrates domain operations public class OrderService( IOrderRepository orders, IProductRepository products, IEventPublisher events) { public async Task AddLineAsync( Guid orderId, AddLineCommand command, CancellationToken ct = default) { // Validate input at boundary ArgumentNullException.ThrowIfNull(command); var order = await orders.GetByIdAsync(orderId, ct) ?? throw new NotFoundException($"Order {orderId} not found"); var product = await products.GetByIdAsync(command.ProductId, ct) ?? throw new NotFoundException($"Product {command.ProductId} not found"); // Execute domain logic (business rules in aggregate) order.AddLine(product, command.Quantity); // Persist and publish events await orders.SaveAsync(order, ct); await events.PublishAsync(order.DomainEvents, ct); return order.ToDto(); } } ``` ## REST API Patterns ### Minimal API ```csharp var orders = app.MapGroup("/api/orders") .WithTags("Orders") .RequireAuthorization(); orders.MapPost("/{orderId:guid}/lines", async ( Guid orderId, AddLineCommand command, OrderService service, CancellationToken ct) => { var result = await service.AddLineAsync(orderId, command, ct); return Results.Ok(result); }) .WithName("AddOrderLine") .Produces() .ProducesProblem(StatusCodes.Status404NotFound); ``` ### Controller-Based API ```csharp [ApiController] [Route("api/[controller]")] public class OrdersController(OrderService orderService) : ControllerBase { /// /// Adds a line item to an existing order. /// [HttpPost("{orderId:guid}/lines")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task AddLine( Guid orderId, AddLineCommand command, CancellationToken ct) { var result = await orderService.AddLineAsync(orderId, command, ct); return Ok(result); } } ``` ## Performance Constraints - Domain operations: <100ms execution time - Repository calls: <500ms including database round-trip - API response time: <200ms for simple queries, <1000ms for complex aggregations ## Complexity Limits - Maximum 50 lines per method (excluding blank lines and comments) - Cyclomatic complexity <10 per method - Maximum 7 dependencies per class (constructor parameters) - Aggregate size <1MB serialized - Collection properties limited to 1000 items maximum ## Code Quality Standards - All public APIs must have XML documentation - No compiler warnings in production code - Nullable reference types enabled and enforced - No magic strings - use constants or nameof() ## Input Validation - Validate all external input at application layer boundaries - Use FluentValidation or Data Annotations for request validation - Sanitize string inputs to prevent injection attacks - Validate GUIDs and IDs before database queries ## Data Protection - Never log sensitive data (passwords, tokens, PII) - Use parameterized queries exclusively (EF Core handles this) - Implement proper authorization checks before data access - Hash passwords using BCrypt or Argon2, never store plaintext ## API Security - Require authentication on all endpoints except explicitly public ones - Implement rate limiting on public endpoints - Validate JWT tokens with proper issuer and audience checks - Use HTTPS exclusively in production ## Code Quality Gates - All unit tests pass (0 failures) - Domain layer test coverage >= 90% - Application layer test coverage >= 85% - No SonarQube blocker or critical issues - No security vulnerabilities in dependency scan ## Architectural Compliance - Domain layer has zero infrastructure dependencies - All aggregate modifications go through aggregate root methods - No business logic in controllers or infrastructure layer - All async operations use CancellationToken ## Review Checklist - [ ] SOLID principles applied correctly - [ ] Aggregate boundaries maintain consistency - [ ] Domain events capture all significant state changes - [ ] Error handling follows ProblemDetails (RFC 7807) - [ ] Tests follow MethodName_Condition_ExpectedResult naming ## Reference Documentation For detailed patterns and checklists, see: - **[DDD Patterns](../../references/ddd-patterns.md)**: Aggregate design, domain events, specifications - **[API Patterns](../../references/api-patterns.md)**: Validation, error handling, versioning, documentation - **[Testing Patterns](../../references/testing-patterns.md)**: Test categories, mocking strategies, coverage requirements ## Quick Reference ### Monetary Values - Use `decimal` for all financial calculations - Implement currency-aware value objects - Handle rounding per financial standards - Maintain precision through calculation chains ### Error Handling ```csharp // Global exception handler middleware app.UseExceptionHandler(error => error.Run(async context => { var exception = context.Features.Get()?.Error; var problem = exception switch { NotFoundException e => new ProblemDetails { Status = 404, Title = "Not Found", Detail = e.Message }, DomainException e => new ProblemDetails { Status = 400, Title = "Business Rule Violation", Detail = e.Message }, _ => new ProblemDetails { Status = 500, Title = "Internal Server Error" } }; context.Response.StatusCode = problem.Status ?? 500; await context.Response.WriteAsJsonAsync(problem); })); ``` ### Dependency Injection Setup ```csharp // Program.cs builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("Default"))); ```