Skip to content

Commit 0a82fff

Browse files
committed
feat: init
0 parents  commit 0a82fff

27 files changed

+934
-0
lines changed

.github/dependabot.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "nuget"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
open-pull-requests-limit: 10
8+
labels:
9+
- "dependencies"
10+
commit-message:
11+
prefix: "deps: "
12+
13+
- package-ecosystem: "github-actions"
14+
directory: "/"
15+
schedule:
16+
interval: "weekly"
17+
open-pull-requests-limit: 5
18+
labels:
19+
- "dependencies"
20+
commit-message:
21+
prefix: "ci: "

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Dotnet CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
18+
- name: Setup .NET 10
19+
uses: actions/setup-dotnet@v4
20+
with:
21+
dotnet-version: '10.0.x'
22+
23+
- name: Restore
24+
working-directory: dotnet
25+
run: dotnet restore
26+
27+
- name: Build
28+
working-directory: dotnet
29+
run: dotnet build --configuration Release --no-restore
30+
31+
- name: Test
32+
working-directory: dotnet
33+
run: dotnet test --configuration Release --no-build --logger "trx;LogFileName=test-results.trx"

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Build output
2+
bin/
3+
obj/
4+
5+
# Test and coverage output
6+
TestResults/
7+
coverage/
8+
9+
# User and IDE-specific files
10+
.vs/
11+
*.rsuser
12+
*.suo
13+
*.user
14+
*.userosscache
15+
*.sln.docstates
16+
17+
# Diagnostic files
18+
*.log

README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Usuarios API .NET
2+
3+
Exemplo do mesmo CRUD de usuarios da pasta `python`, agora implementado em .NET 10 com ASP.NET Core Minimal API, Swagger, persistencia em memoria, testes automatizados e arquitetura Ports and Adapters.
4+
5+
## Tecnologias
6+
7+
- .NET 10
8+
- ASP.NET Core Minimal API
9+
- Swagger UI
10+
- xUnit
11+
12+
## Pre-requisitos
13+
14+
- Windows com PowerShell
15+
- .NET SDK 10 instalado
16+
17+
Para validar a instalacao:
18+
19+
```powershell
20+
dotnet --list-sdks
21+
```
22+
23+
## Arquitetura
24+
25+
Estrutura da solucao:
26+
27+
```text
28+
src/
29+
|-- Usuarios.Api
30+
|-- Usuarios.Application
31+
|-- Usuarios.Domain
32+
`-- Usuarios.Adapters
33+
34+
tests/
35+
`-- Usuarios.Tests
36+
```
37+
38+
Responsabilidades:
39+
40+
- `Usuarios.Domain`: entidade, excecoes e porta de repositorio
41+
- `Usuarios.Application`: servico de aplicacao e comando de entrada
42+
- `Usuarios.Adapters`: implementacao em memoria do repositorio
43+
- `Usuarios.Api`: endpoints Minimal API, Swagger e composicao da aplicacao
44+
- `Usuarios.Tests`: testes de servico e de API
45+
46+
## Estilo da API
47+
48+
A camada HTTP da versao .NET agora usa Minimal API em vez de controllers MVC.
49+
50+
Isso significa que:
51+
52+
- os endpoints sao mapeados diretamente no startup da aplicacao
53+
- o contrato HTTP continua o mesmo da versao anterior
54+
- a estrutura continua separada por camadas, sem misturar regra de negocio com a borda HTTP
55+
56+
Arquivos principais da borda HTTP:
57+
58+
- `src/Usuarios.Api/Program.cs`
59+
- `src/Usuarios.Api/Endpoints/HealthEndpoints.cs`
60+
- `src/Usuarios.Api/Endpoints/UserEndpoints.cs`
61+
62+
## Modelo de dominio
63+
64+
- `id: int`
65+
- `nome: string`
66+
- `dtNascimento: date`
67+
- `status: bool`
68+
- `telefones: string[]`
69+
70+
## Como rodar
71+
72+
Na pasta `dotnet`:
73+
74+
### 1. Restaurar dependencias
75+
76+
```powershell
77+
dotnet restore
78+
```
79+
80+
### 2. Rodar a API
81+
82+
```powershell
83+
dotnet run --project .\src\Usuarios.Api\Usuarios.Api.csproj
84+
```
85+
86+
Por padrao, usando o `launchSettings.json` atual, a API sobe em:
87+
88+
- API: `http://localhost:5266`
89+
- Swagger UI: `http://localhost:5266/swagger`
90+
91+
Se a porta mudar, o `dotnet run` vai mostrar a URL correta no terminal.
92+
93+
### 3. Build de validacao
94+
95+
```powershell
96+
dotnet build .\UsuariosApi.slnx
97+
```
98+
99+
## Como testar
100+
101+
```powershell
102+
dotnet test .\UsuariosApi.slnx --collect:"XPlat Code Coverage"
103+
```
104+
105+
Resultado esperado no estado atual do projeto:
106+
107+
- testes de servico
108+
- testes de API
109+
- 9 testes passando
110+
111+
## Endpoints
112+
113+
- `GET /health/live`
114+
- `GET /health/ready`
115+
- `POST /usuarios`
116+
- `GET /usuarios`
117+
- `GET /usuarios/{usuarioId}`
118+
- `PUT /usuarios/{usuarioId}`
119+
- `DELETE /usuarios/{usuarioId}`
120+
121+
## Exemplo de payload
122+
123+
```json
124+
{
125+
"id": 1,
126+
"nome": "Carlos",
127+
"dtNascimento": "1992-03-14",
128+
"status": true,
129+
"telefones": [
130+
"11911112222",
131+
"1122223333"
132+
]
133+
}
134+
```
135+
136+
## CI
137+
138+
O workflow fica em `.github/workflows/ci.yml` e executa:
139+
140+
- restore
141+
- build
142+
- testes
143+
144+
## Observacoes
145+
146+
- A persistencia e totalmente em memoria.
147+
- Ao reiniciar a aplicacao, os dados sao perdidos.
148+
- O endpoint `/health/ready` retorna `503` se o servico principal nao estiver registrado.
149+
150+
## Comandos uteis
151+
152+
```powershell
153+
dotnet restore
154+
dotnet build .\UsuariosApi.slnx
155+
dotnet test .\UsuariosApi.slnx
156+
dotnet run --project .\src\Usuarios.Api\Usuarios.Api.csproj
157+
```

UsuariosApi.slnx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Solution>
2+
<Folder Name="/src/">
3+
<Project Path="src/Usuarios.Adapters/Usuarios.Adapters.csproj" />
4+
<Project Path="src/Usuarios.Api/Usuarios.Api.csproj" />
5+
<Project Path="src/Usuarios.Application/Usuarios.Application.csproj" />
6+
<Project Path="src/Usuarios.Domain/Usuarios.Domain.csproj" />
7+
</Folder>
8+
<Folder Name="/tests/">
9+
<Project Path="tests/Usuarios.Tests/Usuarios.Tests.csproj" />
10+
</Folder>
11+
</Solution>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Usuarios.Domain.Entities;
2+
using Usuarios.Domain.Ports;
3+
4+
namespace Usuarios.Adapters.Repositories;
5+
6+
public sealed class InMemoryUserRepository : IUserRepository
7+
{
8+
private readonly Dictionary<int, User> _storage = [];
9+
private readonly Lock _lock = new();
10+
11+
public User Save(User user)
12+
{
13+
var storedUser = Clone(user);
14+
15+
lock (_lock)
16+
{
17+
_storage[user.Id] = storedUser;
18+
}
19+
20+
return Clone(storedUser);
21+
}
22+
23+
public IReadOnlyList<User> ListAll()
24+
{
25+
lock (_lock)
26+
{
27+
return _storage.Values.Select(Clone).ToArray();
28+
}
29+
}
30+
31+
public User? GetById(int userId)
32+
{
33+
lock (_lock)
34+
{
35+
return _storage.TryGetValue(userId, out var user) ? Clone(user) : null;
36+
}
37+
}
38+
39+
public void Delete(int userId)
40+
{
41+
lock (_lock)
42+
{
43+
_storage.Remove(userId);
44+
}
45+
}
46+
47+
private static User Clone(User user) =>
48+
user with
49+
{
50+
Telefones = user.Telefones.ToArray()
51+
};
52+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\Usuarios.Application\Usuarios.Application.csproj" />
11+
<ProjectReference Include="..\Usuarios.Domain\Usuarios.Domain.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using System.Text.Json.Serialization;
3+
using Usuarios.Application.Commands;
4+
5+
namespace Usuarios.Api.Contracts;
6+
7+
public sealed class UserRequest
8+
{
9+
[Range(1, int.MaxValue)]
10+
public int Id { get; init; }
11+
12+
[Required]
13+
[StringLength(120, MinimumLength = 1)]
14+
public string Nome { get; init; } = string.Empty;
15+
16+
[JsonPropertyName("dtNascimento")]
17+
public DateOnly DtNascimento { get; init; }
18+
19+
public bool Status { get; init; }
20+
21+
public IReadOnlyList<string> Telefones { get; init; } = [];
22+
23+
public SaveUserCommand ToCommand() =>
24+
new(Id, Nome, DtNascimento, Status, Telefones.ToArray());
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Text.Json.Serialization;
2+
using Usuarios.Domain.Entities;
3+
4+
namespace Usuarios.Api.Contracts;
5+
6+
public sealed class UserResponse
7+
{
8+
public int Id { get; init; }
9+
10+
public string Nome { get; init; } = string.Empty;
11+
12+
[JsonPropertyName("dtNascimento")]
13+
public DateOnly DtNascimento { get; init; }
14+
15+
public bool Status { get; init; }
16+
17+
public IReadOnlyList<string> Telefones { get; init; } = [];
18+
19+
public static UserResponse FromDomain(User user) =>
20+
new()
21+
{
22+
Id = user.Id,
23+
Nome = user.Nome,
24+
DtNascimento = user.DtNascimento,
25+
Status = user.Status,
26+
Telefones = user.Telefones.ToArray()
27+
};
28+
}

0 commit comments

Comments
 (0)