A modern, clean architecture ASP.NET Core Web API template for building secure and scalable applications.
- Clean Architecture - Follows the principles of Clean Architecture with clear separation of concerns
- Domain-Driven Design - Implements DDD patterns for complex business logic
- JWT Authentication - Secure authentication with JWT tokens and refresh token rotation
- CQRS with MediatR - Command Query Responsibility Segregation pattern using MediatR
- Global Exception Handling - Standardized exception handling with ProblemDetails (RFC 7807)
- Swagger/OpenAPI - API documentation with Swagger UI and JWT authentication support
- Repository Pattern - Abstraction over data access with repository and unit of work patterns
- Dependency Injection - Uses attribute-based DI registration with Devlooped.Extensions.DependencyInjection.Attributed
SpringBoard/
├── SpringBoard.Api/ # API layer - Controllers, Middleware, Configuration
├── SpringBoard.Application/ # Application layer - Commands, Queries, Interfaces
├── SpringBoard.Domain/ # Domain layer - Entities, Value Objects, Domain Exceptions
└── SpringBoard.Infrastructure/ # Infrastructure layer - Repository Implementations, External Services
- Domain Layer: Contains enterprise logic and types
- Application Layer: Contains business logic and types
- Infrastructure Layer: Contains all external concerns
- API Layer: Contains everything related to ASP.NET Core Web API
SpringBoard implements a secure authentication system using JWT tokens:
- JWT token-based authentication
- Refresh token rotation for enhanced security
- Password hashing with ASP.NET Core Identity's PasswordHasher
- Login with either username or email
Important: The current implementation uses in-memory fake repositories for demonstration purposes. For production use, you should implement proper persistent repositories (e.g., using Entity Framework Core or another ORM/database technology of your choice). See the Customization section for guidance on implementing your own repositories.
The application uses a domain exception hierarchy for business rule violations:
DomainException: Base exception for all domain-specific errorsEntityNotFoundException: Thrown when an entity cannot be foundDomainUnauthorizedException: Thrown for authorization violationsInvalidEntityStateException: Thrown when an entity is in an invalid stateDuplicateEntityException: Thrown when attempting to create a duplicate entity
All exceptions are handled globally and mapped to appropriate HTTP status codes with standardized ProblemDetails responses.
The template includes a script that allows you to easily rename the solution and all projects to match your desired project name:
-
Make the script executable (if needed)
chmod +x rename-project.sh
-
Run the script with your desired project name
./rename-project.sh TestProject YourProjectName
-
The script will:
- Rename all project directories and files
- Update all namespaces and references in code files
- Update the solution file
- Skip binary files and files listed in .gitignore
-
Build the solution to verify all references are updated correctly
dotnet build
Note: After renaming, you may need to reload your IDE to see all changes. Also, remember to manually check any custom build scripts, Docker files, or CI/CD configurations.
-
Clone the repository
git clone https://github.com/yourusername/testproject.git cd testproject -
Build the solution
dotnet build
-
Run the API
cd SpringBoard.Api dotnet run -
Open your browser and navigate to:
https://localhost:5001/swagger
- Register a new user using the
/api/auth/registerendpoint - Login with the registered user credentials using the
/api/auth/loginendpoint - Use the returned JWT token in the Authorization header for subsequent requests
- When the token expires, use the refresh token to get a new JWT token
- Create a new entity class in the
SpringBoard.Domain/Entitiesfolder - Create a repository interface in the
SpringBoard.Application/Interfacesfolder - Implement the repository in the
SpringBoard.Infrastructure/Persistence/Repositoriesfolder - Register the repository in the DI container
The template uses in-memory fake repositories for demonstration. For a production application:
- Choose a database technology (e.g., SQL Server, PostgreSQL, MongoDB)
- Implement a proper data access layer (e.g., using Entity Framework Core)
- Replace the fake repositories in
SpringBoard.Infrastructure/Persistence/Repositorieswith actual implementations - Update the
FakeUnitOfWorkto use your actual database context - Configure your database connection in
appsettings.json
Example using Entity Framework Core:
public class EfUserRepository : IUserRepository
{
private readonly ApplicationDbContext _dbContext;
public EfUserRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<User> GetByIdAsync(Guid id)
{
return await _dbContext.Users.FindAsync(id);
}
// Implement other methods...
}- Create Command/Query classes in the
SpringBoard.Application/Featuresfolder - Implement the Command/Query handlers
- Create a controller in the
SpringBoard.Api/Controllersfolder - Inject MediatR and send the Command/Query
- ASP.NET Core 9
- MediatR
- JWT Bearer Authentication
- Swagger/OpenAPI
- Devlooped.Extensions.DependencyInjection.Attributed
- Riok.Mapperly
This project is licensed under the MIT License - see the LICENSE file for details.