# API .NET + Next.js - Especificação

## 1. Visão Geral

Arquitetura full-stack moderna com API .NET e frontend Next.js, seguindo boas práticas de separação de responsabilidades.

### 1.1 Objetivos

- Backend robusto com .NET Minimal APIs
- Frontend moderno com Next.js App Router
- Autenticação unificada via Clerk
- Type-safety end-to-end
- Deploy independente de cada camada

### 1.2 Use Cases

- Dashboards administrativos
- Aplicações CRUD complexas
- Sistemas que precisam de SEO (Next.js SSR)
- Aplicações com múltiplos frontends (web, mobile)

## 2. Requisitos Funcionais

### 2.1 Backend (.NET API)

| ID | Requisito |
|----|-----------|
| API-001 | Minimal APIs com OpenAPI/Swagger |
| API-002 | Autenticação JWT via Clerk |
| API-003 | Validação com FluentValidation |
| API-004 | Paginação, filtros e ordenação |
| API-005 | Rate limiting por usuário |
| API-006 | Health checks (liveness + readiness) |
| API-007 | Logging estruturado |
| API-008 | CORS configurável |

### 2.2 Frontend (Next.js)

| ID | Requisito |
|----|-----------|
| WEB-001 | App Router com layouts aninhados |
| WEB-002 | Server Components para dados estáticos |
| WEB-003 | Client Components para interatividade |
| WEB-004 | React Query para cache e mutations |
| WEB-005 | Forms com React Hook Form + Zod |
| WEB-006 | UI com Tailwind + shadcn/ui |
| WEB-007 | Dark mode support |
| WEB-008 | Loading states e error boundaries |

### 2.3 Autenticação

| ID | Requisito |
|----|-----------|
| AUTH-001 | SSO via Clerk |
| AUTH-002 | Proteção de rotas (middleware) |
| AUTH-003 | Token refresh automático |
| AUTH-004 | Role-based access control |

## 3. Requisitos Não-Funcionais

### 3.1 Performance

| ID | Requisito |
|----|-----------|
| PERF-001 | API response < 100ms (P95) |
| PERF-002 | LCP < 2.5s (Core Web Vitals) |
| PERF-003 | FID < 100ms |
| PERF-004 | CLS < 0.1 |

### 3.2 Segurança

| ID | Requisito |
|----|-----------|
| SEC-001 | HTTPS obrigatório |
| SEC-002 | CORS restrito a domínios conhecidos |
| SEC-003 | Input validation em todos endpoints |
| SEC-004 | SQL injection prevention (EF Core) |
| SEC-005 | XSS prevention (React default) |

## 4. Arquitetura da API

### 4.1 Estrutura de Camadas

```
Api/
├── Endpoints/          # Minimal API endpoints
├── Middleware/         # Custom middlewares
├── Filters/            # Exception filters
└── Program.cs          # Entry point

Application/
├── Services/           # Business logic
├── Validators/         # FluentValidation
└── DTOs/               # Data transfer objects

Domain/
├── Entities/           # Domain models
├── Interfaces/         # Repository contracts
└── Enums/              # Domain enums

Infrastructure/
├── Data/               # DbContext
├── Repositories/       # EF Core implementations
└── Services/           # External service clients
```

### 4.2 Endpoint Pattern

```csharp
// Endpoints/ProductEndpoints.cs
public static class ProductEndpoints
{
    public static void MapProductEndpoints(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/api/products")
            .WithTags("Products")
            .RequireAuthorization();

        group.MapGet("/", GetAll);
        group.MapGet("/{id:int}", GetById);
        group.MapPost("/", Create);
        group.MapPut("/{id:int}", Update);
        group.MapDelete("/{id:int}", Delete);
    }

    private static async Task<IResult> GetAll(
        [AsParameters] PaginationQuery query,
        IProductService service,
        CancellationToken ct)
    {
        var result = await service.GetAllAsync(query, ct);
        return Results.Ok(result);
    }
}
```

### 4.3 Pagination

```csharp
public record PaginationQuery(
    int Page = 1,
    int PageSize = 20,
    string? SortBy = null,
    bool SortDesc = false,
    string? Search = null
);

public record PagedResult<T>(
    IReadOnlyList<T> Items,
    int TotalItems,
    int Page,
    int PageSize,
    int TotalPages
);
```

## 5. Arquitetura do Frontend

### 5.1 Estrutura de Pastas

```
app/
├── (auth)/
│   ├── sign-in/[[...sign-in]]/page.tsx
│   └── sign-up/[[...sign-up]]/page.tsx
├── (dashboard)/
│   ├── layout.tsx              # Dashboard layout
│   ├── dashboard/page.tsx
│   ├── products/
│   │   ├── page.tsx            # List
│   │   ├── [id]/page.tsx       # Detail
│   │   └── new/page.tsx        # Create
│   └── orders/
├── api/                        # API routes (BFF)
├── layout.tsx                  # Root layout
└── page.tsx                    # Landing

components/
├── ui/                         # shadcn components
├── forms/                      # Form components
├── tables/                     # Table components
└── shared/                     # Shared components

hooks/
├── use-products.ts
├── use-orders.ts
└── use-pagination.ts

lib/
├── api-client.ts               # Fetch wrapper
├── query-client.ts             # React Query config
└── utils.ts                    # Utility functions

types/
├── api.ts                      # API response types
└── entities.ts                 # Domain types
```

### 5.2 Data Fetching Pattern

```typescript
// hooks/use-products.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/lib/api-client";
import type { Product, CreateProductRequest } from "@/types/entities";

export function useProducts(params?: PaginationParams) {
  return useQuery({
    queryKey: ["products", params],
    queryFn: () => apiClient<PagedResult<Product>>("/api/products", { params }),
  });
}

export function useProduct(id: number) {
  return useQuery({
    queryKey: ["products", id],
    queryFn: () => apiClient<Product>(`/api/products/${id}`),
    enabled: !!id,
  });
}

export function useCreateProduct() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: CreateProductRequest) =>
      apiClient<Product>("/api/products", {
        method: "POST",
        body: JSON.stringify(data),
      }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });
    },
  });
}
```

### 5.3 Form Pattern

```typescript
// components/forms/product-form.tsx
"use client";

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useCreateProduct } from "@/hooks/use-products";

const schema = z.object({
  name: z.string().min(1, "Name is required"),
  price: z.number().positive("Price must be positive"),
  description: z.string().optional(),
});

type FormData = z.infer<typeof schema>;

export function ProductForm() {
  const { mutate, isPending } = useCreateProduct();
  const form = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data: FormData) => {
    mutate(data);
  };

  return (
    <form onSubmit={form.handleSubmit(onSubmit)}>
      {/* Form fields */}
    </form>
  );
}
```

## 6. Comunicação API

### 6.1 API Client

```typescript
// lib/api-client.ts
import { auth } from "@clerk/nextjs/server";

const API_URL = process.env.NEXT_PUBLIC_API_URL;

interface FetchOptions extends RequestInit {
  params?: Record<string, string | number | undefined>;
}

export async function apiClient<T>(
  endpoint: string,
  options: FetchOptions = {}
): Promise<T> {
  const { params, ...init } = options;

  // Build URL with query params
  const url = new URL(`${API_URL}${endpoint}`);
  if (params) {
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined) {
        url.searchParams.append(key, String(value));
      }
    });
  }

  // Get auth token
  const { getToken } = auth();
  const token = await getToken();

  const response = await fetch(url.toString(), {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(token && { Authorization: `Bearer ${token}` }),
      ...init.headers,
    },
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new ApiError(response.status, error.title || "Request failed", error);
  }

  return response.json();
}

class ApiError extends Error {
  constructor(
    public status: number,
    message: string,
    public details?: unknown
  ) {
    super(message);
    this.name = "ApiError";
  }
}
```

## 7. Configurações

### 7.1 API (appsettings.json)

```json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=...;Database=...;"
  },
  "Clerk": {
    "Authority": "https://your-clerk-domain.clerk.accounts.dev",
    "SecretKey": "sk_..."
  },
  "Cors": {
    "AllowedOrigins": ["http://localhost:3000", "https://your-domain.com"]
  }
}
```

### 7.2 Frontend (.env.local)

```env
NEXT_PUBLIC_API_URL=http://localhost:5000
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
```

## 8. Deploy

### 8.1 Docker

```dockerfile
# API Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["src/api/Api/Api.csproj", "Api/"]
RUN dotnet restore "Api/Api.csproj"
COPY src/api/ .
RUN dotnet build "Api/Api.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Api/Api.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Api.dll"]
```

### 8.2 Container Apps

Dois Container Apps separados:
- `app-api`: .NET API
- `app-web`: Next.js (com output: standalone)

---

*MORPH-SPEC by Polymorphism Tech*
