Skip to content

Didza/SalaryCalculator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 

Repository files navigation

Consultant Salary Calculator - Clean Architecture Implementation

A complete consultant compensation management system built with .NET 8 in this project l follow Clean Architecture principles and CQRS pattern with using MediatR.

Architecture Overview

The overview of the architecture is as follows:

┌─────────────────────────────────────────────────────────────┐
│                        API Layer                             │
│  ASP.NET Core Web API - RESTful Controllers + Swagger       │
└────────────────┬────────────────────────────────────────────┘
                 │
┌────────────────▼────────────────────────────────────────────┐
│                   Application Layer                          │
│  MediatR Commands/Queries + Handlers + DTOs + Validators   │
└────────────────┬────────────────────────────────────────────┘
                 │
┌────────────────▼────────────────────────────────────────────┐
│                    Domain Layer                              │
│  Entities + Domain Services + Business Rules                │
└────────────────▲────────────────────────────────────────────┘
                 │
┌────────────────┴────────────────────────────────────────────┐
│                 Infrastructure Layer                         │
│  EF Core + Repositories + DbContext + SQL Server           │
└─────────────────────────────────────────────────────────────┘

Features

Business Requirements

  • ✅ Consultant management with profile images
  • ✅ Role-based salary calculation (Level 1, Level 2, custom roles)
  • ✅ Task creation and assignment
  • ✅ Multiple consultants per task
  • ✅ Multiple tasks per consultant
  • ✅ 12-hour daily limit enforcement
  • ✅ Historical rate preservation (changes don't affect past work)
  • ✅ Timeframe-based earnings calculation
  • ✅ Work hours tracking per day per task

Technical Features

  • Clean Architecture with CQRS pattern
  • MediatR for command/query separation
  • Entity Framework Core with SQL Server
  • Repository and Unit of Work patterns
  • AutoMapper for DTO mapping
  • Swagger/OpenAPI documentation
  • Comprehensive unit tests (87 tests)
  • FluentValidation ready
  • CORS enabled
  • Keycloak Authentication

Project Structure

Mercury.ConsultantCompensation/
├── Mercury.ConsultantCompensation.Domain/           # Domain Layer
│   ├── Entities/
│   │   ├── Consultant.cs                            # Consultant aggregate
│   │   ├── ConsultantRole.cs                        # Role with rates
│   │   ├── WorkTask.cs                              # Task entity
│   │   ├── WorkEntry.cs                             # Work tracking (historical)
│   │   └── TaskAssignment.cs                        # Many-to-many
│   └── Services/
│       └── WorkEntryService.cs                      # 12-hour limit validation
│
├── Mercury.ConsultantCompensation.Application/      # Application Layer
│   ├── DTOs/                                        # Data Transfer Objects
│   ├── Features/                                    # CQRS Commands & Queries
│   │   ├── Consultants/
│   │   ├── ConsultantRoles/
│   │   ├── WorkTasks/
│   │   └── WorkEntries/
│   ├── Interfaces/                                  # Repository interfaces
│   └── Mappings/                                    # AutoMapper profiles
│
├── Mercury.ConsultantCompensation.Infrastructure/   # Infrastructure Layer
│   ├── Data/
│   │   ├── ConsultantCompensationDbContext.cs       # EF Core DbContext
│   │   └── Configurations/                          # Fluent API configs
│   └── Repositories/                                # Repository implementations
│
├── Mercury.ConsultantCompensation.API/              # API Layer
│   ├── Controllers/                                 # RESTful API controllers
│   ├── Program.cs                                   # DI configuration
│   └── appsettings.json                             # Configuration
│
└── Mercury.ConsultantCompensation.Domain.Tests/     # Unit Tests
    ├── Entities/                                    # Entity tests
    └── Services/                                    # Service tests

Getting Started

Prerequisites

  • .NET 8.0 SDK
  • SQL Server (LocalDB or full)
  • Visual Studio 2022 or VS Code

Setup - Local Development

  1. Clone the repository

    git clone https://github.com/Didza/SalaryCalculator.git
    cd SalaryCalculator/Mercury.ConsultantCompensation
  2. Update connection string in appsettings.json:

    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ConsultantCompensationDb;Trusted_Connection=true"
      }
    }
  3. Install EF Core tools (if not already installed):

    dotnet tool install --global dotnet-ef
  4. Create database migration:

    cd Mercury.ConsultantCompensation.Infrastructure
    dotnet ef migrations add InitialCreate --startup-project ../Mercury.ConsultantCompensation.API
  5. Update database:

    dotnet ef database update --startup-project ../Mercury.ConsultantCompensation.API
  6. Run the API:

    cd ../Mercury.ConsultantCompensation.API
    dotnet run
  7. Access Swagger UI:

    • Navigate to: https://localhost:5001/swagger
  8. Create Keycloak realm and client for authentication (optional):

    • Create realm: mercury-consultant-compensation
    • Create client: public-client
    • create roles for client: Management, Consultant

Setup - Docker Compose

  1. Build and run with Docker Compose:
    docker-compose up --build -d
    Then follow sets 8 to create the Keycloak realm and client for authentication.

Running Tests

cd Mercury.ConsultantCompensation
dotnet test

Expected output: 87 tests passed

API Endpoints

Consultants

Method Endpoint Description
GET /api/consultants List all consultants
GET /api/consultants/{id} Get consultant by ID
POST /api/consultants Create new consultant
PUT /api/consultants/{id} Update consultant
PUT /api/consultants/{id}/role Update consultant role
PUT /api/consultants/{id}/profile-image Upload profile image
DELETE /api/consultants/{id} Delete consultant

Consultant Roles

Method Endpoint Description
GET /api/consultantroles List all roles
GET /api/consultantroles/{id} Get role by ID
POST /api/consultantroles Create new role
PUT /api/consultantroles/{id} Update role name
PUT /api/consultantroles/{id}/rate Update hourly rate
DELETE /api/consultantroles/{id} Delete role

Work Tasks

Method Endpoint Description
GET /api/worktasks List all tasks
GET /api/worktasks/{id} Get task by ID
POST /api/worktasks Create new task
PUT /api/worktasks/{id} Update task
POST /api/worktasks/{taskId}/consultants/{consultantId} Assign consultant to task
DELETE /api/worktasks/{taskId}/consultants/{consultantId} Remove consultant from task
DELETE /api/worktasks/{id} Delete task

Work Entries

Method Endpoint Description
GET /api/workentries List all work entries
GET /api/workentries/{id} Get work entry by ID
GET /api/workentries/consultant/{id} Get entries for consultant
GET /api/workentries/consultant/{id}/earnings Calculate earnings for period
POST /api/workentries Create work entry (validates 12-hour limit)
DELETE /api/workentries/{id} Delete work entry

Example Usage

1. Create a Consultant Role

POST /api/consultantroles
Content-Type: application/json

{
  "name": "Consultant Level 1",
  "ratePerHour": 150.00
}

2. Create a Consultant

POST /api/consultants
Content-Type: application/json

{
  "firstName": "John",
  "lastName": "Doe",
  "email": "john.doe@example.com",
  "consultantRoleId": "guid-from-step-1"
}

3. Create a Task

POST /api/worktasks
Content-Type: application/json

{
  "name": "Develop API",
  "description": "Build REST API endpoints",
  "estimatedDurationHours": 40
}

4. Assign Consultant to Task

POST /api/worktasks/{taskId}/consultants/{consultantId}

5. Record Work Hours

POST /api/workentries
Content-Type: application/json

{
  "consultantId": "guid",
  "taskId": "guid",
  "workDate": "2024-02-18",
  "hoursWorked": 8.0
}

Note: This validates the 12-hour daily limit automatically

6. Calculate Earnings

GET /api/workentries/consultant/{consultantId}/earnings?startDate=2024-02-01&endDate=2024-02-28

Response:

{
  "consultantId": "guid",
  "consultantName": "John Doe",
  "startDate": "2024-02-01",
  "endDate": "2024-02-28",
  "totalHoursWorked": 160.0,
  "totalEarnings": 24000.00,
  "workEntries": [...]
}

Key Design Decisions

1. Historical Rate Preservation

Work entries capture the rate and role name at the time of work. Changing current rates doesn't affect previously logged hours.

public class WorkEntry
{
    public decimal RatePerHourAtTimeOfWork { get; private set; }  // Immutable
    public string RoleNameAtTimeOfWork { get; private set; }      // Immutable
    
    public decimal CalculatePayment() 
        => HoursWorked * RatePerHourAtTimeOfWork;  // Always uses historical rate
}

2. 12-Hour Daily Limit

Enforced via domain service before creating work entries:

public class WorkEntryService
{
    private const decimal MaxDailyHours = 12m;
    
    public void ValidateDailyHoursLimit(Consultant consultant, DateTime workDate, decimal newHours)
    {
        var existingHours = consultant.GetTotalHoursForDate(workDate);
        if (existingHours + newHours > MaxDailyHours)
            throw new InvalidOperationException($"Would exceed {MaxDailyHours} hour daily limit");
    }
}

3. CQRS with MediatR

Commands and queries are separated for better maintainability:

// Command
public record CreateConsultantCommand(CreateConsultantDto Dto) : IRequest<ConsultantDto>;

// Query
public record GetConsultantByIdQuery(Guid Id) : IRequest<ConsultantDto?>;

// Handler
public class CreateConsultantCommandHandler : IRequestHandler<CreateConsultantCommand, ConsultantDto>
{
    public async Task<ConsultantDto> Handle(CreateConsultantCommand request, CancellationToken ct)
    {
        // Implementation
    }
}

Testing

Unit Test Coverage

  • 113 tests covering all entities and services
  • Entity validation tests
  • Business rule enforcement tests
  • 12-hour limit tests
  • Historical rate preservation tests
  • Calculation accuracy tests
  • Application Layer

Run tests:

dotnet test --verbosity normal

Technologies Used

  • .NET 8.0 - Framework
  • ASP.NET Core - Web API
  • Entity Framework Core 8.0 - ORM
  • SQL Server - Database
  • MediatR - CQRS implementation
  • AutoMapper - Object mapping
  • FluentValidation - Validation framework
  • Swagger/OpenAPI - API documentation
  • xUnit - Testing framework
  • Docker - Containerization

About

ASP.NET Core Web API for managing consultants, roles, tasks, and role-based salary calculations. Enforces 12-hour daily limits, preserves historical rate integrity, and exposes secure REST endpoints using EF Core and SQL Server. Supports task assignments, hour tracking, and earnings reports.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors