> For the complete documentation index, see [llms.txt](https://docs.flexbase.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flexbase.in/sample-references-domain/recruitment-app.md).

# Recruitment App

## 📌 Requirements Summary & BA Implementation Guide

### Requirements Summary (What the business needs)

* **Purpose**: End-to-end recruitment management from job posting to offer and hire, with auditable workflow and strict role-based access.
* **Actors/Roles**: Manager/TeamLead, HR/RecruitmentManager, Employee (referrals), Interviewer, Finance, Admin, Vendor (external agencies, no login rights), Contractor (future).
* **Core Entities**: JobPosting, Candidate, EmployeeReferral, VendorReferral, Vendor, Application, Interview, Offer, Employee (result of successful hire), OrgAccount, UserAccount, AccountabilityMatrix.
* **Key Workflows**:
  * JobPosting: Draft → Published → Closed → Archived
  * Candidate: Created → UnderReview → Shortlisted → InterviewScheduled → InterviewCompleted → Selected → OfferSent → Hired
  * EmployeeReferral: Submitted → UnderReview → Approved/Rejected → ConvertedToApplication
  * VendorReferral: Submitted → Approved/Rejected → ConvertedToApplication
  * Application: Created → Screened → InterviewScheduled → InterviewCompleted → OfferMade → Rejected
  * Interview: Scheduled → Completed/Cancelled
  * Offer: Created → Approved → Accepted/Rejected/Withdrawn
* **Access Control**: Each transition is role-gated (e.g., HR can close published postings; Interviewer can complete only assigned interviews; Candidate accepts/ rejects offers).
* **Approvals**: Multi-level approvals for job postings and offers (HR → Manager → Director/VP → CEO/Finance as applicable) with timeouts and escalation.
* **Organizational Hierarchy**: OrgAccount and UserAccount structure for managing organizational relationships and accountability matrix.
* **Integrations/Future**: Background checks, email/notifications, contractor imports; all designed for later enablement.
* **Reporting & Audit**: Full audit trail of state transitions, approvals, and key events; metrics on pipeline throughput.
* **Non-Functional**: Security, configurability (dynamic workflow rules), scalability, and maintainability.
* **Out of Scope (current)**: Public candidate self-service portal; full contractor module execution.

## 🎯 Application Overview

This document demonstrates how to implement a comprehensive employee recruitment application using the Flexbase framework, featuring domain models, workflow states, multi-level approval processes, and enterprise-grade role-based access control.

***

## 📋 Business Requirements

### **Core Entities:**

* **Job Posting** - Open positions with requirements
* **Candidate** - Potential candidates managed by HR/Recruiters
* **Employee Referral** - Employee referrals for candidates
* **Vendor Referral** - External vendor/agency referrals for candidates
* **Vendor** - External vendors/agencies that can refer candidates
* **Application** - Candidate's application for a position (created by HR/Recruiter)
* **Interview** - Interview sessions and feedback
* **Offer** - Job offers and negotiations
* **Employee** - Successfully hired candidates
* **OrgAccount** - Organizational accounts for hierarchy management
* **UserAccount** - User accounts for personal information management
* **AccountabilityMatrix** - Responsibility relationships between entities
* **Contractor** - External contractors for recruitment (future)

### **Workflow Requirements:**

* **Multi-level Approval** - HR → Manager → Director → CEO
* **Interview Process** - Phone → Technical → Final → HR
* **Background Check** - Reference verification
* **Offer Management** - Salary negotiation and approval
* **Onboarding** - New employee setup

### **Role-Based Access Control:**

* **Manager/TeamLead** - Create and manage job postings
* **HR/RecruitmentManager** - Approve postings, manage candidates, create applications, manage vendor referrals
* **Employee** - Submit referrals for candidates
* **Interviewer** - Conduct interviews, provide feedback
* **Finance** - Approve salary offers
* **Vendor** - External vendors/agencies (no login rights; managed by HR)
* **Contractor** - External recruitment services (future)
* **Admin** - Full system access

***

## 🏗️ Application Modules and Features Mapping

### **Module Structure Overview**

The recruitment application is organized into **8 core modules**, each containing multiple **features (controllers)** that map to specific **domain objects** and **actions**.

| Module                | Features (Controllers)      | Domain Objects   | Primary Actions                              |
| --------------------- | --------------------------- | ---------------- | -------------------------------------------- |
| **JobPostings**       | JobPostingsController       | JobPosting       | Create, Update, Publish, Close, Archive      |
| **Candidates**        | CandidatesController        | Candidate        | Create, Update, Search, View, Manage         |
| **EmployeeReferrals** | EmployeeReferralsController | EmployeeReferral | Submit, Update, View, Approve                |
| **Vendors**           | VendorsController           | Vendor           | GetVendorsForLookup (HR-managed)             |
| **VendorReferrals**   | VendorReferralsController   | VendorReferral   | Submit, Update, View, Approve (via features) |
| **Applications**      | ApplicationsController      | Application      | Create, Screen, ScheduleInterview, Reject    |
| **Interviews**        | InterviewsController        | Interview        | Schedule, Complete, Reschedule, Cancel       |
| **Offers**            | OffersController            | Offer            | Create, Approve, Accept, Reject, Withdraw    |

***

## 📋 Detailed Module and Feature Mapping

### **1. JobPostings Module**

#### **JobPostingsController Features:**

* **CreateJobPosting** (POST) → `JobPosting.CreateJobPosting()`
* **UpdateJobPosting** (PUT) → `JobPosting.UpdateJobPosting()`
* **PublishJobPosting** (POST) → `JobPosting.PublishJobPosting()`
* **CloseJobPosting** (POST) → `JobPosting.CloseJobPosting()`
* **ArchiveJobPosting** (POST) → `JobPosting.ArchiveJobPosting()`
* **GetJobPosting** (GET) → `JobPosting` (Read-only)
* **GetJobPostings** (GET) → `JobPosting` (List with filtering)
* **GetJobPostingsByDepartment** (GET) → `JobPosting` (Filtered by department)
* **GetJobPostingsByStatus** (GET) → `JobPosting` (Filtered by status)

#### **Domain Object: JobPosting**

* **State Machine:** `JobPostingWorkflowState`
* **States:** Draft → Published → Closed → Archived
* **Role-Based Actions:**
  * **Manager/TeamLead:** Create, Update, Publish, Close (Draft state)
  * **HR/RecruitmentManager:** Publish, Close, Archive (Published state)
  * **Admin:** All actions across all states

***

### **2. Candidates Module**

#### **CandidatesController Features:**

* **CreateCandidate** (POST) → `Candidate.CreateCandidate()`
* **UpdateCandidateProfile** (PUT) → `Candidate.UpdateProfile()`
* **GetCandidate** (GET) → `Candidate` (Read-only)
* **GetCandidates** (GET) → `Candidate` (List with filtering)
* **SearchCandidates** (GET) → `Candidate` (Search by skills, experience)
* **GetCandidateApplications** (GET) → `Candidate.Applications` (Related data)
* **UploadResume** (POST) → `Candidate` (File upload)
* **DeleteCandidate** (DELETE) → `Candidate` (Soft delete)
* **ImportCandidates** (POST) → `Candidate` (Bulk import from contractors)

#### **Domain Object: Candidate**

* **State Machine:** `CandidateManagementState`
* **States:** Created → UnderReview → Shortlisted → InterviewScheduled → InterviewCompleted → Selected → OfferSent → Hired
* **Integration with Vendor Referrals:**
  * Candidates can be created with vendor referral information in a single operation
  * HR users can select a vendor and job posting during candidate creation
  * Vendor referral is automatically created and linked to the candidate
* **Role-Based Actions:**
  * **HR/RecruitmentManager:** All candidate management operations, create vendor referrals during candidate creation
  * **Employee:** View candidates for referral purposes
  * **Interviewer:** View candidate profiles for assigned interviews
  * **Manager:** View candidates for their job postings
  * **Contractor:** Submit candidate profiles (future)

***

### **3. Vendors Module**

#### **VendorsController Features:**

* **GetVendorsForLookup** (GET) → `Vendor` (Lookup for autocomplete/dropdowns)

#### **Domain Object: Vendor**

* **Purpose:** Represents external vendors/agencies that can refer candidates
* **Key Relationships:**
  * **OrgAccount** (required) - One OrgAccount per Vendor (vendor organization)
  * **VendorReferrals** - Collection of referrals made by this vendor
* **Key Properties:**
  * `OrgAccountId` - Foreign key to OrgAccount (vendor name comes from OrgAccount.Name)
  * `ContactPersonName` - Primary contact person
  * `ContactEmail` - Primary contact email
  * `ContactPhone` - Primary contact phone
  * `DefaultCommissionPercentage` - Default commission rate
  * `Status` - Vendor status
  * `Notes` - Additional notes
* **Access Control:**
  * **HR/RecruitmentManager:** View vendors, select vendors for candidate creation
  * **Vendors:** No login rights; vendors are managed by HR in the candidate creation flow
* **Integration:**
  * Vendors are selected by HR users during candidate creation
  * Vendor referrals are created alongside candidates in a single operation
  * Vendor name is displayed from the associated OrgAccount

***

### **4. Vendor Referrals Module**

#### **VendorReferralsController Features:**

* **Note:** Business actions (Submit, Approve, Reject, Update) will be created through features and modules later
* **Current Implementation:** VendorReferral creation is integrated into Candidate creation flow

#### **Domain Object: VendorReferral**

* **Purpose:** Represents a candidate referral from an external vendor/agency
* **State Machine:** `VendorReferralWorkflowState`
* **States:** Submitted → Approved → Rejected → ConvertedToApplication
* **Key Relationships:**
  * **Vendor** (required) - The vendor making the referral
  * **Candidate** (required) - The candidate being referred
  * **JobPosting** (required) - The job posting for this referral
  * **Applications** - Applications created from this referral
* **Key Properties:**
  * `VendorId` - Foreign key to Vendor
  * `CandidateId` - Foreign key to Candidate
  * `JobPostingId` - Foreign key to JobPosting
  * `ReferralReason` - Reason for referral
  * `AdditionalNotes` - Additional notes
  * `CommissionAmount` - Commission amount (vendor-specific)
  * `CommissionPercentage` - Commission percentage
  * `CommissionPaidDate` - Date commission was paid
  * `Status` - Referral status
  * `ApprovedBy`, `ApprovedDate` - Approval tracking
  * `RejectedBy`, `RejectedDate`, `RejectionReason` - Rejection tracking
* **Integration with Candidate Creation:**
  * Vendor referrals can be created alongside candidates in a single operation
  * HR users select vendor and job posting during candidate creation
  * Vendor referral information is captured in `CreateCandidateVendorReferralDto`
  * The referral is automatically linked to the candidate using `SetCandidateId()` and `SetVendorId()` methods
* **Role-Based Actions:**
  * **HR/RecruitmentManager:** Create vendor referrals during candidate creation, approve/reject referrals (via features)
  * **Vendors:** No direct access; referrals are created by HR on behalf of vendors

***

### **5. Employee Referrals Module**

#### **EmployeeReferralsController Features:**

* **SubmitReferral** (POST) → `EmployeeReferral.SubmitReferral()`
* **UpdateReferral** (PUT) → `EmployeeReferral.UpdateReferral()`
* **GetReferral** (GET) → `EmployeeReferral` (Read-only)
* **GetReferrals** (GET) → `EmployeeReferral` (List with filtering)
* **GetReferralsByEmployee** (GET) → `EmployeeReferral` (Filtered by employee)
* **ApproveReferral** (POST) → `EmployeeReferral.ApproveReferral()`
* **RejectReferral** (POST) → `EmployeeReferral.RejectReferral()`
* **GetReferralStatus** (GET) → `EmployeeReferral` (Status tracking)

#### **Domain Object: EmployeeReferral**

* **State Machine:** `EmployeeReferralState`
* **States:** Submitted → UnderReview → Approved → Rejected → ConvertedToApplication
* **Role-Based Actions:**
  * **Employee:** Submit, Update, View own referrals
  * **HR/RecruitmentManager:** Review, Approve, Reject, Convert to application
  * **Manager:** View referrals for their department
  * **Admin:** All referral management operations

***

### **6. Applications Module**

#### **ApplicationsController Features:**

* **CreateApplication** (POST) → `Application.CreateApplication()`
* **ScreenApplication** (POST) → `Application.ScreenApplication()`
* **ScheduleInterview** (POST) → `Application.ScheduleInterview()`
* **CompleteInterview** (POST) → `Application.CompleteInterview()`
* **MakeOffer** (POST) → `Application.MakeOffer()`
* **RejectApplication** (POST) → `Application.RejectApplication()`
* **GetApplication** (GET) → `Application` (Read-only)
* **GetApplications** (GET) → `Application` (List with filtering)
* **GetApplicationsByJobPosting** (GET) → `Application` (Filtered by job posting)
* **GetApplicationsByCandidate** (GET) → `Application` (Filtered by candidate)
* **GetApplicationsByStatus** (GET) → `Application` (Filtered by status)
* **ConvertFromReferral** (POST) → `Application.ConvertFromReferral()`

#### **Domain Object: Application**

* **State Machine:** `ApplicationWorkflowState`
* **States:** Created → Screened → InterviewScheduled → InterviewCompleted → OfferMade → Rejected
* **Role-Based Actions:**
  * **HR/RecruitmentManager:** Create, Screen, ScheduleInterview, MakeOffer, Reject, Convert from referral
  * **Interviewer:** CompleteInterview (for assigned interviews)
  * **Manager:** View applications for their job postings
  * **Employee:** View applications created from their referrals

***

### **7. Interviews Module**

#### **InterviewsController Features:**

* **ScheduleInterview** (POST) → `Interview.ScheduleInterview()`
* **CompleteInterview** (POST) → `Interview.CompleteInterview()`
* **RescheduleInterview** (POST) → `Interview.RescheduleInterview()`
* **CancelInterview** (POST) → `Interview.CancelInterview()`
* **SubmitFeedback** (POST) → `Interview.SubmitFeedback()`
* **GetInterview** (GET) → `Interview` (Read-only)
* **GetInterviews** (GET) → `Interview` (List with filtering)
* **GetInterviewsByApplication** (GET) → `Interview` (Filtered by application)
* **GetInterviewsByInterviewer** (GET) → `Interview` (Filtered by interviewer)
* **GetUpcomingInterviews** (GET) → `Interview` (Filtered by date)
* **GetInterviewHistory** (GET) → `Interview` (Historical data)

#### **Domain Object: Interview**

* **State Machine:** `InterviewWorkflowState`
* **States:** Scheduled → Completed → Cancelled
* **Role-Based Actions:**
  * **HR/RecruitmentManager:** Schedule, Reschedule, Cancel, View all
  * **Interviewer:** Complete, SubmitFeedback (only for assigned interviews)
  * **Manager:** View interviews for their job postings
  * **Candidate:** View own interviews

***

### **8. Offers Module**

#### **OffersController Features:**

* **CreateOffer** (POST) → `Offer.CreateOffer()`
* **ApproveOffer** (POST) → `Offer.ApproveOffer()`
* **AcceptOffer** (POST) → `Offer.AcceptOffer()`
* **RejectOffer** (POST) → `Offer.RejectOffer()`
* **WithdrawOffer** (POST) → `Offer.WithdrawOffer()`
* **UpdateOffer** (PUT) → `Offer.UpdateOffer()`
* **GetOffer** (GET) → `Offer` (Read-only)
* **GetOffers** (GET) → `Offer` (List with filtering)
* **GetOffersByApplication** (GET) → `Offer` (Filtered by application)
* **GetOffersByStatus** (GET) → `Offer` (Filtered by status)
* **GetPendingOffers** (GET) → `Offer` (Filtered by pending status)

#### **Domain Object: Offer**

* **State Machine:** `OfferWorkflowState`
* **States:** Created → Approved → Accepted/Rejected/Withdrawn
* **Role-Based Actions:**
  * **HR/RecruitmentManager:** Create, Approve, Withdraw, View all
  * **Finance:** Approve (for salary validation)
  * **Admin:** All actions across all states
  * **Candidate:** Accept, Reject (only for approved offers)

***

## 🔄 Complete Action-to-Domain Mapping

### **JobPostings Module Actions:**

| Action              | HTTP Method | Controller Method | Domain Method                  | State Transition   | Required Role                 |
| ------------------- | ----------- | ----------------- | ------------------------------ | ------------------ | ----------------------------- |
| Create Job Posting  | POST        | CreateJobPosting  | JobPosting.CreateJobPosting()  | → Draft            | Manager, TeamLead             |
| Update Job Posting  | PUT         | UpdateJobPosting  | JobPosting.UpdateJobPosting()  | Draft → Draft      | Manager, TeamLead             |
| Publish Job Posting | POST        | PublishJobPosting | JobPosting.PublishJobPosting() | Draft → Published  | Manager, TeamLead, HR         |
| Close Job Posting   | POST        | CloseJobPosting   | JobPosting.CloseJobPosting()   | Published → Closed | HR, Admin, RecruitmentManager |
| Archive Job Posting | POST        | ArchiveJobPosting | JobPosting.ArchiveJobPosting() | Closed → Archived  | HR, Admin, RecruitmentManager |
| Get Job Posting     | GET         | GetJobPosting     | -                              | -                  | All (with restrictions)       |
| List Job Postings   | GET         | GetJobPostings    | -                              | -                  | All (with restrictions)       |

### **Candidates Module Actions:**

| Action            | HTTP Method | Controller Method      | Domain Method                | State Transition  | Required Role                      |
| ----------------- | ----------- | ---------------------- | ---------------------------- | ----------------- | ---------------------------------- |
| Create Candidate  | POST        | CreateCandidate        | Candidate.CreateCandidate()  | → Created         | HR, RecruitmentManager             |
| Update Profile    | PUT         | UpdateCandidateProfile | Candidate.UpdateProfile()    | Created → Created | HR, RecruitmentManager             |
| Get Candidate     | GET         | GetCandidate           | -                            | -                 | All (with restrictions)            |
| Search Candidates | GET         | SearchCandidates       | -                            | -                 | HR, RecruitmentManager, Employee   |
| Upload Resume     | POST        | UploadResume           | Candidate.UploadResume()     | -                 | HR, RecruitmentManager             |
| Delete Candidate  | DELETE      | DeleteCandidate        | Candidate.DeleteCandidate()  | Created → Deleted | HR, Admin                          |
| Import Candidates | POST        | ImportCandidates       | Candidate.ImportCandidates() | -                 | HR, RecruitmentManager, Contractor |

### **Employee Referrals Module Actions:**

| Action           | HTTP Method | Controller Method | Domain Method                      | State Transition      | Required Role                    |
| ---------------- | ----------- | ----------------- | ---------------------------------- | --------------------- | -------------------------------- |
| Submit Referral  | POST        | SubmitReferral    | EmployeeReferral.SubmitReferral()  | → Submitted           | Employee                         |
| Update Referral  | PUT         | UpdateReferral    | EmployeeReferral.UpdateReferral()  | Submitted → Submitted | Employee                         |
| Approve Referral | POST        | ApproveReferral   | EmployeeReferral.ApproveReferral() | Submitted → Approved  | HR, RecruitmentManager           |
| Reject Referral  | POST        | RejectReferral    | EmployeeReferral.RejectReferral()  | Submitted → Rejected  | HR, RecruitmentManager           |
| Get Referral     | GET         | GetReferral       | -                                  | -                     | All (with restrictions)          |
| List Referrals   | GET         | GetReferrals      | -                                  | -                     | HR, RecruitmentManager, Employee |

### **Vendors Module Actions:**

| Action                 | HTTP Method | Controller Method   | Domain Method | State Transition | Required Role          |
| ---------------------- | ----------- | ------------------- | ------------- | ---------------- | ---------------------- |
| Get Vendors For Lookup | GET         | GetVendorsForLookup | -             | -                | HR, RecruitmentManager |

### **Vendor Referrals Module Actions:**

| Action                                                                                                      | HTTP Method | Controller Method | Domain Method               | State Transition | Required Role          |
| ----------------------------------------------------------------------------------------------------------- | ----------- | ----------------- | --------------------------- | ---------------- | ---------------------- |
| Create Vendor Referral (via Candidate)                                                                      | POST        | CreateCandidate   | Candidate.CreateCandidate() | -                | HR, RecruitmentManager |
| Note: Business actions (Submit, Approve, Reject, Update) will be created through features and modules later |             |                   |                             |                  |                        |

### **Applications Module Actions:**

| Action                | HTTP Method | Controller Method   | Domain Method                     | State Transition                        | Required Role                    |
| --------------------- | ----------- | ------------------- | --------------------------------- | --------------------------------------- | -------------------------------- |
| Create Application    | POST        | CreateApplication   | Application.CreateApplication()   | → Created                               | HR, RecruitmentManager           |
| Convert from Referral | POST        | ConvertFromReferral | Application.ConvertFromReferral() | → Created                               | HR, RecruitmentManager           |
| Screen Application    | POST        | ScreenApplication   | Application.ScreenApplication()   | Created → Screened/Rejected             | HR, RecruitmentManager           |
| Schedule Interview    | POST        | ScheduleInterview   | Application.ScheduleInterview()   | Screened → InterviewScheduled           | HR, RecruitmentManager           |
| Complete Interview    | POST        | CompleteInterview   | Application.CompleteInterview()   | InterviewScheduled → InterviewCompleted | Interviewer (assigned)           |
| Make Offer            | POST        | MakeOffer           | Application.MakeOffer()           | InterviewCompleted → OfferMade          | HR, RecruitmentManager           |
| Reject Application    | POST        | RejectApplication   | Application.RejectApplication()   | Any → Rejected                          | HR, RecruitmentManager           |
| Get Application       | GET         | GetApplication      | -                                 | -                                       | All (with restrictions)          |
| List Applications     | GET         | GetApplications     | -                                 | -                                       | HR, RecruitmentManager, Employee |

### **Interviews Module Actions:**

| Action               | HTTP Method | Controller Method   | Domain Method                    | State Transition      | Required Role           |
| -------------------- | ----------- | ------------------- | -------------------------------- | --------------------- | ----------------------- |
| Schedule Interview   | POST        | ScheduleInterview   | Interview\.ScheduleInterview()   | → Scheduled           | HR, RecruitmentManager  |
| Complete Interview   | POST        | CompleteInterview   | Interview\.CompleteInterview()   | Scheduled → Completed | Interviewer (assigned)  |
| Reschedule Interview | POST        | RescheduleInterview | Interview\.RescheduleInterview() | Scheduled → Scheduled | HR, Interviewer         |
| Cancel Interview     | POST        | CancelInterview     | Interview\.CancelInterview()     | Scheduled → Cancelled | HR, Interviewer         |
| Submit Feedback      | POST        | SubmitFeedback      | Interview\.SubmitFeedback()      | Completed → Completed | Interviewer (assigned)  |
| Get Interview        | GET         | GetInterview        | -                                | -                     | All (with restrictions) |
| List Interviews      | GET         | GetInterviews       | -                                | -                     | HR, RecruitmentManager  |

### **Offers Module Actions:**

| Action         | HTTP Method | Controller Method | Domain Method         | State Transition             | Required Role           |
| -------------- | ----------- | ----------------- | --------------------- | ---------------------------- | ----------------------- |
| Create Offer   | POST        | CreateOffer       | Offer.CreateOffer()   | → Created                    | HR, RecruitmentManager  |
| Approve Offer  | POST        | ApproveOffer      | Offer.ApproveOffer()  | Created → Approved           | HR, Finance, Admin      |
| Accept Offer   | POST        | AcceptOffer       | Offer.AcceptOffer()   | Approved → Accepted          | Candidate               |
| Reject Offer   | POST        | RejectOffer       | Offer.RejectOffer()   | Approved → Rejected          | Candidate               |
| Withdraw Offer | POST        | WithdrawOffer     | Offer.WithdrawOffer() | Created/Approved → Withdrawn | HR, RecruitmentManager  |
| Update Offer   | PUT         | UpdateOffer       | Offer.UpdateOffer()   | Created → Created            | HR, RecruitmentManager  |
| Get Offer      | GET         | GetOffer          | -                     | -                            | All (with restrictions) |
| List Offers    | GET         | GetOffers         | -                     | -                            | HR, RecruitmentManager  |

***

## 🎯 State Machine Integration

### **Complete State Flow with Actions:**

```mermaid
graph TD
    A[Job Posting Draft] -->|Publish| B[Job Posting Published]
    B -->|Close| C[Job Posting Closed]
    C -->|Archive| D[Job Posting Archived]
    
    E[Candidate Created] -->|Review| F[Candidate Under Review]
    F -->|Shortlist| G[Candidate Shortlisted]
    G -->|Schedule Interview| H[Candidate Interview Scheduled]
    H -->|Complete Interview| I[Candidate Interview Completed]
    I -->|Select| J[Candidate Selected]
    J -->|Send Offer| K[Candidate Offer Sent]
    K -->|Accept| L[Candidate Hired]
    
    M[Employee Referral Submitted] -->|Approve| N[Employee Referral Approved]
    M -->|Reject| O[Employee Referral Rejected]
    N -->|Convert| P[Application Created]
    
    MA[Vendor Referral Submitted] -->|Approve| NA[Vendor Referral Approved]
    MA -->|Reject| OA[Vendor Referral Rejected]
    NA -->|Convert| P
    
    Q[Application Created] -->|Screen| R[Application Screened]
    R -->|Schedule Interview| S[Application Interview Scheduled]
    S -->|Complete Interview| T[Application Interview Completed]
    T -->|Make Offer| U[Application Offer Made]
    
    V[Interview Scheduled] -->|Complete| W[Interview Completed]
    V -->|Cancel| X[Interview Cancelled]
    X -->|Reschedule| V
    
    Y[Offer Created] -->|Approve| Z[Offer Approved]
    Z -->|Accept| AA[Offer Accepted]
    Z -->|Reject| BB[Offer Rejected]
    Y -->|Withdraw| CC[Offer Withdrawn]
```

***

## 🏗️ Domain Models

### **1. Job Posting Domain Model**

```csharp
[Table("JobPostings")]
[Index(nameof(Department), Name = "IX_JobPostings_Department")]
[Index(nameof(Status), Name = "IX_JobPostings_Status")]
[Index(nameof(CreatedDate), Name = "IX_JobPostings_CreatedDate")]
public partial class JobPosting : DomainModelBridge
{
    protected readonly ILogger<JobPosting> _logger;
    protected JobPosting() { }
    public JobPosting(ILogger<JobPosting> logger) { _logger = logger; }

    // Properties
    public JobPostingWorkflowState JobPostingState { get; protected set; }
    
    [Required(ErrorMessage = "Job title is required")]
    [StringLength(100, ErrorMessage = "Job title cannot exceed 100 characters")]
    public string JobTitle { get; protected set; }
    
    [Required(ErrorMessage = "Department is required")]
    [StringLength(50, ErrorMessage = "Department cannot exceed 50 characters")]
    public string Department { get; protected set; }
    
    [Required(ErrorMessage = "Job description is required")]
    [StringLength(2000, ErrorMessage = "Job description cannot exceed 2000 characters")]
    public string JobDescription { get; protected set; }
    
    [Required(ErrorMessage = "Requirements are required")]
    [StringLength(1000, ErrorMessage = "Requirements cannot exceed 1000 characters")]
    public string Requirements { get; protected set; }
    
    [Range(0, 1000000, ErrorMessage = "Salary range must be between 0 and 1,000,000")]
    public decimal MinSalary { get; protected set; }
    
    [Range(0, 1000000, ErrorMessage = "Salary range must be between 0 and 1,000,000")]
    public decimal MaxSalary { get; protected set; }
    
    [Required(ErrorMessage = "Location is required")]
    [StringLength(100, ErrorMessage = "Location cannot exceed 100 characters")]
    public string Location { get; protected set; }
    
    [Required(ErrorMessage = "Employment type is required")]
    [StringLength(20, ErrorMessage = "Employment type cannot exceed 20 characters")]
    public string EmploymentType { get; protected set; } // Full-time, Part-time, Contract
    
    public DateTimeOffset? ApplicationDeadline { get; protected set; }
    public int MaxApplications { get; protected set; } = 100;
    public int CurrentApplications { get; protected set; } = 0;
    
    // Navigation Properties
    public ICollection<Application> Applications { get; protected set; } = new List<Application>();
    public ICollection<Interview> Interviews { get; protected set; } = new List<Interview>();
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "JobPostingCreated")]
    public virtual JobPosting CreateJobPosting(CreateJobPostingCommand cmd)
    {
        if (cmd.Dto.MaxSalary <= cmd.Dto.MinSalary)
            throw new BusinessException("Maximum salary must be greater than minimum salary");
        
        if (cmd.Dto.ApplicationDeadline.HasValue && cmd.Dto.ApplicationDeadline.Value <= DateTimeOffset.Now)
            throw new BusinessException("Application deadline must be in the future");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.JobPostingState = new JobPostingDraft();
        
        _logger.LogInformation("Job posting created: {JobTitle} in {Department}", 
            cmd.Dto.JobTitle, cmd.Dto.Department);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "JobPostingPublished")]
    public virtual JobPosting PublishJobPosting(PublishJobPostingCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.JobPostingState.CanTransition(userRole, "Publish"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot publish job posting in current state");
        }

        if (this.JobPostingState.CanPublish)
        {
            this.JobPostingState = this.JobPostingState.Publish();
            _logger.LogInformation("Job posting published: {JobTitle}", this.JobTitle);
        }
        else
        {
            throw new BusinessException("Job posting cannot be published in current state");
        }
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "JobPostingClosed")]
    public virtual JobPosting CloseJobPosting(CloseJobPostingCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.JobPostingState.CanTransition(userRole, "Close"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot close job posting in current state");
        }

        if (this.JobPostingState.CanClose)
        {
            this.JobPostingState = this.JobPostingState.Close();
            _logger.LogInformation("Job posting closed: {JobTitle}", this.JobTitle);
        }
        else
        {
            throw new BusinessException("Job posting cannot be closed in current state");
        }
        
        return this;
    }
}
```

### **2. Candidate Domain Model**

```csharp
[Table("Candidates")]
[Index(nameof(Email), Name = "IX_Candidates_Email", IsUnique = true)]
[Index(nameof(Phone), Name = "IX_Candidates_Phone")]
public partial class Candidate : DomainModelBridge
{
    protected readonly ILogger<Candidate> _logger;
    protected Candidate() { }
    public Candidate(ILogger<Candidate> logger) { _logger = logger; }

    // Properties
    public CandidateApplicationState ApplicationState { get; protected set; }
    
    [Required(ErrorMessage = "First name is required")]
    [StringLength(50, ErrorMessage = "First name cannot exceed 50 characters")]
    public string FirstName { get; protected set; }
    
    [Required(ErrorMessage = "Last name is required")]
    [StringLength(50, ErrorMessage = "Last name cannot exceed 50 characters")]
    public string LastName { get; protected set; }
    
    [Required(ErrorMessage = "Email is required")]
    [StringLength(100, ErrorMessage = "Email cannot exceed 100 characters")]
    [EmailAddress(ErrorMessage = "Invalid email format")]
    public string Email { get; protected set; }
    
    [Required(ErrorMessage = "Phone is required")]
    [StringLength(20, ErrorMessage = "Phone cannot exceed 20 characters")]
    public string Phone { get; protected set; }
    
    [StringLength(200, ErrorMessage = "Address cannot exceed 200 characters")]
    public string Address { get; protected set; }
    
    [StringLength(100, ErrorMessage = "City cannot exceed 100 characters")]
    public string City { get; protected set; }
    
    [StringLength(50, ErrorMessage = "State cannot exceed 50 characters")]
    public string State { get; protected set; }
    
    [StringLength(20, ErrorMessage = "Postal code cannot exceed 20 characters")]
    public string PostalCode { get; protected set; }
    
    [StringLength(50, ErrorMessage = "Country cannot exceed 50 characters")]
    public string Country { get; protected set; }
    
    public DateTimeOffset? DateOfBirth { get; protected set; }
    public string Gender { get; protected set; }
    public string Nationality { get; protected set; }
    
    // Professional Information
    [StringLength(1000, ErrorMessage = "Summary cannot exceed 1000 characters")]
    public string ProfessionalSummary { get; protected set; }
    
    [StringLength(1000, ErrorMessage = "Skills cannot exceed 1000 characters")]
    public string Skills { get; protected set; }
    
    [StringLength(1000, ErrorMessage = "Experience cannot exceed 1000 characters")]
    public string Experience { get; protected set; }
    
    [StringLength(1000, ErrorMessage = "Education cannot exceed 1000 characters")]
    public string Education { get; protected set; }
    
    // Navigation Properties
    public ICollection<Application> Applications { get; protected set; } = new List<Application>();
    public ICollection<Interview> Interviews { get; protected set; } = new List<Interview>();
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "CandidateCreated")]
    public virtual Candidate CreateCandidate(CreateCandidateCommand cmd)
    {
        if (string.IsNullOrWhiteSpace(cmd.Dto.Email))
            throw new ValidationException("Email is required");
        
        if (string.IsNullOrWhiteSpace(cmd.Dto.Phone))
            throw new ValidationException("Phone is required");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.ApplicationState = new CandidateCreated();
        
        _logger.LogInformation("Candidate created: {Email}", cmd.Dto.Email);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "CandidateProfileUpdated")]
    public virtual Candidate UpdateProfile(UpdateCandidateProfileCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.ApplicationState.CanTransition(userRole, "UpdateProfile"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot update candidate profile in current state");
        }

        this.Convert(cmd.Dto);
        this.SetModified();
        
        _logger.LogInformation("Candidate profile updated: {Email}", this.Email);
        
        return this;
    }
}
```

### **3. Employee Referral Domain Model**

```csharp
[Table("EmployeeReferrals")]
[Index(nameof(EmployeeId), Name = "IX_EmployeeReferrals_EmployeeId")]
[Index(nameof(CandidateId), Name = "IX_EmployeeReferrals_CandidateId")]
[Index(nameof(Status), Name = "IX_EmployeeReferrals_Status")]
public partial class EmployeeReferral : DomainModelBridge
{
    protected readonly ILogger<EmployeeReferral> _logger;
    protected EmployeeReferral() { }
    public EmployeeReferral(ILogger<EmployeeReferral> logger) { _logger = logger; }

    // Properties
    public EmployeeReferralState ReferralState { get; protected set; }
    
    [Required(ErrorMessage = "Employee ID is required")]
    public string EmployeeId { get; protected set; }
    
    [Required(ErrorMessage = "Candidate ID is required")]
    public string CandidateId { get; protected set; }
    
    [Required(ErrorMessage = "Job posting ID is required")]
    public string JobPostingId { get; protected set; }
    
    [Required(ErrorMessage = "Referral reason is required")]
    [StringLength(1000, ErrorMessage = "Referral reason cannot exceed 1000 characters")]
    public string ReferralReason { get; protected set; }
    
    [StringLength(500, ErrorMessage = "Additional notes cannot exceed 500 characters")]
    public string AdditionalNotes { get; protected set; }
    
    public decimal? ReferralBonus { get; protected set; }
    public DateTimeOffset? BonusPaidDate { get; protected set; }
    
    // Navigation Properties
    public Employee Employee { get; protected set; }
    public Candidate Candidate { get; protected set; }
    public JobPosting JobPosting { get; protected set; }
    public ICollection<Application> Applications { get; protected set; } = new List<Application>();
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "EmployeeReferralSubmitted")]
    public virtual EmployeeReferral SubmitReferral(SubmitReferralCommand cmd)
    {
        if (cmd.Dto.EmployeeId <= 0)
            throw new ValidationException("Valid employee ID is required");
        
        if (cmd.Dto.CandidateId <= 0)
            throw new ValidationException("Valid candidate ID is required");
        
        if (cmd.Dto.JobPostingId <= 0)
            throw new ValidationException("Valid job posting ID is required");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.ReferralState = new EmployeeReferralSubmitted();
        
        _logger.LogInformation("Employee referral submitted: Employee {EmployeeId} for Candidate {CandidateId}", 
            cmd.Dto.EmployeeId, cmd.Dto.CandidateId);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "EmployeeReferralApproved")]
    public virtual EmployeeReferral ApproveReferral(ApproveReferralCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.ReferralState.CanTransition(userRole, "Approve"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot approve referral in current state");
        }

        if (this.ReferralState.CanApprove)
        {
            this.ReferralState = this.ReferralState.Approve();
            this.SetModified();
            
            _logger.LogInformation("Employee referral approved: {ReferralId} by {ApproverId}", 
                this.Id, cmd.Dto.ApproverId);
        }
        else
        {
            throw new BusinessException("Referral cannot be approved in current state");
        }
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "EmployeeReferralRejected")]
    public virtual EmployeeReferral RejectReferral(RejectReferralCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.ReferralState.CanTransition(userRole, "Reject"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot reject referral in current state");
        }

        if (this.ReferralState.CanReject)
        {
            this.ReferralState = this.ReferralState.Reject();
            this.SetModified();
            
            _logger.LogInformation("Employee referral rejected: {ReferralId} by {RejectorId}", 
                this.Id, cmd.Dto.RejectorId);
        }
        else
        {
            throw new BusinessException("Referral cannot be rejected in current state");
        }
        
        return this;
    }
}
```

### **4. Application Domain Model**

```csharp
[Table("Applications")]
[Index(nameof(JobPostingId), Name = "IX_Applications_JobPostingId")]
[Index(nameof(CandidateId), Name = "IX_Applications_CandidateId")]
[Index(nameof(Status), Name = "IX_Applications_Status")]
public partial class Application : DomainModelBridge
{
    protected readonly ILogger<Application> _logger;
    protected Application() { }
    public Application(ILogger<Application> logger) { _logger = logger; }

    // Properties
    public ApplicationWorkflowState ApplicationState { get; protected set; }
    
    [Required(ErrorMessage = "Job posting ID is required")]
    public string JobPostingId { get; protected set; }
    
    [Required(ErrorMessage = "Candidate ID is required")]
    public string CandidateId { get; protected set; }
    
    [Required(ErrorMessage = "Cover letter is required")]
    [StringLength(2000, ErrorMessage = "Cover letter cannot exceed 2000 characters")]
    public string CoverLetter { get; protected set; }
    
    [StringLength(500, ErrorMessage = "Additional notes cannot exceed 500 characters")]
    public string AdditionalNotes { get; protected set; }
    
    public decimal ExpectedSalary { get; protected set; }
    public DateTimeOffset? AvailableStartDate { get; protected set; }
    public string ReferralSource { get; protected set; }
    
    // Navigation Properties
    public JobPosting JobPosting { get; protected set; }
    public Candidate Candidate { get; protected set; }
    public ICollection<Interview> Interviews { get; protected set; } = new List<Interview>();
    public ICollection<Offer> Offers { get; protected set; } = new List<Offer>();
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "ApplicationCreated")]
    public virtual Application CreateApplication(CreateApplicationCommand cmd)
    {
        if (cmd.Dto.JobPostingId <= 0)
            throw new ValidationException("Valid job posting ID is required");
        
        if (cmd.Dto.CandidateId <= 0)
            throw new ValidationException("Valid candidate ID is required");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.ApplicationState = new ApplicationCreated();
        
        _logger.LogInformation("Application created: Candidate {CandidateId} for Job {JobPostingId}", 
            cmd.Dto.CandidateId, cmd.Dto.JobPostingId);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "ApplicationConvertedFromReferral")]
    public virtual Application ConvertFromReferral(ConvertFromReferralCommand cmd)
    {
        if (cmd.Dto.ReferralId <= 0)
            throw new ValidationException("Valid referral ID is required");
        
        if (cmd.Dto.JobPostingId <= 0)
            throw new ValidationException("Valid job posting ID is required");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.ApplicationState = new ApplicationCreated();
        
        _logger.LogInformation("Application converted from referral: Referral {ReferralId} for Job {JobPostingId}", 
            cmd.Dto.ReferralId, cmd.Dto.JobPostingId);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "ApplicationScreened")]
    public virtual Application ScreenApplication(ScreenApplicationCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.ApplicationState.CanTransition(userRole, "Screen"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot screen application in current state");
        }

        if (this.ApplicationState.CanScreen)
        {
            this.ApplicationState = this.ApplicationState.Screen(cmd.Dto.IsApproved);
            _logger.LogInformation("Application screened: {ApplicationId} - {Result}", 
                this.Id, cmd.Dto.IsApproved ? "Approved" : "Rejected");
        }
        else
        {
            throw new BusinessException("Application cannot be screened in current state");
        }
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "ApplicationInterviewScheduled")]
    public virtual Application ScheduleInterview(ScheduleInterviewCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.ApplicationState.CanTransition(userRole, "ScheduleInterview"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot schedule interview in current state");
        }

        if (this.ApplicationState.CanScheduleInterview)
        {
            this.ApplicationState = this.ApplicationState.ScheduleInterview();
            _logger.LogInformation("Interview scheduled for application: {ApplicationId}", this.Id);
        }
        else
        {
            throw new BusinessException("Interview cannot be scheduled in current state");
        }
        
        return this;
    }
}
```

### **4. Interview Domain Model**

```csharp
[Table("Interviews")]
[Index(nameof(ApplicationId), Name = "IX_Interviews_ApplicationId")]
[Index(nameof(InterviewDate), Name = "IX_Interviews_InterviewDate")]
[Index(nameof(Status), Name = "IX_Interviews_Status")]
public partial class Interview : DomainModelBridge
{
    protected readonly ILogger<Interview> _logger;
    protected Interview() { }
    public Interview(ILogger<Interview> logger) { _logger = logger; }

    // Properties
    public InterviewWorkflowState InterviewState { get; protected set; }
    
    [Required(ErrorMessage = "Application ID is required")]
    public string ApplicationId { get; protected set; }
    
    [Required(ErrorMessage = "Interviewer ID is required")]
    public string InterviewerId { get; protected set; }
    
    [Required(ErrorMessage = "Interview type is required")]
    [StringLength(50, ErrorMessage = "Interview type cannot exceed 50 characters")]
    public string InterviewType { get; protected set; } // Phone, Technical, Final, HR
    
    [Required(ErrorMessage = "Interview date is required")]
    public DateTimeOffset InterviewDate { get; protected set; }
    
    [StringLength(200, ErrorMessage = "Location cannot exceed 200 characters")]
    public string Location { get; protected set; }
    
    [StringLength(500, ErrorMessage = "Notes cannot exceed 500 characters")]
    public string Notes { get; protected set; }
    
    [StringLength(1000, ErrorMessage = "Feedback cannot exceed 1000 characters")]
    public string Feedback { get; protected set; }
    
    [Range(1, 10, ErrorMessage = "Rating must be between 1 and 10")]
    public int? Rating { get; protected set; }
    
    public bool? IsRecommended { get; protected set; }
    
    // Navigation Properties
    public Application Application { get; protected set; }
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "InterviewScheduled")]
    public virtual Interview ScheduleInterview(ScheduleInterviewCommand cmd)
    {
        if (cmd.Dto.InterviewDate <= DateTimeOffset.Now)
            throw new ValidationException("Interview date must be in the future");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.InterviewState = new InterviewScheduled();
        
        _logger.LogInformation("Interview scheduled: {InterviewType} on {InterviewDate}", 
            cmd.Dto.InterviewType, cmd.Dto.InterviewDate);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "InterviewCompleted")]
    public virtual Interview CompleteInterview(CompleteInterviewCommand cmd)
    {
        // Role-based validation - only assigned interviewer can complete
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        var userId = cmd.Dto.GetAppContext()?.UserId ?? "0";
        
        if (!this.InterviewState.CanTransition(userRole, "Complete", this.InterviewerId.ToString(), userId))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot complete interview in current state");
        }

        if (this.InterviewState.CanComplete)
        {
            this.Convert(cmd.Dto);
            this.InterviewState = this.InterviewState.Complete();
            this.SetModified();
            
            _logger.LogInformation("Interview completed: {InterviewId} - Rating: {Rating}", 
                this.Id, cmd.Dto.Rating);
        }
        else
        {
            throw new BusinessException("Interview cannot be completed in current state");
        }
        
        return this;
    }
}
```

### **5. Offer Domain Model**

```csharp
[Table("Offers")]
[Index(nameof(ApplicationId), Name = "IX_Offers_ApplicationId")]
[Index(nameof(Status), Name = "IX_Offers_Status")]
public partial class Offer : DomainModelBridge
{
    protected readonly ILogger<Offer> _logger;
    protected Offer() { }
    public Offer(ILogger<Offer> logger) { _logger = logger; }

    // Properties
    public OfferWorkflowState OfferState { get; protected set; }
    
    [Required(ErrorMessage = "Application ID is required")]
    public string ApplicationId { get; protected set; }
    
    [Required(ErrorMessage = "Salary is required")]
    [Range(0, 1000000, ErrorMessage = "Salary must be between 0 and 1,000,000")]
    public decimal Salary { get; protected set; }
    
    [StringLength(50, ErrorMessage = "Currency cannot exceed 50 characters")]
    public string Currency { get; protected set; } = "USD";
    
    [StringLength(100, ErrorMessage = "Position cannot exceed 100 characters")]
    public string Position { get; protected set; }
    
    [StringLength(100, ErrorMessage = "Department cannot exceed 100 characters")]
    public string Department { get; protected set; }
    
    [StringLength(100, ErrorMessage = "Location cannot exceed 100 characters")]
    public string Location { get; protected set; }
    
    public DateTimeOffset? StartDate { get; protected set; }
    public DateTimeOffset? ExpiryDate { get; protected set; }
    
    [StringLength(1000, ErrorMessage = "Terms cannot exceed 1000 characters")]
    public string Terms { get; protected set; }
    
    [StringLength(500, ErrorMessage = "Notes cannot exceed 500 characters")]
    public string Notes { get; protected set; }
    
    // Navigation Properties
    public Application Application { get; protected set; }
    
    // Domain Methods
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "OfferCreated")]
    public virtual Offer CreateOffer(CreateOfferCommand cmd)
    {
        if (cmd.Dto.Salary <= 0)
            throw new ValidationException("Salary must be greater than zero");
        
        if (cmd.Dto.StartDate.HasValue && cmd.Dto.StartDate.Value <= DateTimeOffset.Now)
            throw new ValidationException("Start date must be in the future");
        
        this.Convert(cmd.Dto);
        this.SetAdded(cmd.Dto.GetGeneratedId());
        this.OfferState = new OfferCreated();
        
        _logger.LogInformation("Offer created: {Position} - ${Salary}", 
            cmd.Dto.Position, cmd.Dto.Salary);
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "OfferApproved")]
    public virtual Offer ApproveOffer(ApproveOfferCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.OfferState.CanTransition(userRole, "Approve"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot approve offer in current state");
        }

        if (this.OfferState.CanApprove)
        {
            this.OfferState = this.OfferState.Approve();
            this.SetModified();
            
            _logger.LogInformation("Offer approved: {OfferId} by {ApproverId}", 
                this.Id, cmd.Dto.ApproverId);
        }
        else
        {
            throw new BusinessException("Offer cannot be approved in current state");
        }
        
        return this;
    }
    
    [BusinessMethod]
    [ValidationRequired]
    [EventTrigger(EventType = "OfferAccepted")]
    public virtual Offer AcceptOffer(AcceptOfferCommand cmd)
    {
        // Role-based validation
        var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
        
        if (!this.OfferState.CanTransition(userRole, "Accept"))
        {
            throw new UnauthorizedAccessException($"User role '{userRole}' cannot accept offer in current state");
        }

        if (this.OfferState.CanAccept)
        {
            this.OfferState = this.OfferState.Accept();
            this.SetModified();
            
            _logger.LogInformation("Offer accepted: {OfferId} by candidate", this.Id);
        }
        else
        {
            throw new BusinessException("Offer cannot be accepted in current state");
        }
        
        return this;
    }
}
```

***

## 🔄 Enhanced Workflow State Machines with Role-Based Access Control

### **1. Job Posting Workflow States with Role Validation**

```csharp
// Base Job Posting Workflow State with Role-Based Access Control
public class JobPostingWorkflowState : FlexState
{
    public virtual string FriendlyName => "";
    public virtual JobPostingWorkflowState Publish() => this;
    public virtual JobPostingWorkflowState Close() => this;
    public virtual JobPostingWorkflowState Archive() => this;
    public virtual bool CanPublish { get; }
    public virtual bool CanClose { get; }
    public virtual bool CanArchive { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action)
    {
        return GetAllowedRoles(action).Contains(userRole);
    }
    
    protected virtual string[] GetAllowedRoles(string action)
    {
        return new string[0]; // Override in specific states
    }
}

// Draft State - Only Manager and TeamLead can publish/close
public class JobPostingDraft : JobPostingWorkflowState
{
    public JobPostingDraft() { this.SetAdded(); }
    public override string FriendlyName => "Draft";
    public override JobPostingWorkflowState Publish() => new JobPostingPublished();
    public override JobPostingWorkflowState Close() => new JobPostingClosed();
    public override bool CanPublish { get; } = true;
    public override bool CanClose { get; } = true;
    public override bool CanArchive { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Publish" => new[] { "Manager", "TeamLead", "HR" },
            "Close" => new[] { "Manager", "TeamLead", "HR" },
            "Archive" => new string[0], // Not allowed
            _ => new string[0]
        };
    }
}

// Published State - Only HR and Admin can close/archive
public class JobPostingPublished : JobPostingWorkflowState
{
    public JobPostingPublished() { this.SetAdded(); }
    public override string FriendlyName => "Published";
    public override JobPostingWorkflowState Close() => new JobPostingClosed();
    public override JobPostingWorkflowState Archive() => new JobPostingArchived();
    public override bool CanPublish { get; } = false;
    public override bool CanClose { get; } = true;
    public override bool CanArchive { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Publish" => new string[0], // Not allowed
            "Close" => new[] { "HR", "Admin", "RecruitmentManager" },
            "Archive" => new[] { "HR", "Admin", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Closed State - Only HR and Admin can archive
public class JobPostingClosed : JobPostingWorkflowState
{
    public JobPostingClosed() { this.SetAdded(); }
    public override string FriendlyName => "Closed";
    public override JobPostingWorkflowState Archive() => new JobPostingArchived();
    public override bool CanPublish { get; } = false;
    public override bool CanClose { get; } = false;
    public override bool CanArchive { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Publish" => new string[0], // Not allowed
            "Close" => new string[0], // Not allowed
            "Archive" => new[] { "HR", "Admin", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Archived State - No transitions allowed
public class JobPostingArchived : JobPostingWorkflowState
{
    public JobPostingArchived() { this.SetAdded(); }
    public override string FriendlyName => "Archived";
    public override bool CanPublish { get; } = false;
    public override bool CanClose { get; } = false;
    public override bool CanArchive { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}
```

### **2. Employee Referral Workflow States with Role Validation**

```csharp
// Base Employee Referral Workflow State with Role-Based Access Control
public class EmployeeReferralState : FlexState
{
    public virtual string FriendlyName => "";
    public virtual EmployeeReferralState Approve() => this;
    public virtual EmployeeReferralState Reject() => this;
    public virtual EmployeeReferralState ConvertToApplication() => this;
    public virtual bool CanApprove { get; }
    public virtual bool CanReject { get; }
    public virtual bool CanConvertToApplication { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action)
    {
        return GetAllowedRoles(action).Contains(userRole);
    }
    
    protected virtual string[] GetAllowedRoles(string action)
    {
        return new string[0]; // Override in specific states
    }
}

// Submitted State - Only HR can approve/reject
public class EmployeeReferralSubmitted : EmployeeReferralState
{
    public EmployeeReferralSubmitted() { this.SetAdded(); }
    public override string FriendlyName => "Submitted";
    public override EmployeeReferralState Approve() => new EmployeeReferralApproved();
    public override EmployeeReferralState Reject() => new EmployeeReferralRejected();
    public override EmployeeReferralState ConvertToApplication() => new EmployeeReferralConvertedToApplication();
    public override bool CanApprove { get; } = true;
    public override bool CanReject { get; } = true;
    public override bool CanConvertToApplication { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Approve" => new[] { "HR", "RecruitmentManager" },
            "Reject" => new[] { "HR", "RecruitmentManager" },
            "ConvertToApplication" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Approved State - Can be converted to application
public class EmployeeReferralApproved : EmployeeReferralState
{
    public EmployeeReferralApproved() { this.SetAdded(); }
    public override string FriendlyName => "Approved";
    public override EmployeeReferralState ConvertToApplication() => new EmployeeReferralConvertedToApplication();
    public override bool CanApprove { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanConvertToApplication { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Approve" => new string[0], // Not allowed
            "Reject" => new string[0], // Not allowed
            "ConvertToApplication" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Rejected State - No transitions allowed
public class EmployeeReferralRejected : EmployeeReferralState
{
    public EmployeeReferralRejected() { this.SetAdded(); }
    public override string FriendlyName => "Rejected";
    public override bool CanApprove { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanConvertToApplication { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}

// Converted to Application State - No transitions allowed
public class EmployeeReferralConvertedToApplication : EmployeeReferralState
{
    public EmployeeReferralConvertedToApplication() { this.SetAdded(); }
    public override string FriendlyName => "Converted to Application";
    public override bool CanApprove { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanConvertToApplication { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}
```

### **3. Application Workflow States with Role Validation**

```csharp
// Base Application Workflow State with Role-Based Access Control
public class ApplicationWorkflowState : FlexState
{
    public virtual string FriendlyName => "";
    public virtual ApplicationWorkflowState Screen(bool isApproved) => this;
    public virtual ApplicationWorkflowState ScheduleInterview() => this;
    public virtual ApplicationWorkflowState CompleteInterview() => this;
    public virtual ApplicationWorkflowState MakeOffer() => this;
    public virtual ApplicationWorkflowState Reject() => this;
    public virtual bool CanScreen { get; }
    public virtual bool CanScheduleInterview { get; }
    public virtual bool CanCompleteInterview { get; }
    public virtual bool CanMakeOffer { get; }
    public virtual bool CanReject { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action)
    {
        return GetAllowedRoles(action).Contains(userRole);
    }
    
    protected virtual string[] GetAllowedRoles(string action)
    {
        return new string[0]; // Override in specific states
    }
}

// Submitted State - Only HR can screen
public class ApplicationSubmitted : ApplicationWorkflowState
{
    public ApplicationSubmitted() { this.SetAdded(); }
    public override string FriendlyName => "Submitted";
    public override ApplicationWorkflowState Screen(bool isApproved) => 
        isApproved ? new ApplicationScreened() : new ApplicationRejected();
    public override bool CanScreen { get; } = true;
    public override bool CanScheduleInterview { get; } = false;
    public override bool CanCompleteInterview { get; } = false;
    public override bool CanMakeOffer { get; } = false;
    public override bool CanReject { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Screen" => new[] { "HR", "RecruitmentManager" },
            "ScheduleInterview" => new string[0], // Not allowed
            "CompleteInterview" => new string[0], // Not allowed
            "MakeOffer" => new string[0], // Not allowed
            "Reject" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Screened State - Only HR can schedule interview
public class ApplicationScreened : ApplicationWorkflowState
{
    public ApplicationScreened() { this.SetAdded(); }
    public override string FriendlyName => "Screened";
    public override ApplicationWorkflowState ScheduleInterview() => new ApplicationInterviewScheduled();
    public override ApplicationWorkflowState Reject() => new ApplicationRejected();
    public override bool CanScreen { get; } = false;
    public override bool CanScheduleInterview { get; } = true;
    public override bool CanCompleteInterview { get; } = false;
    public override bool CanMakeOffer { get; } = false;
    public override bool CanReject { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Screen" => new string[0], // Not allowed
            "ScheduleInterview" => new[] { "HR", "RecruitmentManager" },
            "CompleteInterview" => new string[0], // Not allowed
            "MakeOffer" => new string[0], // Not allowed
            "Reject" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Interview Scheduled State - Only HR can complete interview
public class ApplicationInterviewScheduled : ApplicationWorkflowState
{
    public ApplicationInterviewScheduled() { this.SetAdded(); }
    public override string FriendlyName => "Interview Scheduled";
    public override ApplicationWorkflowState CompleteInterview() => new ApplicationInterviewCompleted();
    public override ApplicationWorkflowState Reject() => new ApplicationRejected();
    public override bool CanScreen { get; } = false;
    public override bool CanScheduleInterview { get; } = false;
    public override bool CanCompleteInterview { get; } = true;
    public override bool CanMakeOffer { get; } = false;
    public override bool CanReject { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Screen" => new string[0], // Not allowed
            "ScheduleInterview" => new string[0], // Not allowed
            "CompleteInterview" => new[] { "HR", "RecruitmentManager", "Interviewer" },
            "MakeOffer" => new string[0], // Not allowed
            "Reject" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Interview Completed State - Only HR can make offer
public class ApplicationInterviewCompleted : ApplicationWorkflowState
{
    public ApplicationInterviewCompleted() { this.SetAdded(); }
    public override string FriendlyName => "Interview Completed";
    public override ApplicationWorkflowState MakeOffer() => new ApplicationOfferMade();
    public override ApplicationWorkflowState Reject() => new ApplicationRejected();
    public override bool CanScreen { get; } = false;
    public override bool CanScheduleInterview { get; } = false;
    public override bool CanCompleteInterview { get; } = false;
    public override bool CanMakeOffer { get; } = true;
    public override bool CanReject { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Screen" => new string[0], // Not allowed
            "ScheduleInterview" => new string[0], // Not allowed
            "CompleteInterview" => new string[0], // Not allowed
            "MakeOffer" => new[] { "HR", "RecruitmentManager" },
            "Reject" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Offer Made State - Only HR can reject
public class ApplicationOfferMade : ApplicationWorkflowState
{
    public ApplicationOfferMade() { this.SetAdded(); }
    public override string FriendlyName => "Offer Made";
    public override ApplicationWorkflowState Reject() => new ApplicationRejected();
    public override bool CanScreen { get; } = false;
    public override bool CanScheduleInterview { get; } = false;
    public override bool CanCompleteInterview { get; } = false;
    public override bool CanMakeOffer { get; } = false;
    public override bool CanReject { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Screen" => new string[0], // Not allowed
            "ScheduleInterview" => new string[0], // Not allowed
            "CompleteInterview" => new string[0], // Not allowed
            "MakeOffer" => new string[0], // Not allowed
            "Reject" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Rejected State - No transitions allowed
public class ApplicationRejected : ApplicationWorkflowState
{
    public ApplicationRejected() { this.SetAdded(); }
    public override string FriendlyName => "Rejected";
    public override bool CanScreen { get; } = false;
    public override bool CanScheduleInterview { get; } = false;
    public override bool CanCompleteInterview { get; } = false;
    public override bool CanMakeOffer { get; } = false;
    public override bool CanReject { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}
```

### **4. Interview Workflow States with Role Validation**

```csharp
// Base Interview Workflow State with Role-Based Access Control
public class InterviewWorkflowState : FlexState
{
    public virtual string FriendlyName => "";
    public virtual InterviewWorkflowState Complete() => this;
    public virtual InterviewWorkflowState Reschedule() => this;
    public virtual InterviewWorkflowState Cancel() => this;
    public virtual bool CanComplete { get; }
    public virtual bool CanReschedule { get; }
    public virtual bool CanCancel { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action, string interviewerId = null, string currentUserId = null)
    {
        return userRole switch
        {
            "HR" => CanHRTransition(action),
            "RecruitmentManager" => CanRecruitmentManagerTransition(action),
            "Interviewer" => CanInterviewerTransition(action, interviewerId, currentUserId),
            "Admin" => true, // Admin can do everything
            _ => false
        };
    }
    
    protected virtual bool CanHRTransition(string action) => action == "Schedule" || action == "Cancel" || action == "Reschedule";
    protected virtual bool CanRecruitmentManagerTransition(string action) => true; // Same as HR
    protected virtual bool CanInterviewerTransition(string action, string interviewerId, string currentUserId) 
    {
        // Interviewer can only perform actions on their assigned interviews
        if (interviewerId != currentUserId) return false;
        
        return action switch
        {
            "Complete" => CanComplete,
            "Reschedule" => CanReschedule,
            "Cancel" => CanCancel,
            _ => false
        };
    }
}

// Scheduled State - Only assigned interviewer can complete
public class InterviewScheduled : InterviewWorkflowState
{
    public InterviewScheduled() { this.SetAdded(); }
    public override string FriendlyName => "Scheduled";
    public override InterviewWorkflowState Complete() => new InterviewCompleted();
    public override InterviewWorkflowState Reschedule() => new InterviewScheduled();
    public override InterviewWorkflowState Cancel() => new InterviewCancelled();
    public override bool CanComplete { get; } = true;
    public override bool CanReschedule { get; } = true;
    public override bool CanCancel { get; } = true;
}

// Completed State - No transitions allowed
public class InterviewCompleted : InterviewWorkflowState
{
    public InterviewCompleted() { this.SetAdded(); }
    public override string FriendlyName => "Completed";
    public override bool CanComplete { get; } = false;
    public override bool CanReschedule { get; } = false;
    public override bool CanCancel { get; } = false;
}

// Cancelled State - Only HR can reschedule
public class InterviewCancelled : InterviewWorkflowState
{
    public InterviewCancelled() { this.SetAdded(); }
    public override string FriendlyName => "Cancelled";
    public override InterviewWorkflowState Reschedule() => new InterviewScheduled();
    public override bool CanComplete { get; } = false;
    public override bool CanReschedule { get; } = true;
    public override bool CanCancel { get; } = false;
}
```

### **5. Offer Workflow States with Role Validation**

```csharp
// Base Offer Workflow State with Role-Based Access Control
public class OfferWorkflowState : FlexState
{
    public virtual string FriendlyName => "";
    public virtual OfferWorkflowState Approve() => this;
    public virtual OfferWorkflowState Accept() => this;
    public virtual OfferWorkflowState Reject() => this;
    public virtual OfferWorkflowState Withdraw() => this;
    public virtual bool CanApprove { get; }
    public virtual bool CanAccept { get; }
    public virtual bool CanReject { get; }
    public virtual bool CanWithdraw { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action)
    {
        return GetAllowedRoles(action).Contains(userRole);
    }
    
    protected virtual string[] GetAllowedRoles(string action)
    {
        return new string[0]; // Override in specific states
    }
}

// Created State - Only HR and Finance can approve
public class OfferCreated : OfferWorkflowState
{
    public OfferCreated() { this.SetAdded(); }
    public override string FriendlyName => "Created";
    public override OfferWorkflowState Approve() => new OfferApproved();
    public override OfferWorkflowState Withdraw() => new OfferWithdrawn();
    public override bool CanApprove { get; } = true;
    public override bool CanAccept { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanWithdraw { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Approve" => new[] { "HR", "Finance", "Admin" },
            "Accept" => new string[0], // Not allowed
            "Reject" => new string[0], // Not allowed
            "Withdraw" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Approved State - Only candidate can accept/reject
public class OfferApproved : OfferWorkflowState
{
    public OfferApproved() { this.SetAdded(); }
    public override string FriendlyName => "Approved";
    public override OfferWorkflowState Accept() => new OfferAccepted();
    public override OfferWorkflowState Reject() => new OfferRejected();
    public override OfferWorkflowState Withdraw() => new OfferWithdrawn();
    public override bool CanApprove { get; } = false;
    public override bool CanAccept { get; } = true;
    public override bool CanReject { get; } = true;
    public override bool CanWithdraw { get; } = true;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return action switch
        {
            "Approve" => new string[0], // Not allowed
            "Accept" => new[] { "Candidate" },
            "Reject" => new[] { "Candidate" },
            "Withdraw" => new[] { "HR", "RecruitmentManager" },
            _ => new string[0]
        };
    }
}

// Accepted State - No transitions allowed
public class OfferAccepted : OfferWorkflowState
{
    public OfferAccepted() { this.SetAdded(); }
    public override string FriendlyName => "Accepted";
    public override bool CanApprove { get; } = false;
    public override bool CanAccept { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanWithdraw { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}

// Rejected State - No transitions allowed
public class OfferRejected : OfferWorkflowState
{
    public OfferRejected() { this.SetAdded(); }
    public override string FriendlyName => "Rejected";
    public override bool CanApprove { get; } = false;
    public override bool CanAccept { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanWithdraw { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}

// Withdrawn State - No transitions allowed
public class OfferWithdrawn : OfferWorkflowState
{
    public OfferWithdrawn() { this.SetAdded(); }
    public override string FriendlyName => "Withdrawn";
    public override bool CanApprove { get; } = false;
    public override bool CanAccept { get; } = false;
    public override bool CanReject { get; } = false;
    public override bool CanWithdraw { get; } = false;

    // Role-based transition validation
    protected override string[] GetAllowedRoles(string action)
    {
        return new string[0]; // No transitions allowed
    }
}
```

***

## 🔄 Dynamic Workflow Configuration

### **1. Workflow Configuration System**

```csharp
// Workflow Configuration for Dynamic State Transitions
public class WorkflowConfiguration
{
    public string WorkflowName { get; set; }
    public List<StateTransitionRule> TransitionRules { get; set; } = new List<StateTransitionRule>();
    public List<WorkflowCondition> Conditions { get; set; } = new List<WorkflowCondition>();
}

public class StateTransitionRule
{
    public string FromState { get; set; }
    public string Action { get; set; }
    public string ToState { get; set; }
    public string[] AllowedRoles { get; set; }
    public string Condition { get; set; } // Optional condition for transition
    public bool IsAutomatic { get; set; } // For automatic transitions
}

public class WorkflowCondition
{
    public string Name { get; set; }
    public string Expression { get; set; }
    public string Description { get; set; }
}
```

### **2. Enhanced State Machine with Workflow Engine**

```csharp
public class JobPostingWorkflowState : FlexState
{
    protected static WorkflowConfiguration _workflowConfig;
    
    public virtual string FriendlyName => "";
    public virtual JobPostingWorkflowState Publish() => this;
    public virtual JobPostingWorkflowState Close() => this;
    public virtual JobPostingWorkflowState Archive() => this;
    public virtual bool CanPublish { get; }
    public virtual bool CanClose { get; }
    public virtual bool CanArchive { get; }
    
    // Role-based access control
    public virtual bool CanTransition(string userRole, string action)
    {
        return GetAllowedRoles(action).Contains(userRole);
    }
    
    protected virtual string[] GetAllowedRoles(string action)
    {
        return new string[0]; // Override in specific states
    }
    
    // Workflow Engine Integration
    public virtual JobPostingWorkflowState GetNextState(string action, Dictionary<string, object> context = null)
    {
        var currentStateName = this.GetType().Name;
        var rule = _workflowConfig?.TransitionRules
            .FirstOrDefault(r => r.FromState == currentStateName && r.Action == action);
            
        if (rule == null)
        {
            return this; // No transition defined
        }
        
        // Check conditions if specified
        if (!string.IsNullOrEmpty(rule.Condition) && context != null)
        {
            if (!EvaluateCondition(rule.Condition, context))
            {
                return this; // Condition not met
            }
        }
        
        // Return next state based on configuration
        return CreateStateFromName(rule.ToState);
    }
    
    protected virtual bool EvaluateCondition(string condition, Dictionary<string, object> context)
    {
        // Simple condition evaluation - can be enhanced with expression engine
        return condition switch
        {
            "Priority == 'High'" => context.ContainsKey("Priority") && 
                context["Priority"].ToString() == "High",
            "Department == 'IT'" => context.ContainsKey("Department") && 
                context["Department"].ToString() == "IT",
            "MinSalary > 100000" => context.ContainsKey("MinSalary") && 
                decimal.TryParse(context["MinSalary"].ToString(), out var salary) && salary > 100000,
            _ => true
        };
    }
    
    protected virtual JobPostingWorkflowState CreateStateFromName(string stateName)
    {
        return stateName switch
        {
            "JobPostingDraft" => new JobPostingDraft(),
            "JobPostingPublished" => new JobPostingPublished(),
            "JobPostingClosed" => new JobPostingClosed(),
            "JobPostingArchived" => new JobPostingArchived(),
            _ => this
        };
    }
}
```

### **3. Configure Job Posting Workflow**

```csharp
// Configure Job Posting Workflow
public static void ConfigureJobPostingWorkflow()
{
    _workflowConfig = new WorkflowConfiguration
    {
        WorkflowName = "JobPostingProcessing",
        TransitionRules = new List<StateTransitionRule>
        {
            // Standard transitions
            new StateTransitionRule
            {
                FromState = "JobPostingDraft",
                Action = "Publish",
                ToState = "JobPostingPublished",
                AllowedRoles = new[] { "Manager", "TeamLead", "HR" }
            },
            new StateTransitionRule
            {
                FromState = "JobPostingDraft",
                Action = "Close",
                ToState = "JobPostingClosed",
                AllowedRoles = new[] { "Manager", "TeamLead", "HR" }
            },
            new StateTransitionRule
            {
                FromState = "JobPostingPublished",
                Action = "Close",
                ToState = "JobPostingClosed",
                AllowedRoles = new[] { "HR", "Admin", "RecruitmentManager" }
            },
            new StateTransitionRule
            {
                FromState = "JobPostingPublished",
                Action = "Archive",
                ToState = "JobPostingArchived",
                AllowedRoles = new[] { "HR", "Admin", "RecruitmentManager" }
            },
            new StateTransitionRule
            {
                FromState = "JobPostingClosed",
                Action = "Archive",
                ToState = "JobPostingArchived",
                AllowedRoles = new[] { "HR", "Admin", "RecruitmentManager" }
            },
            
            // Conditional transitions
            new StateTransitionRule
            {
                FromState = "JobPostingDraft",
                Action = "AutoPublish",
                ToState = "JobPostingPublished",
                AllowedRoles = new[] { "System" },
                Condition = "Priority == 'High' && Department == 'IT'",
                IsAutomatic = true
            },
            
            // Workflow-specific transitions
            new StateTransitionRule
            {
                FromState = "JobPostingPublished",
                Action = "Reopen",
                ToState = "JobPostingDraft",
                AllowedRoles = new[] { "HR", "Admin" }
            }
        },
        Conditions = new List<WorkflowCondition>
        {
            new WorkflowCondition
            {
                Name = "Priority == 'High'",
                Expression = "Priority == 'High'",
                Description = "Job posting has high priority"
            },
            new WorkflowCondition
            {
                Name = "Department == 'IT'",
                Expression = "Department == 'IT'",
                Description = "Job posting is for IT department"
            },
            new WorkflowCondition
            {
                Name = "MinSalary > 100000",
                Expression = "MinSalary > 100000",
                Description = "Job posting has high salary range"
            }
        }
    };
}
```

### **4. Enhanced Domain Methods with Workflow Engine**

```csharp
// Enhanced Domain Method with Workflow Engine Integration
public virtual JobPosting PublishJobPosting(PublishJobPostingCommand cmd)
{
    // Role-based validation
    var userRole = cmd.Dto.GetAppContext()?.UserRole ?? "Guest";
    
    if (!this.JobPostingState.CanTransition(userRole, "Publish"))
    {
        throw new UnauthorizedAccessException($"User role '{userRole}' cannot publish job posting in current state");
    }
    
    // State validation
    if (!this.JobPostingState.CanPublish)
    {
        throw new InvalidOperationException("Job posting cannot be published in current state");
    }
    
    // Use workflow engine for state transition
    var context = new Dictionary<string, object>
    {
        ["Priority"] = this.Priority,
        ["Department"] = this.Department,
        ["MinSalary"] = this.MinSalary
    };
    
    this.JobPostingState = this.JobPostingState.GetNextState("Publish", context);
    this.SetModified();
    
    return this;
}

public virtual JobPosting ProcessWorkflowAction(string action, Dictionary<string, object> context = null)
{
    // Generic workflow action processing
    var userRole = context?["UserRole"]?.ToString() ?? "Guest";
    
    if (!this.JobPostingState.CanTransition(userRole, action))
    {
        throw new UnauthorizedAccessException($"User role '{userRole}' cannot perform '{action}' in current state");
    }
    
    // Get next state from workflow configuration
    var nextState = this.JobPostingState.GetNextState(action, context);
    
    if (nextState != this.JobPostingState) // State transition occurred
    {
        var previousState = this.JobPostingState;
        this.JobPostingState = nextState;
        
        // Publish state change event
        this.RaiseEvent(new JobPostingStateChangedEvent
        {
            JobPostingId = this.Id,
            PreviousState = previousState.GetType().Name,
            NewState = this.JobPostingState.GetType().Name,
            Action = action,
            ChangedAt = DateTime.UtcNow,
            Context = context
        });
    }
    
    return this;
}
```

***

## 🔐 Approval Process Implementation

### **1. Approval Configuration**

```csharp
// Approval Process Configuration
public class ApprovalProcessConfiguration
{
    public string ProcessName { get; set; }
    public List<ApprovalStep> Steps { get; set; } = new List<ApprovalStep>();
    public ApprovalPolicy Policy { get; set; }
}

public class ApprovalStep
{
    public int StepNumber { get; set; }
    public string StepName { get; set; }
    public List<string> RequiredRoles { get; set; } = new List<string>();
    public List<string> RequiredPermissions { get; set; } = new List<string>();
    public bool IsMandatory { get; set; } = true;
    public int TimeoutHours { get; set; } = 72; // 3 days default
    public string EscalationRole { get; set; }
}

public class ApprovalPolicy
{
    public bool RequireAllSteps { get; set; } = true;
    public bool AllowParallelApproval { get; set; } = false;
    public bool AllowDelegation { get; set; } = true;
    public bool RequireJustification { get; set; } = true;
    public int MaxApprovalDays { get; set; } = 30;
}

// Recruitment Approval Process
public static class RecruitmentApprovalProcess
{
    public static ApprovalProcessConfiguration GetJobPostingApprovalProcess()
    {
        return new ApprovalProcessConfiguration
        {
            ProcessName = "Job Posting Approval",
            Steps = new List<ApprovalStep>
            {
                new ApprovalStep
                {
                    StepNumber = 1,
                    StepName = "HR Review",
                    RequiredRoles = new List<string> { "HR", "HR_Manager" },
                    RequiredPermissions = new List<string> { "JobPosting.Review" },
                    IsMandatory = true,
                    TimeoutHours = 24
                },
                new ApprovalStep
                {
                    StepNumber = 2,
                    StepName = "Department Manager Review",
                    RequiredRoles = new List<string> { "Department_Manager", "Team_Lead" },
                    RequiredPermissions = new List<string> { "JobPosting.Approve" },
                    IsMandatory = true,
                    TimeoutHours = 48
                },
                new ApprovalStep
                {
                    StepNumber = 3,
                    StepName = "Director Approval",
                    RequiredRoles = new List<string> { "Director", "VP" },
                    RequiredPermissions = new List<string> { "JobPosting.FinalApprove" },
                    IsMandatory = true,
                    TimeoutHours = 72
                }
            },
            Policy = new ApprovalPolicy
            {
                RequireAllSteps = true,
                AllowParallelApproval = false,
                AllowDelegation = true,
                RequireJustification = true,
                MaxApprovalDays = 7
            }
        };
    }
    
    public static ApprovalProcessConfiguration GetOfferApprovalProcess()
    {
        return new ApprovalProcessConfiguration
        {
            ProcessName = "Offer Approval",
            Steps = new List<ApprovalStep>
            {
                new ApprovalStep
                {
                    StepNumber = 1,
                    StepName = "HR Review",
                    RequiredRoles = new List<string> { "HR", "HR_Manager" },
                    RequiredPermissions = new List<string> { "Offer.Review" },
                    IsMandatory = true,
                    TimeoutHours = 24
                },
                new ApprovalStep
                {
                    StepNumber = 2,
                    StepName = "Finance Review",
                    RequiredRoles = new List<string> { "Finance", "Finance_Manager" },
                    RequiredPermissions = new List<string> { "Offer.FinanceApprove" },
                    IsMandatory = true,
                    TimeoutHours = 48
                },
                new ApprovalStep
                {
                    StepNumber = 3,
                    StepName = "CEO Approval",
                    RequiredRoles = new List<string> { "CEO", "President" },
                    RequiredPermissions = new List<string> { "Offer.FinalApprove" },
                    IsMandatory = true,
                    TimeoutHours = 72
                }
            },
            Policy = new ApprovalPolicy
            {
                RequireAllSteps = true,
                AllowParallelApproval = false,
                AllowDelegation = true,
                RequireJustification = true,
                MaxApprovalDays = 10
            }
        };
    }
}
```

***

## 🧪 Testing the Role-Based System

```csharp
[Test]
public void JobPostingDraft_ShouldAllowManagerToPublish()
{
    // Arrange
    var state = new JobPostingDraft();
    
    // Act & Assert
    Assert.IsTrue(state.CanTransition("Manager", "Publish"));
    Assert.IsTrue(state.CanTransition("TeamLead", "Publish"));
    Assert.IsTrue(state.CanTransition("HR", "Publish"));
    Assert.IsFalse(state.CanTransition("Interviewer", "Publish"));
    Assert.IsFalse(state.CanTransition("Finance", "Publish"));
}

[Test]
public void JobPostingPublished_ShouldOnlyAllowHRToClose()
{
    // Arrange
    var state = new JobPostingPublished();
    
    // Act & Assert
    Assert.IsTrue(state.CanTransition("HR", "Close"));
    Assert.IsTrue(state.CanTransition("Admin", "Close"));
    Assert.IsTrue(state.CanTransition("RecruitmentManager", "Close"));
    Assert.IsFalse(state.CanTransition("Manager", "Close"));
    Assert.IsFalse(state.CanTransition("TeamLead", "Close"));
}

[Test]
public void ApplicationSubmitted_ShouldOnlyAllowHRToScreen()
{
    // Arrange
    var state = new ApplicationSubmitted();
    
    // Act & Assert
    Assert.IsTrue(state.CanTransition("HR", "Screen"));
    Assert.IsTrue(state.CanTransition("RecruitmentManager", "Screen"));
    Assert.IsFalse(state.CanTransition("Manager", "Screen"));
    Assert.IsFalse(state.CanTransition("Interviewer", "Screen"));
}

[Test]
public void InterviewScheduled_ShouldOnlyAllowAssignedInterviewerToComplete()
{
    // Arrange
    var state = new InterviewScheduled();
    var interviewerId = "123";
    var currentUserId = "123";
    var otherUserId = "456";
    
    // Act & Assert
    Assert.IsTrue(state.CanTransition("Interviewer", "Complete", interviewerId, currentUserId));
    Assert.IsFalse(state.CanTransition("Interviewer", "Complete", interviewerId, otherUserId));
    Assert.IsFalse(state.CanTransition("HR", "Complete", interviewerId, currentUserId));
}

[Test]
public void WorkflowEngine_ShouldTransitionBasedOnConfiguration()
{
    // Arrange
    ConfigureJobPostingWorkflow();
    var jobPosting = new JobPosting();
    jobPosting.JobPostingState = new JobPostingDraft();
    
    var context = new Dictionary<string, object>
    {
        ["UserRole"] = "Manager",
        ["Priority"] = "High",
        ["Department"] = "IT"
    };
    
    // Act
    jobPosting.ProcessWorkflowAction("Publish", context);
    
    // Assert
    Assert.IsInstanceOf<JobPostingPublished>(jobPosting.JobPostingState);
}
```

***

## 🎯 Key Benefits of This Enhanced Design

### **1. Complete Recruitment Workflow**

* **End-to-End Process**: From job posting to employee onboarding
* **Multi-Level Approval**: HR → Manager → Director → CEO
* **State Management**: Clear workflow states for each entity
* **Audit Trail**: Complete tracking of all actions and approvals

### **2. Enterprise-Grade Security**

* **Role-Based Access Control**: Fine-grained permissions for each state transition
* **Dynamic Workflow Configuration**: Workflow rules can be changed without code changes
* **Conditional Transitions**: Business rules drive automatic state changes
* **Interviewer Isolation**: Interviewers can only access their assigned interviews
* **Organizational Hierarchy**: OrgAccount and UserAccount structure for managing organizational relationships
* **Accountability Matrix**: Responsibility relationships between entities for audit and compliance

### **3. Flexbase Framework Integration**

* **Auto-Generated APIs**: REST endpoints for all operations
* **Database Management**: Automatic migrations and schema updates
* **Message Bus**: Event-driven communication between services
* **Security**: Built-in authentication and authorization

### **4. Scalable Architecture**

* **Modular Design**: Each domain model is independent
* **Workflow Engine**: Configurable approval processes
* **Role-Based Access**: Fine-grained permissions
* **Event-Driven**: Loose coupling between components

### **5. Business Value**

* **Faster Development**: 60-70% reduction in development time
* **Consistent Quality**: Framework-enforced patterns
* **Easy Maintenance**: Clear separation of concerns
* **Future-Proof**: Easy to extend and modify

***

## 🚀 Implementation Summary

This enhanced recruitment application design demonstrates how the Flexbase framework can be used to create a comprehensive, enterprise-grade recruitment system with:

* **8 Core Modules**: JobPostings, Candidates, EmployeeReferrals, Vendors, VendorReferrals, Applications, Interviews, Offers
* **12 Domain Models**: JobPosting, Candidate, EmployeeReferral, VendorReferral, Vendor, Application, Interview, Offer, Employee, OrgAccount, UserAccount, AccountabilityMatrix
* **6 Enhanced Workflow State Machines**: Complete state management with role-based access control (JobPosting, EmployeeReferral, VendorReferral, Application, Interview, Offer)
* **Employee Referral System**: Internal employee referrals with approval workflow
* **Vendor Referral System**: External vendor/agency referrals managed by HR with commission tracking
* **Organizational Hierarchy**: OrgAccount and UserAccount structure for managing organizational relationships and accountability matrix
* **HR-Managed Candidate Pipeline**: Candidates managed by HR/Recruiters, not self-service
* **Vendor Integration**: Vendors selected by HR during candidate creation; no vendor login required
* **Multi-Level Approval Process**: Configurable approval workflows
* **Role-Based Security**: Fine-grained access control for each state transition
* **Dynamic Workflow Configuration**: Workflow rules defined in configuration, not code
* **Event-Driven Architecture**: Loose coupling and scalability
* **Complete Audit Trail**: Full tracking of all operations
* **Future Contractor Integration**: Ready for external contractor candidate submissions

The framework handles all the infrastructure complexity while developers focus on business logic and workflow design, resulting in a robust, maintainable, and scalable recruitment application with enterprise-grade security and role-based access control.

***

*This enhanced design showcases the power of the Flexbase framework in creating complex, workflow-driven applications with minimal development effort while maintaining enterprise-grade quality, consistency, and security.*
