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:

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:

Business Logic Layer (BLL)

Contains business rules and validations.

Examples:

Data Access Layer (DAL)

Handles database operations.

Examples:

Shared Layer

Contains reusable components.

Examples:

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:

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:

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:

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

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:

Vehicle Rental Business Rules

The Vehicle Rental Service contains business validations before creating rentals.

Rules Implemented

These validations ensure business consistency before a rental is created.

Vehicle Return Process

When a customer returns a vehicle:

  1. Rental status is verified.

  2. Return date is recorded.

  3. Rental status changes to Returned.

  4. Vehicle availability is increased.

  5. 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:

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

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.