LogisticsFlow is a backend application for managing logistics orders and their lifecycle. The solution is built with .NET and follows a layered architecture designed to keep business rules, application logic, infrastructure concerns, and HTTP endpoints clearly separated.
The project currently supports order creation and retrieval, including the items associated with each order, and is structured to evolve toward asynchronous processing, observability, cloud deployment, and distributed architecture patterns.
The solution is organized into four main projects:
LogisticsFlow.Api
|
v
LogisticsFlow.Application
|
v
LogisticsFlow.Domain
LogisticsFlow.Infrastructure
|
v
LogisticsFlow.Domain
Contains the core business model and abstractions.
Responsibilities include:
- domain entities;
- order lifecycle rules;
- enums and domain types;
- repository contracts.
Main entities:
Order
└── OrderItems
Order acts as the aggregate root and is responsible for maintaining the consistency of its items and lifecycle.
Contains the application use cases and contracts used by the API.
The Application layer coordinates the execution of business operations without depending directly on infrastructure implementations.
Example flow:
CreateOrderRequest
|
v
CreateOrderUsecase
|
v
OrderEntity
|
v
IOrdersRepository
Contains persistence and external infrastructure implementations.
Current responsibilities include:
- Entity Framework Core;
- SQL Server persistence;
- repository implementations;
- entity configurations;
- database migrations.
Exposes the application through ASP.NET Core Minimal APIs.
Endpoint definitions are separated from the application bootstrap to keep Program.cs focused on dependency registration and middleware configuration.
- .NET 10
- ASP.NET Core
- Minimal APIs
- Entity Framework Core
- SQL Server 2022
- Docker
- Docker Compose
An order contains:
- customer identifier;
- destination;
- current status;
- creation date;
- dispatch date;
- one or more items.
Example:
{
"customerId": 123,
"destination": "Rio de Janeiro",
"items": [
{
"sku": "NOTEBOOK-001",
"quantity": 2
},
{
"sku": "MOUSE-001",
"quantity": 5
}
]
}The order lifecycle is modeled through explicit status transitions.
Created
|
v
Dispatched
|
v
Completed
Orders may also be cancelled according to the rules defined by the domain.
SQL Server runs in a Docker container and uses a named volume for data persistence.
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: logistics-sqlserver
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD}
ports:
- "1433:1433"
volumes:
- sqlserver_data:/var/opt/mssql
volumes:
sqlserver_data:Sensitive values are not stored directly in versioned configuration files.
Docker-specific environment variables are loaded from a local .env file, while the API connection string can be configured using .NET User Secrets during local development.
Database schema changes are managed through Entity Framework Core migrations.
The main relationship is:
Orders
|
| 1:N
v
OrderItems
Entity identifiers use SQL Server uniqueidentifier columns generated with:
NEWSEQUENTIALID()This allows GUID-based identifiers while reducing the index fragmentation associated with fully random GUID generation.
From the solution root:
dotnet ef migrations add MigrationName \
--project src/LogisticsFlow.Infrastructure \
--startup-project src/LogisticsFlow.Apidotnet ef database update \
--project src/LogisticsFlow.Infrastructure \
--startup-project src/LogisticsFlow.ApiMake sure the following tools are installed:
- .NET 10 SDK
- Docker
- Docker Compose
- Entity Framework Core CLI
Install the EF Core CLI if necessary:
dotnet tool install --global dotnet-efCreate a .env file in the repository root:
MSSQL_SA_PASSWORD=your-strong-password
MSSQL_DATABASE=LogisticsFlowThe .env file is ignored by Git and must not be committed.
Start SQL Server:
docker compose up -dCheck the container status:
docker compose psNavigate to the API project:
cd src/LogisticsFlow.ApiConfigure the connection string using .NET User Secrets:
dotnet user-secrets set \
"ConnectionStrings:LogisticsFlowDbStringConnection" \
"Server=localhost,1433;Database=LogisticsFlow;User Id=sa;Password=your-strong-password;TrustServerCertificate=True"Configured secrets can be inspected with:
dotnet user-secrets listFrom the solution root:
dotnet ef database update \
--project src/LogisticsFlow.Infrastructure \
--startup-project src/LogisticsFlow.Apicd src/LogisticsFlow.Api
dotnet watch runPOST /ordersRequest:
{
"customerId": 123,
"destination": "Rio de Janeiro",
"items": [
{
"sku": "NOTEBOOK-001",
"quantity": 2
}
]
}The order and all associated items are persisted as a single aggregate.
A successful request returns 201 Created with the generated order identifier.
The API supports retrieving orders together with their associated items.
Additional query and lifecycle endpoints are being added as the domain evolves.
The next application capabilities include:
GET /orders
GET /orders/{id}
POST /orders/{id}/dispatch
POST /orders/{id}/complete
POST /orders/{id}/cancel
The architecture is also prepared to evolve with:
- domain validation and lifecycle rules;
- standardized error responses with Problem Details;
- unit tests;
- integration tests;
- Testcontainers;
- API containerization;
- Redis caching;
- asynchronous processing with AWS SQS;
- .NET background workers;
- retry and circuit breaker policies;
- Dead Letter Queues;
- idempotent message processing;
- Transactional Outbox;
- OpenTelemetry;
- centralized logs, metrics, and distributed tracing;
- GitHub Actions CI/CD;
- AWS ECR;
- AWS ECS/Fargate;
- Infrastructure as Code with Terraform;
- load testing and horizontal scaling.
LogisticsFlow is designed around a few core principles:
- business rules remain independent from infrastructure concerns;
- application use cases depend on abstractions rather than concrete implementations;
- persistence details are isolated in the Infrastructure layer;
- HTTP contracts are kept separate from domain entities;
- order items are managed as part of the
Orderaggregate; - sensitive configuration is kept outside source control;
- architectural complexity is introduced only when required by the application's behavior.
The goal is to keep the codebase maintainable while allowing the system to evolve toward a scalable and observable cloud-native architecture.