Sistema completo de gerenciamento de produtos e movimentações de estoque desenvolvido com Clean Architecture, princípios SOLID e padrões de projeto.
- Sobre o Projeto
- Funcionalidades
- Arquitetura
- Tecnologias
- Padrões de Projeto
- Princípios SOLID
- Pré-requisitos
- Instalação e Execução
- Uso da API
- Estrutura do Projeto
- Testes
- Demonstração
Este projeto foi desenvolvido como trabalho acadêmico para demonstrar a aplicação prática de Engenharia de Software moderna, incluindo:
✅ Arquitetura em Camadas (Clean Architecture)
✅ Princípios SOLID (DIP, SRP, OCP)
✅ Padrões de Projeto (Factory Method, Strategy, Repository)
✅ Testes Automatizados (xUnit + Moq)
✅ API RESTful com documentação Swagger
✅ Inversão de Dependência completa entre camadas
O sistema permite gerenciar produtos e suas movimentações de estoque (entradas e saídas), com validação de regras de negócio e rastreabilidade completa.
- ✅ Cadastro de produtos com SKU único
- ✅ Consulta de produtos individuais ou listagem completa
- ✅ Controle automático de quantidade em estoque
- ✅ Validação de SKU duplicado
- ✅ ENTRADA: Adiciona quantidade ao estoque
- ✅ SAÍDA: Remove quantidade com validação de disponibilidade
- ✅ Histórico completo de movimentações
- ✅ Rastreabilidade (data, quantidade, tipo)
- ✅ Relacionamento com produto
- ✅ Não permite saída maior que estoque disponível
- ✅ SKU único por produto
- ✅ Validação de tipos de movimentação
- ✅ Tratamento de erros com mensagens descritivas
O projeto segue Clean Architecture com separação clara de responsabilidades:
┌─────────────────────────────────────────────────┐
│ API Layer (Apresentação) │
│ • Endpoints REST │
│ • Swagger/OpenAPI │
│ • Dependency Injection │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Application Layer (Lógica de Negócio) │
│ • Services │
│ • Factory Method Pattern │
│ • Strategy Pattern │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Domain Layer (Núcleo do Sistema) │
│ • Entidades (Product, Movement) │
│ • Interfaces (Contratos) │
│ • Regras de Domínio │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Infrastructure Layer (Persistência) │
│ • Entity Framework Core │
│ • SQL Server │
│ • Repository Pattern │
└─────────────────────────────────────────────────┘
- 🎯 Separação de Responsabilidades: Cada camada tem um propósito único
- 🔄 Inversão de Dependência: Camadas dependem de abstrações, não implementações
- 🧪 Testabilidade: Fácil criar testes unitários com mocks
- 🔌 Extensibilidade: Adicionar novos tipos de movimentação sem alterar código existente
- 🛡️ Manutenibilidade: Mudanças isoladas em cada camada
- .NET 8.0 - Framework principal
- C# 12 - Linguagem de programação
- ASP.NET Core Minimal APIs - Framework web
- Entity Framework Core 8.0 - ORM
- SQL Server - Banco de dados relacional
- LocalDB - Desenvolvimento local
- Swagger/OpenAPI - Documentação interativa da API
- Swashbuckle - Geração automática de documentação
Classe: MovementFactory
Localização: TaskManagement.Application/Factories/
public class MovementFactory : IMovementFactory
{
public Movement CreateMovement(int productId, int quantity, string type)
{
return new Movement
{
ProductId = productId,
Quantity = quantity,
Type = type,
Date = DateTime.UtcNow // ← Regra centralizada
};
}
}Benefícios:
- ✅ Centraliza a criação de objetos
- ✅ Garante que toda movimentação tenha data UTC
- ✅ Facilita mudanças nas regras de criação
- ✅ Princípio SOLID: Single Responsibility Principle (SRP)
Classes: EntryStrategy, ExitStrategy
Localização: TaskManagement.Application/Strategies/
public class EntryStrategy : IMovementStrategy
{
public string TypeHandled => "ENTRADA";
public void Process(Product product, Movement movement)
{
product.StockQuantity += movement.Quantity;
}
}
public class ExitStrategy : IMovementStrategy
{
public string TypeHandled => "SAÍDA";
public void Process(Product product, Movement movement)
{
if (product.StockQuantity < movement.Quantity)
throw new InvalidOperationException("Estoque insuficiente");
product.StockQuantity -= movement.Quantity;
}
}Benefícios:
- ✅ Encapsula lógica específica de cada tipo de movimentação
- ✅ Permite adicionar novos tipos sem modificar código existente
- ✅ Princípio SOLID: Open/Closed Principle (OCP)
- ✅ Facilita testes unitários de cada estratégia isoladamente
Classes: ProductRepository, MovementRepository
Localização: TaskManagement.Infrastructure/Repositories/
public class ProductRepository : IProductRepository
{
private readonly TaskManagementDbContext _context;
public Product? GetById(int id)
{
return _context.Products.AsNoTracking()
.FirstOrDefault(p => p.Id == id);
}
// ... outros métodos CRUD
}Benefícios:
- ✅ Abstrai o acesso a dados
- ✅ Facilita troca de tecnologia de persistência
- ✅ Princípio SOLID: Dependency Inversion Principle (DIP)
- ✅ Permite mockar repositórios em testes
Cada classe tem uma única responsabilidade:
MovementFactory: apenas criar movimentaçõesEntryStrategy: apenas processar entradasProductRepository: apenas persistir produtos
Sistema aberto para extensão, fechado para modificação:
- Adicionar novo tipo de movimentação? Crie uma nova
Strategy - Não precisa alterar
MovementService
Implementações podem substituir interfaces sem quebrar o sistema:
- Qualquer
IMovementStrategypode ser usada peloMovementService
Interfaces específicas e coesas:
IProductRepositorycom apenas operações de produtoIMovementFactorycom apenas criação de movimentações
Camadas dependem de abstrações, não implementações concretas:
// ✅ Correto: Depende da interface
public MovementService(IMovementRepository repository) { }
// ❌ Errado: Depender da implementação concreta
public MovementService(MovementRepository repository) { }Antes de começar, certifique-se de ter instalado:
- ✅ .NET 8.0 SDK
- ✅ Visual Studio 2022 ou VS Code
- ✅ SQL Server LocalDB (incluído no Visual Studio)
- ✅ Git (para clonar o repositório)
git clone https://github.com/vitorpaiv4/TaskManagement.git
cd TaskManagementdotnet restoredotnet ef migrations add InitialStockSystem --project TaskManagement.Infrastructure --startup-project TaskManagement.APIdotnet ef database update --project TaskManagement.APIdotnet run --project TaskManagement.APIOu pressione F5 no Visual Studio.
Abra o navegador em:
http://localhost:5122/swagger
GET /healthResposta:
{
"status": "OK",
"message": "Sistema de Controle de Estoque - API Running"
}GET /api/productsResposta:
[
{
"id": 1,
"sku": "MOUSE-001",
"name": "Mouse Gamer RGB",
"description": "Mouse óptico 16000 DPI",
"price": 149.90,
"stockQuantity": 45,
"createdAt": "2025-11-19T12:00:00Z"
}
]GET /api/products/{id}Parâmetros:
id(path): ID do produto
Resposta: Mesma estrutura do produto individual
POST /api/products
Content-Type: application/jsonBody:
{
"sku": "TECLADO-001",
"name": "Teclado Mecânico",
"description": "Switch Blue, RGB",
"price": 299.90
}Resposta (201 Created):
{
"id": 2,
"sku": "TECLADO-001",
"name": "Teclado Mecânico",
"description": "Switch Blue, RGB",
"price": 299.90,
"stockQuantity": 0,
"createdAt": "2025-11-19T12:30:00Z"
}GET /api/movementsResposta:
[
{
"id": 1,
"productId": 1,
"quantity": 50,
"type": "ENTRADA",
"date": "2025-11-19T12:15:00Z",
"product": {
"id": 1,
"name": "Mouse Gamer RGB",
"sku": "MOUSE-001"
}
}
]POST /api/movements
Content-Type: application/jsonBody:
{
"productId": 1,
"quantity": 100,
"type": "ENTRADA"
}Resposta (201 Created):
{
"id": 2,
"productId": 1,
"quantity": 100,
"type": "ENTRADA",
"date": "2025-11-19T12:45:00Z"
}POST /api/movements
Content-Type: application/jsonBody:
{
"productId": 1,
"quantity": 10,
"type": "SAÍDA"
}Resposta (201 Created): Mesmo formato da entrada
Erro (400 Bad Request - Estoque Insuficiente):
{
"error": "Estoque insuficiente. Disponível: 5, Solicitado: 10"
}TaskManagement/
│
├── TaskManagement.Domain/ # Camada de Domínio
│ ├── Entities/
│ │ ├── Product.cs # Entidade Produto
│ │ └── Movement.cs # Entidade Movimentação
│ └── Interfaces/
│ ├── IProductRepository.cs # Contrato do repositório
│ ├── IMovementRepository.cs
│ ├── IMovementService.cs # Contrato do serviço
│ ├── IMovementFactory.cs # Contrato da fábrica
│ └── IMovementStrategy.cs # Contrato da estratégia
│
├── TaskManagement.Application/ # Camada de Aplicação
│ ├── Services/
│ │ └── MovementService.cs # Lógica de negócio
│ ├── Factories/
│ │ └── MovementFactory.cs # Factory Method
│ └── Strategies/
│ ├── EntryStrategy.cs # Estratégia ENTRADA
│ └── ExitStrategy.cs # Estratégia SAÍDA
│
├── TaskManagement.Infrastructure/ # Camada de Infraestrutura
│ ├── Data/
│ │ └── TaskManagementDbContext.cs # Contexto EF Core
│ ├── Repositories/
│ │ ├── ProductRepository.cs # Implementação do repositório
│ │ └── MovementRepository.cs
│ └── Migrations/ # Migrações do banco
│
├── TaskManagement.API/ # Camada de API
│ ├── Program.cs # Configuração e endpoints
│ └── appsettings.json # Configurações
│
└── TaskManagement.Tests.Unit/ # Testes Unitários
└── MovementServiceTests.cs # Testes do serviço
O projeto inclui testes automatizados usando xUnit e Moq para garantir qualidade e funcionamento correto das regras de negócio.
dotnet test[Fact]
public void ProcessNewMovement_ShouldUseFactoryAndStrategy()Valida:
- Factory é chamada corretamente
- Strategy é selecionada e executada
- Repositório persiste a movimentação
[Fact]
public void ExitStrategy_ShouldFailWhenStockIsZero()Valida:
ExitStrategylança exceção quando estoque é insuficiente- Mensagem de erro é descritiva
[Fact]
public void MovementFactory_ShouldSetTypeAndDate()Valida:
- Factory configura tipo corretamente
- Data é definida como UTC
- Quantidade é preservada
- ✅ Serviços (MovementService)
- ✅ Strategies (EntryStrategy, ExitStrategy)
- ✅ Factories (MovementFactory)
⚠️ Repositórios (testes de integração não incluídos)
POST /api/products
{
"sku": "HEADSET-001",
"name": "Headset Gamer 7.1",
"description": "Som surround, RGB",
"price": 349.90
}POST /api/movements
{
"productId": 1,
"quantity": 50,
"type": "ENTRADA"
}Estoque atual: 50 unidades
POST /api/movements
{
"productId": 1,
"quantity": 15,
"type": "SAÍDA"
}Estoque atual: 35 unidades (50 - 15)
POST /api/movements
{
"productId": 1,
"quantity": 100,
"type": "SAÍDA"
}Resultado: ❌ Erro 400 - "Estoque insuficiente. Disponível: 35, Solicitado: 100"
GET /api/movementsRetorna todas as movimentações com detalhes do produto.
- ✅ Clean Architecture
- ✅ Separation of Concerns
- ✅ Dependency Injection
- ✅ Inversion of Control (IoC)
- ✅ Single Responsibility Principle
- ✅ Open/Closed Principle
- ✅ Liskov Substitution Principle
- ✅ Interface Segregation Principle
- ✅ Dependency Inversion Principle
- ✅ Factory Method
- ✅ Strategy
- ✅ Repository
- ✅ Testes Automatizados
- ✅ Código Limpo (Clean Code)
- ✅ Documentação de API (Swagger)
- ✅ Versionamento (Git)
- ✅ Tratamento de Erros
- ✅ Validação de Regras de Negócio
Contribuições são bem-vindas! Para contribuir:
- Faça um fork do projeto
- Crie uma branch para sua feature (
git checkout -b feature/NovaFuncionalidade) - Commit suas mudanças (
git commit -m 'Adiciona nova funcionalidade') - Push para a branch (
git push origin feature/NovaFuncionalidade) - Abra um Pull Request
Este projeto está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.
Vitor Paiva
- GitHub: @vitorpaiv4
- LinkedIn: Vitor Paiva
- Professores e colegas da faculdade
- Documentação oficial da Microsoft
- Criadores dos frameworks e bibliotecas utilizados
Desenvolvido com ❤️ e ☕ por Vitor Paiva