Introduction
As software applications grow, maintaining clean code, separation of concerns, and scalability becomes increasingly important. One of the most widely adopted architectural patterns in enterprise .NET applications is N-Layer Architecture, which separates an application into distinct layers, each responsible for a specific set of tasks.
In this article, we will build a Vehicle Rental Management System using C#, Entity Framework Core, Repository Pattern, and Service Layer Architecture. The project follows a structured N-Layer approach that improves maintainability, testability, and code organization.
The implementation includes:
Entity Framework Core with SQL Server
Repository Pattern
Service Layer
Session Management
Authentication and Registration
Email Notifications
Vehicle Rental Operations
Console-Based User Interface
Result Wrapper Pattern
The project demonstrates how enterprise-level architecture can be implemented even in a console application.
What Is N-Layer Architecture?
N-Layer Architecture divides an application into multiple logical layers.
Presentation Layer
Responsible for user interaction.
Examples:
Console UI
ASP.NET MVC
Blazor
WinForms
Business Logic Layer (BLL)
Contains business rules and validations.
Examples:
Authentication rules
Vehicle rental policies
Customer validations
Data Access Layer (DAL)
Handles database operations.
Examples:
Entity Framework Core
Repository classes
Database Context
Shared Layer
Contains reusable components.
Examples:
Enums
DTOs
Utility classes
Result wrappers
Project Structure
Solution
│
├── Presentation
│ ├── Views
│ └── Program.cs
│
├── BLL
│ ├── Services
│ └── Interfaces
│
├── DAL
│ ├── Context
│ ├── Entities
│ ├── Repository
│ └── Interfaces
│
├── Utilities
│ ├── Validators
│ ├── SessionManager
│ └── EmailSender
│
└── Shared
├── Enums
└── ResultWrapper
Required NuGet Packages
Install the following packages before starting the project:
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.Design
For reading configuration from appsettings.json:
Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Microsoft.Extensions.Configuration.FileExtensions
Install-Package Microsoft.Extensions.FileProviders.Physical
Additional console UI packages:
Install-Package Spectre.Console
Install-Package ConsoleTables
These packages are used throughout the application setup.
Configuring Entity Framework Core
After installing packages, create migrations and update the database.
Add-Migration InitialCreate
Update-Database
Creating the Database Context
The AppDbContext acts as the bridge between Entity Framework Core and SQL Server.
DbSets
The application contains the following entities:
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<Rental> Rentals { get; set; }
The context loads the connection string from appsettings.json and configures SQL Server using Entity Framework Core. It also defines relationships between:
User and Customer
Customer and Rental
Vehicle and Rental
The project additionally uses seed data to insert default users and vehicles during database creation.
Database Configuration
The SQL Server connection string is stored in appsettings.json.
{
"ConnectionStrings": {
"DefaultConnection": "Your SQL Server Connection String"
}
}
This approach keeps database configuration separate from application code.
Implementing the Repository Pattern
The Repository Pattern acts as an abstraction layer between the Business Layer and the database.
Benefits include:
Instead of accessing Entity Framework directly inside services, repositories handle all CRUD operations.
Typical repositories include:
UserRepository
CustomerRepository
VehicleRepository
RentalRepository
Implementing the Service Layer
The Service Layer contains all business logic.
Examples include:
Services interact with repositories rather than directly communicating with the database.
Authentication Service
The AuthServices class handles:
User login
User registration
Email validation
Password validation
Session creation
The service uses dependency-based communication with repositories to perform user-related operations.
Session Management
The application maintains the logged-in user using a static Session Manager.
Features
Login
Logout
Current User Tracking
Role Checking
public static User? CurrentUser { get; private set; }
public static bool IsLoggedIn()
{
return CurrentUser != null;
}
This allows role-based navigation throughout the application.
Input Validation
Validation is centralized inside a utility class.
Email Validation
The application validates email addresses using regular expressions.
Password Validation
Passwords are checked before registration and login.
Benefits:
Implementing Email Notifications
The system includes an Email Sender service using SMTP.
Features
public interface IEmailSender
{
Task SendEmailAsync(string toEmail, string subject, string body);
}
The implementation uses Gmail SMTP for sending emails.
Result Wrapper Pattern
Instead of returning primitive values, the application uses a generic Result Wrapper.
public class Result<T>
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public T? Data { get; set; }
}
Benefits:
Consistent API responses
Better error handling
Easier debugging
Strongly typed responses
Vehicle Rental Business Rules
The Vehicle Rental Service contains business validations before creating rentals.
Rules Implemented
Rental period must be between 1 and 30 days
Customer must exist
Vehicle must exist
Vehicle must be available
Maximum 2 active rentals per customer
Same vehicle cannot be rented twice simultaneously
These validations ensure business consistency before a rental is created.
Vehicle Return Process
When a customer returns a vehicle:
Rental status is verified.
Return date is recorded.
Rental status changes to Returned.
Vehicle availability is increased.
Database records are updated.
This logic is encapsulated inside the User Service layer.
Console User Interface
The application uses Spectre.Console to create an interactive console experience.
Public Menu
Available before login:
Administrator Menu
Available after admin login:
Customer Menu
Available after customer login:
View Available Plans
Subscribe to a Plan
Pause Subscription
Resume Subscription
Cancel Subscription
Logout
The menu-driven structure provides a clean user experience inside the console environment.
Advantages of Repository and Service Pattern
Separation of Concerns
Each layer handles a specific responsibility.
Maintainability
Business rules remain isolated from database logic.
Scalability
Additional features can be added without modifying existing layers.
Testability
Repositories and services can be mocked during unit testing.
Reusability
Business logic can be reused across multiple UI layers.
Best Practices Followed
N-Layer Architecture
Repository Pattern
Service Layer Pattern
Entity Framework Core
Configuration Management
Centralized Validation
Session Management
Generic Result Wrapper
Dependency-Based Design
Database Seeding
Conclusion
N-Layer Architecture remains one of the most effective ways to organize medium and large-scale C# applications. By combining Entity Framework Core, Repository Pattern, Service Layer, and Utility Components, developers can create applications that are easier to maintain, extend, and test.
The Vehicle Rental Management System demonstrated in this article shows how these architectural principles work together in a real-world scenario. Although the application is console-based, the same architecture can easily be extended to ASP.NET Core Web APIs, MVC applications, Blazor projects, or enterprise solutions.