A modern ASP.NET Core Web API application demonstrating a two-tier architecture with comprehensive user management functionality using CQRS pattern, MediatR, Dapper ORM, and SQL Server.
This application follows a two-tier architecture:
- Presentation Tier: ASP.NET Core Web API with RESTful endpoints
- Data Tier: SQL Server database with optimized data access using Dapper
- CQRS (Command Query Responsibility Segregation) with MediatR
- Repository Pattern with Dapper ORM
- Validation using FluentValidation
- Dependency Injection with built-in ASP.NET Core DI container
- OpenAPI/Swagger documentation
- Docker containerization support
- β Create new users
- β Retrieve all active users
- β Get user by ID
- β Search users by email
- β Update existing users
- β Soft delete users
- β Input validation with detailed error messages
- π Automatic database initialization
- π Comprehensive API documentation with Swagger UI
- π³ Docker containerization
- π‘οΈ Input validation and error handling
- π Performance optimized with database indexing
- π Search functionality
- π Structured logging
- π― .NET 9.0 - Latest Microsoft development platform
- π ASP.NET Core - Cross-platform web framework
- ποΈ Dapper 2.1.35 - Lightweight, high-performance ORM
- ποΈ SQL Server 2022 - Enterprise-grade relational database
- π Microsoft.Data.SqlClient 5.2.2 - Modern SQL Server connectivity
- π‘ MediatR 12.4.1 - CQRS and Mediator pattern implementation
- β FluentValidation 11.10.0 - Fluent interface for validation rules
- π Swashbuckle.AspNetCore 7.2.0 - OpenAPI/Swagger documentation
- π§ Microsoft.AspNetCore.OpenApi 9.0.8 - OpenAPI specification support
- π³ Docker - Containerization platform
- π¨ Visual Studio Code - Recommended IDE with REST Client extension
- βοΈ PowerShell - Windows development environment
- .NET 9.0 SDK
- SQL Server (or Docker container)
- Docker (optional, for containerization)
git clone https://github.com/a0s21en5/two-tier-user-management-api.git
cd two-tier-web-appYou can run this application using either .NET CLI or Docker. Choose the method that best fits your environment:
-
Database Setup: Ensure SQL Server is running locally and update the connection string in
appsettings.json:{ "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=TwoTierWebAppDB;User Id=sa;Password=your-password;TrustServerCertificate=True;MultipleActiveResultSets=true;" } } -
Run the Application:
dotnet restore dotnet build dotnet run
-
Create Docker Network:
docker network create -d bridge two-tier
-
Start SQL Server Container:
docker run -d --name sqlserver2022 --network two-tier \ -e "ACCEPT_EULA=Y" \ -e "MSSQL_SA_PASSWORD=Obito#9775" \ mcr.microsoft.com/mssql/server:2022-latest
-
Build and Run Application:
docker build -t two-tier-backend:v1 . docker run -d -p 5000:5000 --network two-tier two-tier-backend:v1
Once running, you can access the application at:
- π API Base URL:
http://localhost:5000 - π Swagger UI:
http://localhost:5000/swagger - π OpenAPI Spec:
http://localhost:5000/swagger/v1/swagger.json
Note: The application runs on HTTP port 5000 when using Docker. For local development with .NET CLI, both HTTP (5000) and HTTPS (5001) are available.
The application provides a RESTful API for user management operations:
| Method | Endpoint | Description | Status Codes |
|---|---|---|---|
| π’ GET | /api/users |
Retrieve all active users | 200 |
| π’ GET | /api/users/{id} |
Get user by ID | 200, 400, 404 |
| π GET | /api/users/search?email={email} |
Search users by email | 200, 400 |
| β POST | /api/users |
Create a new user | 201, 400, 500 |
| π PUT | /api/users/{id} |
Update existing user | 204, 400, 404, 500 |
| β DELETE | /api/users/{id} |
Soft delete user | 204, 400, 404, 500 |
POST /api/users
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890"
}Response (201 Created):
3GET /api/usersResponse (200 OK):
[
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"createdDate": "2025-09-04T10:30:00Z",
"updatedDate": null,
"isActive": true
},
{
"id": 2,
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@example.com",
"phone": "+1987654321",
"createdDate": "2025-09-04T11:15:00Z",
"updatedDate": "2025-09-04T12:00:00Z",
"isActive": true
}
]GET /api/users/1Response (200 OK):
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"createdDate": "2025-09-04T10:30:00Z",
"updatedDate": null,
"isActive": true
}GET /api/users/search?email=johnPUT /api/users/1
Content-Type: application/json
{
"id": 1,
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@example.com",
"phone": "+1234567890"
}Response (204 No Content)
DELETE /api/users/1Response (204 No Content)
- Swagger UI: Navigate to
http://localhost:5000/swaggerfor interactive testing - HTTP Files: Use the provided
.httpfiles in VS Code with REST Client extension - Postman: Import the API endpoints using the OpenAPI specification
- cURL: Use command-line tools for testing
The API returns consistent error responses:
{
"errors": [
"FirstName is required",
"Email must be a valid email address"
]
}Common HTTP status codes:
- 200: Success
- 201: Created
- 204: No Content (successful update/delete)
- 400: Bad Request (validation errors)
- 404: Not Found
- 500: Internal Server Error
two-tier-web-app/
βββ π Controllers/
β βββ π UsersController.cs # REST API endpoints and HTTP request handling
βββ π Features/ # CQRS implementation
β βββ π Users/
β βββ π Commands/ # Write operations (Create, Update, Delete)
β βββ π Queries/ # Read operations (Get, Search)
β βββ π Validators/ # FluentValidation rules for requests
βββ π Infrastructure/ # Core infrastructure services
β βββ π DatabaseInitializer.cs # Automatic database setup and seeding
β βββ π DbConnectionFactory.cs # Database connection management
βββ π Models/ # Data models and DTOs
β βββ π User.cs # User entity and request/response models
βββ π Database/ # Database scripts
β βββ π CreateDatabase.sql # Database schema and sample data
βββ π Properties/ # Application configuration
β βββ π launchSettings.json # Development server settings
βββ π Program.cs # Application entry point and service configuration
βββ π Dockerfile # Container configuration
βββ π appsettings.json # Application configuration
βββ π appsettings.Development.json # Development environment settings
βββ π appsettings.Production.json # Production environment settings
βββ π *.http # HTTP test files for API testing
- Controllers: Handle HTTP requests and responses
- Validation: Input validation using FluentValidation
- Commands: Handle write operations (CQRS)
- Queries: Handle read operations (CQRS)
- MediatR: Decouples request handling
- Database: SQL Server with Dapper ORM
- Connection Management: Factory pattern for database connections
- Models: Core business entities and DTOs
The application supports multiple environments with specific configuration files:
appsettings.json- Base configurationappsettings.Development.json- Development environmentappsettings.Production.json- Production environment
{
"ConnectionStrings": {
"DefaultConnection": "Your SQL Server connection string"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}The project includes HTTP test files for comprehensive API testing:
- π
two-tier-web-app.http- General API endpoint tests - π
two-tier-web-app-api.http- Specific user management scenarios
- Install the "REST Client" extension in VS Code
- Open any
.httpfile and click "Send Request" above each HTTP call - View responses directly in VS Code
- Navigate to
http://localhost:5000/swagger - Test all endpoints with an interactive interface
- View request/response schemas and examples
- Import the OpenAPI specification:
http://localhost:5000/swagger/v1/swagger.json - Automatically generate a complete collection of API calls
# Test API health
curl -X GET "http://localhost:5000/api/users"
# Create a new user
curl -X POST "http://localhost:5000/api/users" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Test",
"lastName": "User",
"email": "test@example.com",
"phone": "+1234567890"
}'β CRUD Operations
- Create new users with valid data
- Retrieve all users
- Get specific users by ID
- Update existing user information
- Soft delete users
β Validation Testing
- Required field validation
- Email format validation
- Input length constraints
- Invalid ID handling
β Error Handling
- Non-existent user lookup
- Invalid request formats
- Server error scenarios
The database initializes with sample users for immediate testing:
- John Doe (john.doe@example.com)
- Jane Smith (jane.smith@example.com)
- Bob Johnson (bob.johnson@example.com)
- Alice Brown (alice.brown@example.com)
- Charlie Wilson (charlie.wilson@example.com)
The Docker setup uses a custom bridge network to enable communication between containers:
βββββββββββββββββββββββββββββββββββββββ
β two-tier network β
β β
β βββββββββββββββββββ ββββββββββββββ β
β β sqlserver2022 β β web-app β β
β β (SQL Server) β β (Backend) β β
β β Port: 1433 β β Port: 5000 β β
β βββββββββββββββββββ ββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββ
docker ps# Get container ID first
docker ps
# View logs
docker logs <container-id>
# Follow logs in real-time
docker logs -f <container-id># Stop containers
docker stop sqlserver2022 <web-app-container-id>
# Remove containers
docker rm sqlserver2022 <web-app-container-id>
# Remove network
docker network rm two-tier
# Remove image (optional)
docker rmi two-tier-backend:v1For easier management, create a docker-compose.yml file:
version: '3.8'
networks:
two-tier:
driver: bridge
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: sqlserver2022
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=Obito#9775
networks:
- two-tier
volumes:
- sqlserver_data:/var/opt/mssql
healthcheck:
test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Obito#9775 -Q 'SELECT 1'"]
interval: 30s
timeout: 10s
retries: 3
web-app:
build: .
container_name: two-tier-backend
ports:
- "5000:5000"
depends_on:
sqlserver:
condition: service_healthy
networks:
- two-tier
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnection=Server=sqlserver2022,1433;Database=TwoTierWebAppDB;User Id=sa;Password=Obito#9775;TrustServerCertificate=True;MultipleActiveResultSets=true;
volumes:
sqlserver_data:# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v| Column | Type | Description |
|---|---|---|
| Id | INT IDENTITY | Primary key |
| FirstName | NVARCHAR(50) | User's first name |
| LastName | NVARCHAR(50) | User's last name |
| NVARCHAR(100) | User's email address | |
| Phone | NVARCHAR(20) | User's phone number |
| CreatedDate | DATETIME2 | Record creation timestamp |
| UpdatedDate | DATETIME2 | Last update timestamp |
| IsActive | BIT | Soft delete flag |
IX_Users_Email- Email lookup optimizationIX_Users_IsActive- Active users filteringIX_Users_CreatedDate- Date-based queries
- FirstName: Required, max 50 characters
- LastName: Required, max 50 characters
- Email: Required, valid email format, max 100 characters
- Phone: Required, max 20 characters
- Id: Required, must be positive integer
- All fields from Create User Request
- Commands/Queries: Add to
Features/Users/CommandsorFeatures/Users/Queries - Validation: Add validators to
Features/Users/Validators - API Endpoints: Extend
UsersController - Database Changes: Update
CreateDatabase.sqlandDatabaseInitializer
- Follow CQRS pattern for separation of concerns
- Use FluentValidation for input validation
- Implement proper error handling and logging
- Write comprehensive API documentation
- Use async/await for database operations
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in the repository
- Contact the development team
Happy Coding! π