BTW.CleanArchitecture.Template 2.1.0

dotnet new install BTW.CleanArchitecture.Template::2.1.0
                    
This package contains a .NET Template Package you can call from the shell/command line.

BTW Clean Architecture Template

Plantilla dotnet new para proyectos .NET 10 con Clean Architecture, PostgreSQL, observabilidad y gestión de secretos opcionales.

Instalación

dotnet new install BTW.CleanArchitecture.Template

Uso

dotnet new clean-arch -n MiProyecto

Parámetros opcionales

Parámetro Tipo Default Descripción
--loki bool true Integración con Grafana Loki vía BTW.Logging.Loki
--infisical bool true Gestión de secretos con Infisical vía BTW.SecretsProvider
--grpc bool false Servicio gRPC con contrato Protobuf

Los parámetros bool son switches: presencia activa la opción, false explícito la desactiva.

# Sin Infisical ni Loki, solo REST + PostgreSQL
dotnet new clean-arch -n MiProyecto --loki false --infisical false

# Con gRPC habilitado
dotnet new clean-arch -n MiProyecto --grpc

# Todo incluido
dotnet new clean-arch -n MiProyecto --grpc

Prerrequisitos

  • .NET 10 SDK
  • Docker + Docker Compose (para levantar PostgreSQL, Prometheus, Grafana y opcionalmente Infisical/Loki)

Estructura del proyecto

MiProyecto/
├── Api/                        # Capa de presentación
│   ├── Controllers/            # Endpoints REST
│   ├── GrpcServices/           # Servicios gRPC (--grpc)
│   ├── Protos/                 # Contratos Protobuf (--grpc)
│   ├── appsettings.json
│   └── Program.cs
├── Application/                # Lógica de negocio
│   ├── DTO/
│   │   ├── Request/            # SampleRequest, PagedRequest
│   │   └── Response/           # SampleResponse, ApiResponse<T>, PagedResult<T>
│   ├── Exceptions/             # AppException, NotFoundException, ConflictException
│   ├── Interfaces/
│   │   ├── Repositories/       # IRepository<T>, ISampleRepository
│   │   ├── Services/           # ISampleService
│   │   ├── UnitOfWork/         # IUnitOfWork
│   │   ├── Metrics/            # ISampleMetrics
│   │   └── Api/                # IApiService
│   ├── Mapping/                # Configuración Mapster
│   └── Services/               # SampleService
├── Domain/                     # Entidades y abstracciones
│   └── Entities/
│       ├── Abstractions/       # IAuditableEntity
│       └── Sample.cs
├── Infrastructure/             # Acceso a datos y servicios externos
│   ├── Context/
│   │   ├── Configurations/     # IEntityTypeConfiguration<T>
│   │   └── SampleDbContext.cs
│   ├── Interceptors/           # AuditInterceptor (EF Core)
│   ├── Repositories/           # Repository<T>, SampleRepository
│   ├── UnitOfWork/             # UnitOfWork
│   └── Api/                    # ApiService (HTTP client)
├── Host/                       # Composición y startup
│   ├── Extensions/             # ServiceCollectionExtensions, GrpcExtensions
│   ├── Metrics/                # SampleMetrics (Prometheus)
│   ├── Middlewares/            # GlobalExceptionHandler
│   └── HostExtensions.cs
├── Shared/                     # Utilidades transversales
│   ├── Constants/              # Messages
│   └── Helpers/                # SHA256Helper
├── docker-compose.yml          # Base: imagen + healthcheck + red
├── docker-compose.override.yml # Dev local (VS): build + infra completa
├── docker-compose.test.yml     # Sobreescritura para ambiente test
├── docker-compose.prod.yml     # Sobreescritura para producción
├── deploy.sh                   # Script despliegue Linux (compose/swarm)
├── deploy.ps1                  # Script despliegue Windows (compose/swarm)
├── .env.example                # Variables para dev local
├── .env.test.example           # Variables para test
└── .env.prod.example           # Variables para producción

Stack técnico

Componente Tecnología
Base de datos PostgreSQL 17 (Npgsql EF Core)
Mapping Mapster 10
Observabilidad Prometheus + Grafana
Logging BTW.Logging.Loki → Grafana Loki (opcional)
Secretos BTW.SecretsProvider → Infisical (opcional)
API docs Scalar (/scalar)
Health checks /health (DbContext check incluido)
Resiliencia HTTP Microsoft.Extensions.Http.Resilience + Polly

Levantar el entorno local

Visual Studio (recomendado)

Abre la solución y selecciona docker-compose como proyecto de inicio. VS carga automáticamente docker-compose.yml + docker-compose.override.yml, levanta PostgreSQL, Prometheus, Grafana (y opcionalmente Infisical/Loki) y hace hot-reload.

CLI

cp .env.example .env
# Editar .env con las credenciales reales

docker compose up -d

Variables de entorno (.env)

APP_DB_PASSWORD=<contraseña para PostgreSQL>

# Solo si --infisical:
INFISICAL_CLIENT_ID=<machine identity client id>
INFISICAL_CLIENT_SECRET=<machine identity client secret>
INFISICAL_PROJECT_ID=<project id>

Secretos gestionados por Infisical (/api)

Cuando --infisical, estos valores deben configurarse en Infisical bajo el path /api:

ConnectionStrings__DefaultConnection = Host=postgres-app;Database=SampleDb;Username=postgres;Password=<APP_DB_PASSWORD>
Loki__Url                            = http://loki:3100

Primera vez con Infisical self-hosted:

  1. docker compose up -d — levanta todos los servicios
  2. Abrir http://localhost:8888 → crear cuenta y proyecto
  3. Crear Machine Identity y copiar sus credenciales al .env
  4. Agregar los secretos indicados bajo el path /api

Despliegue en servidor

Usa los scripts de despliegue. Soportan Docker Compose (servidor único) y Docker Swarm (múltiples réplicas).

# Linux — test con Docker Compose
./deploy.sh test compose

# Linux — producción con Docker Swarm
./deploy.sh prod swarm

# Windows — test con Docker Compose
.\deploy.ps1 -Env test -Mode compose

# Windows — producción con Docker Swarm
.\deploy.ps1 -Env prod -Mode swarm

Los scripts copian automáticamente la configuración correcta de Prometheus (static_configs para compose, dns_sd_configs para swarm). Requieren .env.test o .env.prod (copiar desde los .example).

Patrones de arquitectura

Repository + Unit of Work

IUnitOfWork expone los repositorios tipados y centraliza SaveChangesAsync y la gestión de transacciones:

public interface IUnitOfWork : IAsyncDisposable
{
    ISampleRepository SampleRepository { get; }
    Task<int> SaveChangesAsync(CancellationToken ct = default);
    Task BeginTransactionAsync(CancellationToken ct = default);
    Task CommitTransactionAsync(CancellationToken ct = default);  // NO llama SaveChanges internamente
    Task RollbackTransactionAsync(CancellationToken ct = default);
}

Los servicios inyectan solo IUnitOfWork y acceden al repositorio a través de él. EF Core garantiza que el DbContext sea la misma instancia (Scoped).

Paginación

// Request
GET /sample?page=1&pageSize=10

// Response
{
  "success": true,
  "result": {
    "items": [...],
    "totalCount": 100,
    "page": 1,
    "pageSize": 10,
    "totalPages": 10,
    "hasPreviousPage": false,
    "hasNextPage": true
  }
}

Jerarquía de excepciones

AppException (abstract)
├── NotFoundException      → 404
├── ConflictException      → 409
└── (extensible)           → 400

Los servicios lanzan excepciones de dominio. GlobalExceptionHandler las mapea a códigos HTTP sin que la capa de aplicación conozca HTTP.

Auditoría automática

Las entidades que implementan IAuditableEntity reciben CreatedAt y UpdatedAt automáticamente vía AuditInterceptor (EF Core SaveChangesInterceptor).

ApiResponse

Los servicios retornan objetos de dominio. Los controllers los envuelven:

// Servicio
Task<SampleResponse> GetSampleByIdAsync(int id, CancellationToken ct);

// Controller
SampleResponse result = await sampleService.GetSampleByIdAsync(id, ct);
return Ok(ApiResponse<SampleResponse>.Ok(result));

Migraciones EF Core

cd Api
dotnet ef migrations add InitialCreate --project ../Infrastructure
dotnet ef database update

Endpoints disponibles

Método Ruta Descripción
GET /sample Lista paginada (?page=1&pageSize=10)
GET /sample/{id} Obtener por ID
POST /sample Crear nuevo
GET /health Health check (liveness + DB)
GET /metrics Métricas Prometheus
GET /scalar Documentación interactiva

Observabilidad

  • Prometheus: http://localhost:9090
  • Grafana: http://localhost:3000 (admin/admin)
  • Loki (si habilitado): http://localhost:3100
  • Métricas expuestas: samples_created_total, samples_get_all_duration_seconds

Licencia

MIT

  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.0 132 5/8/2026
1.1.2 503 3/28/2025