# Architecture At A Glance ## 📐 The 4-Layer Architecture ``` ┌──────────────────────────────────────────────────────┐ │ 🌐 API LAYER (Controllers) │ │ - HTTP Endpoints │ │ - Authentication │ │ - Exception → HTTP Status Code mapping │ │ ❌ NO Business Logic │ │ ❌ NO Database Access │ └─────────────────┬────────────────────────────────────┘ │ │ Calls ▼ ┌──────────────────────────────────────────────────────┐ │ 💼 SERVICE LAYER (Business Logic) │ │ - FluentValidation │ │ - Business Rules │ │ - Orchestration │ │ - DTO Transformations │ │ ❌ NO HTTP Concerns │ │ ❌ NO Database Access │ └─────────────────┬────────────────────────────────────┘ │ │ Calls ▼ ┌──────────────────────────────────────────────────────┐ │ 💾 DATA SERVICE LAYER (Persistence) │ │ - CRUD Operations │ │ - Database Queries │ │ - Audit Field Management │ │ ❌ NO Validation │ │ ❌ NO Business Logic │ └─────────────────┬────────────────────────────────────┘ │ │ Uses ▼ ┌──────────────────────────────────────────────────────┐ │ 🗄️ DATA LAYER (EF Core + Entities) │ │ - DbContext │ │ - Entity Models (POCOs) │ │ - Migrations │ └──────────────────────────────────────────────────────┘ ``` --- ## 🎯 Responsibilities Matrix | Layer | What It Does | What It Doesn't Do | |-------|--------------|-------------------| | **Controller** | • Handle HTTP requests
• Authenticate users
• Map API DTOs
• Catch exceptions
• Return HTTP status codes | • Business logic
• Validation rules
• Database queries | | **Service** | • Validate data (FluentValidation)
• Apply business rules
• Coordinate operations
• Transform data | • HTTP concerns
• Database operations | | **DataService** | • Execute database queries
• CRUD operations
• Set audit fields | • Validation
• Business rules | | **Data** | • Define entities
• Database schema | • Application logic | --- ## 🔄 Request Flow ### Creating a Contact (Example) ``` 1. HTTP POST /api/Contact/Create ↓ 2. ContactController.Create() - Checks authentication - Gets userId from claims ↓ 3. ContactService.CreateContactAsync() - Validates using FluentValidation - Applies business rules ↓ 4. ContactDataService.CreateAsync() - Creates entity - Sets audit fields - Saves to database ↓ 5. Returns through layers: Entity → DataDTO → ServiceDTO → ApiDTO → HTTP Response ``` --- ## 📦 DTO Flow ``` Frontend (JSON) ↓ API DTO (Contact_DTO) ← Controller receives this ↓ Service DTO (CreateContactDTO) ← Service validates this ↓ Data DTO (CreateContactDataDTO) ← DataService uses this ↓ Entity (Contact) ← Database stores this ``` --- ## 🔌 Dependency Injection ### Automatic Assembly Scanning Services auto-register via assembly scanning (no manual registration needed). ```csharp // Program.cs builder.Services.AddDataServices(); // Auto-register data services builder.Services.AddBusinessServices(); // Auto-register business services + validators ``` ### Naming Convention (REQUIRED) | Interface | Implementation | Registered? | |-----------|---------------|-------------| | `IContactService` | `ContactService` | ✅ YES | | `ISomething` | `SomethingElse` | ❌ NO | **Rule**: Interface = `I` + Class name exactly --- ## ✅ Validation ### Where and How ``` ❌ Controller → Catches ValidationException ✅ Service → Runs FluentValidation.Validate() ❌ DataService → No validation ``` ### FluentValidation Example ```csharp public class CreateContactDTOValidator : AbstractValidator { public CreateContactDTOValidator() { RuleFor(x => x.FirstName) .NotEmpty().WithMessage("First name is required") .MaximumLength(50); RuleFor(x => x.Email) .EmailAddress().WithMessage("Invalid email"); } } ``` ### In Service ```csharp var validationResult = await _validator.ValidateAsync(dto); if (!validationResult.IsValid) { throw new ValidationException(validationResult.Errors); } ``` --- ## 🚨 Exception Handling ### Standard Pattern (All Controllers) ```csharp try { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (userId == null) return Unauthorized(...); await _service.SomeMethod(dto, userId); return Ok(...); } catch (ValidationException vex) // 400 Bad Request { return BadRequest(...); } catch (KeyNotFoundException) // 404 Not Found { return NotFound(...); } catch (Exception ex) // 500 Internal Server Error { return StatusCode(500, ...); } ``` --- ## 🔐 Authentication ### Getting User ID ```csharp // Method 1: Claims (Preferred) var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // Method 2: UserManager (for Identity) var userId = _userManager.GetUserId(User); // Always check for null if (userId == null) return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" }); ``` ### Audit Fields Every entity has: ```csharp public string CreatedBy { get; set; } // User ID (GUID) public DateTime CreatedDate { get; set; } // UTC timestamp public string ModifiedBy { get; set; } // User ID (GUID) public DateTime ModifiedDate { get; set; } // UTC timestamp ``` Set in DataService: ```csharp CreatedBy = userId, CreatedDate = DateTime.UtcNow, ModifiedBy = userId, ModifiedDate = DateTime.UtcNow ``` --- ## 📋 Naming Conventions | Component | Pattern | Example | |-----------|---------|---------| | Entity | `[Entity]` | `Contact` | | Controller | `[Entity]Controller` | `ContactController` | | Service Interface | `I[Entity]Service` | `IContactService` | | Service Class | `[Entity]Service` | `ContactService` | | DataService Interface | `I[Entity]DataService` | `IContactDataService` | | DataService Class | `[Entity]DataService` | `ContactDataService` | | API DTO | `[Entity]_DTO` | `Contact_DTO` | | Service DTO | `[Action][Entity]DTO` | `CreateContactDTO` | | Data DTO | `[Action][Entity]DataDTO` | `CreateContactDataDTO` | | Validator | `[DTO]Validator` | `CreateContactDTOValidator` | --- ## 🎨 Code Templates ### Controller Method Template ```csharp [HttpPost("ActionName")] public async Task ActionName([FromBody] SomeDTO dto) { try { var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); if (userId == null) return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" }); await _service.Method(dto, userId); return Ok(new Response { Status = "Success", Message = "Success message" }); } catch (ValidationException vex) { var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage)); return BadRequest(new Response { Status = "Validation Error", Message = errors }); } catch (KeyNotFoundException) { return NotFound(new Response { Status = "Error", Message = "Not found" }); } catch (Exception ex) { return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); } } ``` ### Service Method Template ```csharp public async Task MethodAsync(InputDTO dto, string userId) { // 1. Validate var validationResult = await _validator.ValidateAsync(dto); if (!validationResult.IsValid) { throw new ValidationException(validationResult.Errors); } // 2. Business logic (if any) // 3. Call DataService var result = await _dataService.MethodAsync(dataDto, userId); // 4. Map and return return MapToServiceDTO(result); } ``` ### DataService Method Template ```csharp public async Task MethodAsync(InputDataDTO dto, string userId) { // 1. Create/modify entity var entity = new Entity { Property = dto.Property, CreatedBy = userId, CreatedDate = DateTime.UtcNow, ModifiedBy = userId, ModifiedDate = DateTime.UtcNow }; // 2. Database operation _db.Entities.Add(entity); await _db.SaveChangesAsync(); // 3. Map and return return MapToDataDTO(entity); } ``` --- ## 📚 Key Rules 1. **Never skip layers** - Always go Controller → Service → DataService 2. **No direct `_db` in Controllers** - Database access only in DataService 3. **Validate in Service** - Use FluentValidation, not manual checks 4. **Always check userId** - Return 401 if null 5. **Catch ValidationException first** - Before generic Exception 6. **Use interfaces everywhere** - For dependency injection 7. **Set audit fields** - CreatedBy, CreatedDate, ModifiedBy, ModifiedDate 8. **Return proper HTTP codes** - 200, 400, 401, 404, 500 --- ## 🎓 Learn More - **Full Details**: See `BACKEND_ARCHITECTURE.md` - **Step-by-Step Guide**: See `QUICK_START_GUIDE.md` - **Working Examples**: Look at `ContactController`, `ContactService`, `ContactDataService`