# Templates Backend - Application Skill

> Ces templates génèrent le code C# pour les nouvelles applications/modules.

---

## TEMPLATE: PERMISSIONS CLASS

```csharp
// Ajouter dans src/SmartStack.Application/Common/Authorization/Permissions.cs

public static class $CONTEXT_PASCAL
{
    public const string Access = "$context.$application";

    public static class $MODULE_PASCAL
    {
        public const string View = "$context.$application.$module.read";
        public const string Create = "$context.$application.$module.create";
        public const string Update = "$context.$application.$module.update";
        public const string Delete = "$context.$application.$module.delete";
        public const string Assign = "$context.$application.$module.assign";
        public const string Execute = "$context.$application.$module.execute";
    }
}
```

### Exemple pour ERP > Sales > Products

```csharp
public static class Erp
{
    public const string Access = "erp.sales";

    public static class Products
    {
        public const string View = "erp.sales.products.read";
        public const string Create = "erp.sales.products.create";
        public const string Update = "erp.sales.products.update";
        public const string Delete = "erp.sales.products.delete";
    }
}
```

---

## TEMPLATE: SERVICE INTERFACE

```csharp
// src/SmartStack.Application/Common/Interfaces/I{Module}Service.cs

using SmartStack.Application.$MODULE_PASCAL.Models;

namespace SmartStack.Application.Common.Interfaces;

public interface I$MODULE_PASCALService
{
    Task<PagedResult<$ENTITY_PASCALDto>> GetAllAsync(
        $ENTITY_PASCALQueryParameters parameters,
        CancellationToken cancellationToken = default);

    Task<$ENTITY_PASCALDto?> GetByIdAsync(
        Guid id,
        CancellationToken cancellationToken = default);

    Task<$ENTITY_PASCALDto> CreateAsync(
        Create$ENTITY_PASCALRequest request,
        CancellationToken cancellationToken = default);

    Task<$ENTITY_PASCALDto> UpdateAsync(
        Guid id,
        Update$ENTITY_PASCALRequest request,
        CancellationToken cancellationToken = default);

    Task<bool> DeleteAsync(
        Guid id,
        CancellationToken cancellationToken = default);
}
```

---

## TEMPLATE: SERVICE IMPLEMENTATION

```csharp
// src/SmartStack.Infrastructure/Services/$MODULE_PASCAL/$MODULE_PASCALService.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SmartStack.Application.Common.Interfaces;
using SmartStack.Application.$MODULE_PASCAL.Models;
using SmartStack.Domain.$MODULE_PASCAL;

namespace SmartStack.Infrastructure.Services.$MODULE_PASCAL;

public class $MODULE_PASCALService : I$MODULE_PASCALService
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;
    private readonly ILogger<$MODULE_PASCALService> _logger;

    public $MODULE_PASCALService(
        IApplicationDbContext context,
        ICurrentUserService currentUser,
        ILogger<$MODULE_PASCALService> logger)
    {
        _context = context;
        _currentUser = currentUser;
        _logger = logger;
    }

    public async Task<PagedResult<$ENTITY_PASCALDto>> GetAllAsync(
        $ENTITY_PASCALQueryParameters parameters,
        CancellationToken cancellationToken = default)
    {
        var query = _context.$ENTITY_PLURAL
            .AsNoTracking();

        // Apply filters
        if (!string.IsNullOrWhiteSpace(parameters.SearchTerm))
        {
            query = query.Where(x =>
                x.Name.Contains(parameters.SearchTerm) ||
                x.Description.Contains(parameters.SearchTerm));
        }

        // Apply sorting
        query = parameters.SortColumn?.ToLower() switch
        {
            "name" => parameters.SortDirection == "desc"
                ? query.OrderByDescending(x => x.Name)
                : query.OrderBy(x => x.Name),
            "createdat" => parameters.SortDirection == "desc"
                ? query.OrderByDescending(x => x.CreatedAt)
                : query.OrderBy(x => x.CreatedAt),
            _ => query.OrderByDescending(x => x.CreatedAt)
        };

        var totalCount = await query.CountAsync(cancellationToken);

        var items = await query
            .Skip((parameters.Page - 1) * parameters.PageSize)
            .Take(parameters.PageSize)
            .Select(x => new $ENTITY_PASCALDto
            {
                Id = x.Id,
                Name = x.Name,
                Description = x.Description,
                IsActive = x.IsActive,
                CreatedAt = x.CreatedAt,
                UpdatedAt = x.UpdatedAt
            })
            .ToListAsync(cancellationToken);

        return new PagedResult<$ENTITY_PASCALDto>
        {
            Items = items,
            TotalCount = totalCount,
            Page = parameters.Page,
            PageSize = parameters.PageSize
        };
    }

    public async Task<$ENTITY_PASCALDto?> GetByIdAsync(
        Guid id,
        CancellationToken cancellationToken = default)
    {
        var entity = await _context.$ENTITY_PLURAL
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id, cancellationToken);

        if (entity == null)
            return null;

        return new $ENTITY_PASCALDto
        {
            Id = entity.Id,
            Name = entity.Name,
            Description = entity.Description,
            IsActive = entity.IsActive,
            CreatedAt = entity.CreatedAt,
            UpdatedAt = entity.UpdatedAt
        };
    }

    public async Task<$ENTITY_PASCALDto> CreateAsync(
        Create$ENTITY_PASCALRequest request,
        CancellationToken cancellationToken = default)
    {
        var entity = $ENTITY_PASCAL.Create(
            request.Name,
            request.Description);

        _context.$ENTITY_PLURAL.Add(entity);
        await _context.SaveChangesAsync(cancellationToken);

        _logger.LogInformation(
            "User {User} created $ENTITY_PASCAL {Id} ({Name})",
            _currentUser.Email,
            entity.Id,
            entity.Name);

        return new $ENTITY_PASCALDto
        {
            Id = entity.Id,
            Name = entity.Name,
            Description = entity.Description,
            IsActive = entity.IsActive,
            CreatedAt = entity.CreatedAt
        };
    }

    public async Task<$ENTITY_PASCALDto> UpdateAsync(
        Guid id,
        Update$ENTITY_PASCALRequest request,
        CancellationToken cancellationToken = default)
    {
        var entity = await _context.$ENTITY_PLURAL
            .FirstOrDefaultAsync(x => x.Id == id, cancellationToken)
            ?? throw new NotFoundException("$ENTITY_PASCAL", id);

        entity.Update(request.Name, request.Description);

        await _context.SaveChangesAsync(cancellationToken);

        _logger.LogInformation(
            "User {User} updated $ENTITY_PASCAL {Id} ({Name})",
            _currentUser.Email,
            entity.Id,
            entity.Name);

        return new $ENTITY_PASCALDto
        {
            Id = entity.Id,
            Name = entity.Name,
            Description = entity.Description,
            IsActive = entity.IsActive,
            CreatedAt = entity.CreatedAt,
            UpdatedAt = entity.UpdatedAt
        };
    }

    public async Task<bool> DeleteAsync(
        Guid id,
        CancellationToken cancellationToken = default)
    {
        var entity = await _context.$ENTITY_PLURAL
            .FirstOrDefaultAsync(x => x.Id == id, cancellationToken);

        if (entity == null)
            return false;

        _context.$ENTITY_PLURAL.Remove(entity);
        await _context.SaveChangesAsync(cancellationToken);

        _logger.LogWarning(
            "User {User} deleted $ENTITY_PASCAL {Id} ({Name})",
            _currentUser.Email,
            id,
            entity.Name);

        return true;
    }
}
```

---

## TEMPLATE: DTOs

```csharp
// src/SmartStack.Application/$MODULE_PASCAL/Models/$ENTITY_PASCALDto.cs

namespace SmartStack.Application.$MODULE_PASCAL.Models;

public record $ENTITY_PASCALDto
{
    public Guid Id { get; init; }
    public string Name { get; init; } = null!;
    public string? Description { get; init; }
    public bool IsActive { get; init; }
    public DateTime CreatedAt { get; init; }
    public DateTime? UpdatedAt { get; init; }
}

public record Create$ENTITY_PASCALRequest
{
    public string Name { get; init; } = null!;
    public string? Description { get; init; }
}

public record Update$ENTITY_PASCALRequest
{
    public string Name { get; init; } = null!;
    public string? Description { get; init; }
}

public record $ENTITY_PASCALQueryParameters
{
    public int Page { get; init; } = 1;
    public int PageSize { get; init; } = 10;
    public string? SearchTerm { get; init; }
    public string? SortColumn { get; init; } = "createdAt";
    public string? SortDirection { get; init; } = "desc";
}
```

---

## TEMPLATE: DOMAIN ENTITY

```csharp
// src/SmartStack.Domain/$MODULE_PASCAL/$ENTITY_PASCAL.cs

using SmartStack.Domain.Common;

namespace SmartStack.Domain.$MODULE_PASCAL;

public class $ENTITY_PASCAL : BaseEntity
{
    public string Name { get; private set; } = null!;
    public string? Description { get; private set; }
    public bool IsActive { get; private set; }

    private $ENTITY_PASCAL() { }

    public static $ENTITY_PASCAL Create(string name, string? description = null)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new Exceptions.DomainException("Name is required");

        return new $ENTITY_PASCAL
        {
            Id = Guid.NewGuid(),
            Name = name,
            Description = description,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };
    }

    public void Update(string name, string? description)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new Exceptions.DomainException("Name is required");

        Name = name;
        Description = description;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Activate() => IsActive = true;
    public void Deactivate() => IsActive = false;
}
```

---

## TEMPLATE: EF CONFIGURATION

```csharp
// src/SmartStack.Infrastructure/Persistence/Configurations/$MODULE_PASCAL/$ENTITY_PASCALConfiguration.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using SmartStack.Domain.$MODULE_PASCAL;

namespace SmartStack.Infrastructure.Persistence.Configurations.$MODULE_PASCAL;

public class $ENTITY_PASCALConfiguration : IEntityTypeConfiguration<$ENTITY_PASCAL>
{
    public void Configure(EntityTypeBuilder<$ENTITY_PASCAL> builder)
    {
        builder.ToTable("$ENTITY_PLURAL", "$SCHEMA");

        builder.HasKey(x => x.Id);

        builder.Property(x => x.Name)
            .HasMaxLength(200)
            .IsRequired();

        builder.Property(x => x.Description)
            .HasMaxLength(1000);

        builder.HasIndex(x => x.Name)
            .IsUnique();
    }
}
```

---

## TEMPLATE: DBCONTEXT UPDATE

```csharp
// Ajouter dans IApplicationDbContext.cs
DbSet<$ENTITY_PASCAL> $ENTITY_PLURAL { get; }

// Ajouter dans ApplicationDbContext.cs
public DbSet<$ENTITY_PASCAL> $ENTITY_PLURAL => Set<$ENTITY_PASCAL>();
```

---

## TEMPLATE: DI REGISTRATION

```csharp
// Ajouter dans src/SmartStack.Infrastructure/DependencyInjection.cs

services.AddScoped<I$MODULE_PASCALService, $MODULE_PASCALService>();
```

---

## CHAÎNAGE AVEC /controller:create

Après génération des templates backend, le skill appelle automatiquement :

```
/controller:create $AREA $MODULE $ENTITY
```

Exemple :
```
/controller:create Business Products Product
```

Cela génère :
- Controller avec CRUD complet
- Endpoints REST
- ProducesResponseType
- Logging approprié
- Tests Postman

---

## CHECKLIST BACKEND

| Vérification | Status |
|--------------|--------|
| ☐ Permissions.cs mis à jour | |
| ☐ Service interface créée | |
| ☐ Service implementation créée | |
| ☐ DTOs créés | |
| ☐ Domain entity créée | |
| ☐ EF Configuration créée | |
| ☐ DbContext mis à jour | |
| ☐ DI registration ajoutée | |
| ☐ /controller:create exécuté | |
| ☐ dotnet build réussi | |
