Here is the refactored package structure. I have inferred standard implementations for the classes based on the names provided to ensure functionality is maintained while adhering to the Single Responsibility Principle.

### Directory Structure
```text
queue/
├── __init__.py
├── core.py
├── priority.py
└── retry.py
```

### 1. `queue/__init__.py`
This file defines the public API. It aggregates classes and functions from the submodules so users can import directly from the package.

```python
# queue/__init__.py
"""
Job Queue Package.
Provides Job definitions, Queue implementations, and Retry logic.
"""

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

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

### 2. `queue/core.py`
Contains the data model (`Job`) and the basic FIFO queue implementation (`JobQueue`). It has no dependencies on other modules in the package.

```python
# queue/core.py
"""
Core module containing Job data model and basic FIFO Queue logic.
"""
from dataclasses import dataclass, field
from typing import Any, Optional
from collections import deque
import uuid


@dataclass
class Job:
    """
    Represents a unit of work to be processed.
    """
    payload: Any
    priority: int = 0
    status: str = "pending"  # pending, processing, completed, failed
    created_at: str = field(default_factory=lambda: str(uuid.uuid4()))
    retry_count: int = 0
    max_retries: int = 3
    
    def __hash__(self):
        return hash(self.created_at)

    def __eq__(self, other):
        if isinstance(other, Job):
            return self.created_at == other.created_at
        return False


class JobQueue:
    """
    A basic First-In-First-Out (FIFO) queue for Job instances.
    """
    def __init__(self):
        self._queue: deque[Job] = deque()

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

    def dequeue(self) -> Optional[Job]:
        """Remove and return the job at the front of the queue."""
        if not self._queue:
            return None
        return self._queue.popleft()

    def peek(self) -> Optional[Job]:
        """Return the job at the front without removing it."""
        if not self._queue:
            return None
        return self._queue[0]

    def is_empty(self) -> bool:
        """Check if the queue is empty."""
        return len(self._queue) == 0

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

### 3. `queue/priority.py`
Contains the `PriorityQueue` implementation. It imports `Job` from `core` to work with the data model but handles the heap logic independently.

```python
# queue/priority.py
"""
Priority Queue module.
Implements a heap-based queue where higher priority jobs are processed first.
"""
from typing import Any, Optional
import heapq

from .core import Job


class PriorityQueue:
    """
    A priority queue where jobs with higher priority values are dequeued first.
    Uses a min-heap structure.
    """
    def __init__(self):
        # Heap stores tuples: (priority, timestamp, job)
        # Timestamp is used as a tie-breaker to maintain FIFO for same priority
        self._heap: list[tuple[int, float, Job]] = []
        self._counter = 0

    def push(self, job: Job) -> None:
        """Add a job to the priority queue."""
        # Invert priority so higher value = lower heap index (processed first)
        # We use -priority because heapq is a min-heap
        entry = (-job.priority, self._counter, job)
        heapq.heappush(self._heap, entry)
        self._counter += 1

    def pop(self) -> Optional[Job]:
        """Remove and return the job with the highest priority."""
        if not self._heap:
            return None
        _, _, job = heapq.heappop(self._heap)
        return job

    def peek(self) -> Optional[Job]:
        """Return the job with the highest priority without removing it."""
        if not self._heap:
            return None
        return self._heap[0][2]

    def is_empty(self) -> bool:
        """Check if the priority queue is empty."""
        return len(self._heap) == 0
```

### 4. `queue/retry.py`
Contains the logic for handling failed jobs with exponential backoff. It imports `Job` from `core`.

```python
# queue/retry.py
"""
Retry logic module.
Implements exponential backoff for job processing failures.
"""
import time
from typing import Callable, Any

from .core import Job


def process_with_retry(
    job: Job, 
    processor: Callable[[Job], Any], 
    max_retries: int = None
) -> Job:
    """
    Attempts to process a job. If it fails, retries with exponential backoff.
    
    Args:
        job: The Job instance to process.
        processor: A callable that accepts a Job and returns a result.
        max_retries: Optional override for the job's max_retries.
        
    Returns:
        The updated Job instance with new status and retry count.
    """
    retries = max_retries if max_retries is not None else job.max_retries
    base_delay = 1.0  # seconds
    
    for attempt in range(retries + 1):
        try:
            processor(job)
            job.status = "completed"
            return job
        except Exception as e:
            job.retry_count += 1
            job.status = "failed"
            
            if attempt < retries:
                # Exponential backoff: 1, 2, 4, 8...
                delay = base_delay * (2 ** attempt)
                time.sleep(delay)
            else:
                # Max retries exhausted
                break
                
    return job
```

### Key Refactoring Decisions

1.  **Circular Import Safety**: `core.py` is the foundation. It does not import from `priority` or `retry`. `priority` and `retry` import `Job` from `core`. This prevents circular dependencies.
2.  **Data Encapsulation**: The `Job` dataclass is defined in `core.py` because it represents the core domain entity. It does not know how it is queued or retried.
3.  **API Simplification**: `__init__.py` acts as a facade. Users don't need to know the internal package structure; they just `from queue import Job, PriorityQueue`.
4.  **Algorithm Separation**: `JobQueue` uses `collections.deque` (O(1) FIFO), while `PriorityQueue` uses `heapq` (O(log N) priority). This keeps the logic distinct and testable.
5.  **Backoff Logic**: `process_with_retry` is a utility function that operates on the `Job` state, keeping execution logic separate from queue structure logic.