# 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.*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flexbase.in/sample-references-domain/recruitment-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
