Skip to content

MBMor/MicroS_01_OnePureREST

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Inventory Service

Inventory Service is a clean ASP.NET Core REST API for product inventory management.

The goal is to build one well-structured backend service first. This is not a full microservices system yet. The service focuses on REST API design, PostgreSQL persistence, Entity Framework Core, validation, error handling, Swagger, health checks, Docker, and basic testing.


Features

  • Product CRUD
  • Soft delete for products
  • PostgreSQL persistence
  • Entity Framework Core
  • EF Core migrations
  • FluentValidation request validation
  • Problem Details error responses
  • Swagger / OpenAPI documentation
  • Health endpoint
  • Dockerfile for the API
  • Docker Compose for local development
  • Basic unit tests
  • Basic integration tests with PostgreSQL

Product model

The main domain entity is Product.

Fields:

  • Id
  • Name
  • Description
  • Sku
  • Price
  • QuantityInStock
  • IsActive
  • CreatedAt
  • UpdatedAt

Important rules:

  • Id uses Guid.
  • Price uses decimal.
  • PostgreSQL stores Price as numeric(18,2).
  • Sku is required and unique.
  • Name is required.
  • Price must be greater than or equal to 0.
  • QuantityInStock must be greater than or equal to 0.
  • Timestamps are stored in UTC.
  • Products are not physically deleted.
  • DELETE /api/products/{id} sets IsActive to false.

Architecture

The solution uses a simple Clean Architecture / layered architecture approach.

src/
+¦¦ InventoryService.Api/
+¦¦ InventoryService.Application/
+¦¦ InventoryService.Domain/
L¦¦ InventoryService.Infrastructure/

InventoryService.Api

HTTP/API layer.

Responsibilities:

  • Controllers
  • Swagger setup
  • Dependency injection composition
  • Problem Details configuration
  • Global exception handling
  • Health endpoint setup
  • API configuration

The API layer references:

  • InventoryService.Application
  • InventoryService.Infrastructure

Controllers should stay thin and should not contain database or business logic.


InventoryService.Application

Application layer.

Responsibilities:

  • DTOs
  • request models
  • response models
  • validators
  • application services
  • application interfaces
  • application-level business logic

The Application layer references:

  • InventoryService.Domain

The Application layer does not depend on Infrastructure.


InventoryService.Domain

Domain layer.

Responsibilities:

  • domain entities
  • domain constants
  • simple domain rules

The Domain layer does not depend on any other project.


InventoryService.Infrastructure

Infrastructure layer.

Responsibilities:

  • EF Core DbContext
  • PostgreSQL persistence
  • entity configuration
  • EF Core migrations
  • repository implementations
  • system clock implementation

The Infrastructure layer references:

  • InventoryService.Application
  • InventoryService.Domain

Technology stack

  • .NET 10
  • ASP.NET Core Web API
  • Controllers
  • PostgreSQL
  • Entity Framework Core
  • EF Core migrations
  • FluentValidation
  • Problem Details
  • Swagger / OpenAPI
  • Health checks
  • Docker
  • Docker Compose
  • xUnit
  • Testcontainers for integration tests

Used Technologies & Concepts

  • ASP.NET Core Web API
  • Controllers
  • REST API
  • Clean Architecture / layered architecture
  • PostgreSQL
  • Entity Framework Core
  • EF Core migrations
  • DTOs
  • FluentValidation
  • Problem Details
  • Swagger / OpenAPI
  • Health checks
  • Docker
  • Docker Compose
  • Dependency Injection
  • Unit tests
  • Integration tests

Prerequisites

Install:

  • .NET 10 SDK
  • Docker Desktop or another Docker-compatible runtime
  • EF Core CLI tool

Check .NET:

dotnet --version

Install EF Core CLI tool if needed:

dotnet tool install --global dotnet-ef

Update EF Core CLI tool if already installed:

dotnet tool update --global dotnet-ef

Local setup

Restore and build the solution:

dotnet restore
dotnet build

Start PostgreSQL only:

docker compose up -d postgres

Apply EF Core migrations:

dotnet ef database update \
  --project src/InventoryService.Infrastructure/InventoryService.Infrastructure.csproj \
  --startup-project src/InventoryService.Api/InventoryService.Api.csproj \
  --context InventoryDbContext

Run the API locally:

dotnet run --project src/InventoryService.Api/InventoryService.Api.csproj

The local API should be available at:

http://localhost:5080

Swagger should be available at:

http://localhost:5080/swagger

Health endpoint:

http://localhost:5080/health

Docker Compose setup

Run the full local stack:

docker compose up --build

This starts:

  • PostgreSQL
  • Inventory Service API

The API should be available at:

http://localhost:8080

Swagger:

http://localhost:8080/swagger

Health endpoint:

http://localhost:8080/health

Stop containers:

docker compose down

Stop containers and remove the PostgreSQL volume:

docker compose down -v

Use -v only when you intentionally want to delete local database data.


PostgreSQL setup

PostgreSQL runs through Docker Compose.

Default local database values:

Database: inventorydb
Username: inventory_user
Password: inventory_password
Port: 5432

The API uses different host values depending on where it runs:

Local dotnet run:
Host=localhost

Docker Compose:
Host=postgres

Inside Docker Compose, the API connects to PostgreSQL by service name:

postgres

EF Core migrations

Migrations are stored in the Infrastructure project:

src/InventoryService.Infrastructure/Persistence/Migrations

Create a new migration:

dotnet ef migrations add MigrationName \
  --project src/InventoryService.Infrastructure/InventoryService.Infrastructure.csproj \
  --startup-project src/InventoryService.Api/InventoryService.Api.csproj \
  --context InventoryDbContext \
  --output-dir Persistence/Migrations

Apply migrations:

dotnet ef database update \
  --project src/InventoryService.Infrastructure/InventoryService.Infrastructure.csproj \
  --startup-project src/InventoryService.Api/InventoryService.Api.csproj \
  --context InventoryDbContext

Migrations are not automatically applied on application startup in this project.

That is intentional for the first version. It keeps database changes explicit and easier to understand.


Configuration

Main configuration files:

src/InventoryService.Api/appsettings.json
src/InventoryService.Api/appsettings.Development.json
docker-compose.yml

Local development connection string:

ConnectionStrings:InventoryDatabase

Docker Compose overrides it with:

ConnectionStrings__InventoryDatabase

API endpoints

Products

Method Endpoint Description
GET /api/products List products
GET /api/products/{id} Get product by ID
POST /api/products Create product
PUT /api/products/{id} Update product
DELETE /api/products/{id} Soft delete product

Health

Method Endpoint Description
GET /health API and PostgreSQL health check

Product listing

GET /api/products supports:

  • pagination
  • filtering
  • sorting

Query parameters:

Parameter Description
page Page number
pageSize Number of items per page
name Filter by product name
sku Filter by SKU
isActive Filter by active/inactive status
sortBy Sort field
sortDirection Sort direction

Supported sortBy values:

Name
Price
CreatedAt
QuantityInStock

Supported sortDirection values:

Asc
Desc

Example:

curl "http://localhost:8080/api/products?page=1&pageSize=10&sortBy=CreatedAt&sortDirection=Desc"

Example requests

Create product

curl -X POST http://localhost:8080/api/products \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mechanical Keyboard",
    "description": "Compact keyboard with hot-swappable switches",
    "sku": "KEYBOARD-001",
    "price": 129.99,
    "quantityInStock": 25
  }'

Get product by ID

curl http://localhost:8080/api/products/YOUR_PRODUCT_ID

List products

curl "http://localhost:8080/api/products?page=1&pageSize=10"

Update product

curl -X PUT http://localhost:8080/api/products/YOUR_PRODUCT_ID \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mechanical Keyboard Pro",
    "description": "Updated keyboard",
    "price": 149.99,
    "quantityInStock": 20,
    "isActive": true
  }'

Soft delete product

curl -X DELETE http://localhost:8080/api/products/YOUR_PRODUCT_ID

This does not remove the database row.

It sets:

IsActive = false

Validation

Request validation is implemented with FluentValidation.

Create product validation:

  • Name is required.
  • Name has a maximum length.
  • Sku is required.
  • Sku has a maximum length.
  • Price must be greater than or equal to 0.
  • QuantityInStock must be greater than or equal to 0.

Update product validation:

  • Name is required.
  • Name has a maximum length.
  • Price must be greater than or equal to 0.
  • QuantityInStock must be greater than or equal to 0.

SKU is not updateable in the initial version.


Error handling

The API uses global exception handling and Problem Details responses.

Handled cases:

Case Status code
Validation error 400 Bad Request
Product not found 404 Not Found
Duplicate SKU 409 Conflict
Unexpected server error 500 Internal Server Error

Validation errors return ValidationProblemDetails.

Other errors return ProblemDetails.

Unexpected errors do not expose internal exception details.


Health checks

The API exposes:

GET /health

The health endpoint checks:

  • API process
  • PostgreSQL connection through EF Core

Example:

curl http://localhost:8080/health

Expected status:

Healthy

Testing

The solution contains:

tests/
+¦¦ InventoryService.Tests.Unit/
L¦¦ InventoryService.Tests.Integration/

Run all tests:

dotnet test

Run unit tests only:

dotnet test tests/InventoryService.Tests.Unit/InventoryService.Tests.Unit.csproj

Run integration tests only:

dotnet test tests/InventoryService.Tests.Integration/InventoryService.Tests.Integration.csproj

Integration tests use a temporary PostgreSQL container.

Docker must be running before integration tests are executed.


Design decisions

Controllers instead of Minimal APIs

This project intentionally uses ASP.NET Core Controllers to practice classic REST API structure.

Simple layered architecture

The project uses a simple Clean Architecture style:

API � Application � Domain
API � Infrastructure � Application � Domain

This keeps responsibilities clear without adding unnecessary complexity.

No generic repository pattern

The project uses product-specific abstractions instead of a generic repository.

This keeps the code explicit and easier to understand.

No automatic migrations on startup

Migrations are applied manually.

This keeps database changes visible and avoids surprising startup behavior.

Soft delete instead of physical delete

Products are deactivated with IsActive = false.

This preserves product history and avoids accidental data loss.

No mapping framework

Mappings are explicit.

This keeps the project simple and avoids adding extra abstraction too early.


Trade-offs

This project intentionally avoids advanced microservice patterns.

Not included:

  • authentication
  • authorization
  • message brokers
  • distributed tracing
  • service discovery
  • API gateway
  • Redis
  • OpenSearch
  • Kubernetes
  • cloud services
  • CQRS
  • MediatR
  • event sourcing
  • background jobs

These are useful topics, but they are intentionally left for future projects.

The goal here is to build one clean REST service first.


Future improvements

Possible next improvements:

  • authentication and authorization
  • API versioning
  • separate readiness and liveness health endpoints
  • structured request logging improvements
  • CI pipeline
  • test coverage report
  • more integration tests
  • pagination metadata headers
  • product activation endpoint
  • optimistic concurrency
  • production deployment configuration
  • container health check for the API
  • OpenTelemetry tracing

Useful commands

Build:

dotnet build

Run API locally:

dotnet run --project src/InventoryService.Api/InventoryService.Api.csproj

Run Docker Compose:

docker compose up --build

Apply migrations:

dotnet ef database update \
  --project src/InventoryService.Infrastructure/InventoryService.Infrastructure.csproj \
  --startup-project src/InventoryService.Api/InventoryService.Api.csproj \
  --context InventoryDbContext

Run tests:

dotnet test

Project status

This project currently represents a single clean REST service.

It is intentionally not a full microservices system yet.

The service can later become one part of a larger microservices portfolio.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors