# 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`