Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Platform Adapters

Each job platform is implemented as an adapter behind the JobSearchService trait.

Trait Definition

#![allow(unused)]
fn main() {
#[async_trait]
pub trait JobSearchService: Send + Sync {
    fn platform(&self) -> Platform;
    async fn search(&self, criteria: &SearchCriteria) -> Result<Vec<Job>, DomainError>;
    async fn fetch_details(&self, job: &Job) -> Result<Job, DomainError>;
}
}

Adapter Architecture

graph LR
    subgraph kairos-core
        Trait[JobSearchService trait]
    end

    subgraph kairos-platform
        Seek[SeekClient]
        Indeed[IndeedClient]
        LinkedIn[LinkedInClient]
        RL[RateLimiter]
        HC[PlatformHttpClient]
    end

    Seek --> Trait
    Indeed --> Trait
    LinkedIn --> Trait
    Seek --> HC
    Indeed --> HC
    LinkedIn --> HC
    HC --> RL

Shared Infrastructure

RateLimiter

Enforces randomized delays between requests:

  • Seek: 2-5s
  • Indeed: 3-8s (more aggressive anti-bot)
  • LinkedIn: 2-6s

PlatformHttpClient

Wraps reqwest::Client with:

  • Rate limiting
  • User-Agent rotation (pool of realistic browser UAs)
  • Retry with exponential backoff (3 attempts)
  • Configurable proxy support

Seek.com.au

Method: reqwest + scraper (HTTP + HTML parsing)

flowchart LR
    Search[Search URL] --> HTTP[reqwest GET]
    HTTP --> HTML[HTML Response]
    HTML --> Parse["scraper: extract JSON"]
    Parse --> Struct[SeekJobListing]
    Struct --> Map[Map to Job entity]
  • Server-side rendered with job data embedded as JSON in HTML
  • Pagination: 20 results per page
  • Key selector: <script type="application/json"> tag

Indeed AU

Method: chromiumoxide (headless Chrome via CDP)

flowchart LR
    Search[Search URL] --> Chrome[chromiumoxide]
    Chrome --> Wait[Wait for render]
    Wait --> DOM[Extract from DOM]
    DOM --> Parse[Parse listings]
    Parse --> Map[Map to Job entity]
  • Requires JavaScript rendering (Cloudflare protection)
  • Anti-detection: disable automation flags, randomized viewport, human-like scrolling
  • Slower (~5-10s per page)

LinkedIn

Method: reqwest + scraper (public job listings)

  • Public job search pages, no login required
  • Fallback: if blocked (403/429), suggests user browse manually
  • Jobs can be added by pasting URL

Adding a New Platform

  1. Create module under kairos-platform/src/
  2. Implement JobSearchService trait
  3. Add variant to Platform enum in kairos-core
  4. Register adapter in kairos-api dependency injection

No existing code needs to change.