Here’s the refactored package structure with each module following the single-responsibility principle, proper relative imports, and a clean public API.

### `queue/core.py`
```python
import uuid
from dataclasses import dataclass, field
from typing import Any, Optional
from collections import deque

@dataclass
class Job:
    """Represents a unit of work to be processed."""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    payload: Any = None
    priority: int = 0
    status: str = "pending"
    retries: int = 0
    max_retries: int = 3

class JobQueue:
    """Basic FIFO job queue implementation."""
    def __init__(self) -> None:
        self._queue: deque[Job] = deque()

    def enqueue(self, job: Job) -> None:
        self._queue.append(job)

    def dequeue(self) -> Optional[Job]:
        if self._queue:
            return self._queue.popleft()
        return None

    def size(self) -> int:
        return len(self._queue)

    def is_empty(self) -> bool:
        return len(self._queue) == 0
```

### `queue/priority.py`
```python
import heapq
from typing import Optional
from .core import Job

class PriorityQueue:
    """Priority queue implementation where lower priority values are processed first."""
    def __init__(self) -> None:
        # Heap stores tuples of (priority, job_id, job) for stable sorting
        self._heap: list[tuple[int, str, Job]] = []

    def enqueue(self, job: Job) -> None:
        heapq.heappush(self._heap, (job.priority, job.id, job))

    def dequeue(self) -> Optional[Job]:
        if self._heap:
            _, _, job = heapq.heappop(self._heap)
            return job
        return None

    def size(self) -> int:
        return len(self._heap)

    def is_empty(self) -> bool:
        return len(self._heap) == 0
```

### `queue/retry.py`
```python
import time
from typing import Any, Callable, Optional
from .core import Job

def process_with_retry(
    func: Callable[..., Any],
    job: Job,
    max_retries: Optional[int] = None,
    backoff_factor: float = 1.0
) -> Any:
    """
    Execute a function with exponential backoff retry logic.
    
    Args:
        func: Callable to execute with job.payload
        job: The job instance to process
        max_retries: Override job's max_retries if provided
        backoff_factor: Base delay in seconds for exponential backoff
    """
    retries = max_retries if max_retries is not None else job.max_retries
    job.status = "running"
    job.retries = 0

    while job.retries <= retries:
        try:
            result = func(job.payload)
            job.status = "completed"
            return result
        except Exception as e:
            job.retries += 1
            job.status = "failed"
            if job.retries > retries:
                raise e
            wait_time = backoff_factor * (2 ** (job.retries - 1))
            time.sleep(wait_time)
            job.status = "pending"
```

### `queue/__init__.py`
```python
"""
Job Queue Package
"""
from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

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

### Key Design Decisions:
1. **Single Responsibility**: 
   - `core.py` handles the domain model (`Job`) and basic FIFO operations
   - `priority.py` focuses solely on priority-based ordering using `heapq` for O(log n) inserts/pops
   - `retry.py` encapsulates execution, error handling, and backoff timing
2. **Import Strategy**: Uses relative imports (`.core`) which is the Pythonic standard for packages. Works seamlessly with `from queue import Job, JobQueue`.
3. **Type Safety**: Modern type hints with explicit return types and parameter annotations. `PriorityQueue` uses `(priority, id, job)` tuples to avoid `TypeError` when priorities collide.
4. **Clean Public API**: `__init__.py` exposes only the intended public interface via `__all__`, matching your requirement while keeping auxiliary components accessible.

### Usage Example:
```python
from queue import Job, JobQueue, PriorityQueue, process_with_retry

# FIFO usage
fifo = JobQueue()
fifo.enqueue(Job(payload="task_1"))

# Priority usage
pq = PriorityQueue()
pq.enqueue(Job(payload="low_priority", priority=5))
pq.enqueue(Job(payload="high_priority", priority=1))

# Retry usage
def risky_task(data: str) -> str:
    if "fail" in data:
        raise ValueError("Simulated failure")
    return f"Processed: {data}"

job = Job(payload="fail_test")
result = process_with_retry(risky_task, job, max_retries=2, backoff_factor=0.1)
```