Here’s a complete, production-ready refactoring that splits the monolithic file into a clean package structure. Each module follows the single-responsibility principle, uses relative imports to avoid circular dependencies, and exposes a clean public API.

### `queue/__init__.py`
```python
"""Job queue package.

Public API:
    - Job: Data model for a queueable task
    - JobQueue: FIFO queue implementation
    - PriorityQueue: Priority-based queue implementation
    - process_with_retry: Standalone retry wrapper with exponential backoff
"""

from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

__all__ = ["Job", "JobQueue", "PriorityQueue", "process_with_retry"]
```

### `queue/core.py`
```python
"""Core data structures and basic FIFO queue operations."""

from dataclasses import dataclass, field
from typing import Any, Callable, Optional
from collections import deque


@dataclass
class Job:
    """Represents a unit of work to be executed."""
    id: str
    func: Callable[..., Any]
    args: tuple = ()
    kwargs: dict = field(default_factory=dict)
    priority: int = 0
    status: str = "pending"
    retries: int = 0
    max_retries: int = 3
    last_error: Optional[Exception] = None


class JobQueue:
    """Basic FIFO job queue."""

    def __init__(self) -> None:
        self._queue: deque[Job] = deque()

    def enqueue(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        job.status = "pending"
        self._queue.append(job)

    def dequeue(self) -> Optional[Job]:
        """Remove and return the next job, or None if empty."""
        if self._queue:
            job = self._queue.popleft()
            job.status = "running"
            return job
        return None

    def size(self) -> int:
        """Return the number of jobs waiting."""
        return len(self._queue)
```

### `queue/retry.py`
```python
"""Retry logic with exponential backoff."""

import time
from typing import Any
from .core import Job


def process_with_retry(job: Job) -> Any:
    """Execute a job with exponential backoff on failure.
    
    Raises:
        Exception: The last exception raised if all retries are exhausted.
    """
    last_error = None
    for attempt in range(job.max_retries + 1):
        try:
            result = job.func(*job.args, **job.kwargs)
            job.status = "completed"
            return result
        except Exception as e:
            last_error = e
            job.retries += 1
            job.last_error = e
            
            if attempt < job.max_retries:
                # Exponential backoff: 1s, 2s, 4s, 8s...
                wait_time = 2 ** attempt
                time.sleep(wait_time)
            else:
                job.status = "failed"
                
    raise last_error
```

### `queue/priority.py`
```python
"""Priority queue implementation using a min-heap."""

import heapq
from typing import Optional
from .core import Job


class PriorityQueue:
    """Priority-based job queue. Lower priority number = higher precedence."""

    def __init__(self) -> None:
        self._heap: list[tuple[int, int, Job]] = []
        self._counter = 0  # Tie-breaker to preserve FIFO order for equal priorities

    def enqueue(self, job: Job) -> None:
        """Add a job to the priority queue."""
        job.status = "pending"
        entry = (job.priority, self._counter, job)
        heapq.heappush(self._heap, entry)
        self._counter += 1

    def dequeue(self) -> Optional[Job]:
        """Remove and return the highest-priority job, or None if empty."""
        if self._heap:
            _, _, job = heapq.heappop(self._heap)
            job.status = "running"
            return job
        return None

    def size(self) -> int:
        """Return the number of jobs waiting."""
        return len(self._heap)
```

### Key Design Decisions:
1. **Relative Imports**: Used `.core`, `.priority`, etc., to ensure the package works correctly when installed or imported as a module without path pollution.
2. **No Circular Dependencies**: `core.py` contains zero internal imports. `retry.py` and `priority.py` only import `Job` from `core.py`, keeping the dependency graph strictly unidirectional.
3. **Single Responsibility**: 
   - `core.py`: Data model + basic FIFO queue
   - `retry.py`: Execution wrapper + backoff algorithm
   - `priority.py`: Heap-based ordering logic
   - `__init__.py`: Public API surface
4. **Extensibility**: Both `JobQueue` and `PriorityQueue` share the same `enqueue`/`dequeue`/`size` interface, allowing them to be swapped as drop-in backends in a worker loop:
   ```python
   from queue import Job, JobQueue, PriorityQueue, process_with_retry

   # Swap backend without changing worker logic
   queue = PriorityQueue() 
   # or queue = JobQueue()

   while job := queue.dequeue():
       process_with_retry(job)
   ```