File size: 3,101 Bytes
788eab4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | # IMATS ERP Integration Architecture
This document specifies the integration architecture between the Collegiate Placement Engine and IMATS (Institutional Management & Academic Tracking System) or enterprise campus ERPs.
---
## 1. Architectural Pattern: Hexagonal Ports and Adapters
The placement engine isolates external enterprise systems behind an explicit domain port:
- **Port Interface**: [`backend.app.integrations.imats.interface.IIMATSClient`](backend/app/integrations/imats/interface.py)
- **Production/Sandbox Adapter**: [`backend.app.integrations.imats.client.SandboxIMATSClient`](backend/app/integrations/imats/client.py)
- **Offline Outbox Engine**: [`backend.app.integrations.imats.outbox.IMATSOutboxManager`](backend/app/integrations/imats/outbox.py)
---
## 2. Key Reliability Guarantees
### A. Idempotency Keys
Every outbound sync operation (`push_shortlist`, `push_schedule`) requires an unguessable `idempotency_key`. The server and client deduplicate duplicate submissions, guaranteeing that network reconnects or retry storms never duplicate records or schedule slots.
### B. Exponential Backoff with Jitter
When transient network interruptions occur across the college intranet, the client performs exponential backoff:
$$\text{delay} = \text{initial\_backoff} \times 2^{\text{attempt}}$$
By default, the client makes 3 retry attempts before delegating to the offline outbox.
### C. Offline Outbox Pattern
When the campus LAN is severed or the remote IMATS server is in a maintenance window:
1. Shortlists and schedules are persisted to the local outbox marked as `PENDING`.
2. When connectivity is re-established, the officer or background automation calls `/api/imats/sync` to flush pending items reliably.
---
## 3. How to Swap in the Live Production Endpoint
To connect to a live institutional IMATS ERP instance:
1. Create a concrete class `LiveIMATSClient` implementing `IIMATSClient`:
```python
import httpx
from backend.app.integrations.imats.interface import IIMATSClient
class LiveIMATSClient(IIMATSClient):
def __init__(self, base_url: str, api_token: str):
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json",
}
def push_shortlist(self, drive_id: str, student_ids: list[str], idempotency_key: str):
resp = httpx.post(
f"{self.base_url}/api/v1/drives/{drive_id}/shortlist",
json={"student_ids": student_ids},
headers={**self.headers, "Idempotency-Key": idempotency_key},
timeout=15.0,
)
resp.raise_for_status()
return resp.json()
```
2. Register the live adapter in `backend/app/api/main.py` using standard dependency injection:
```python
imats_client = LiveIMATSClient(
base_url=os.getenv("IMATS_BASE_URL", "https://imats.college.edu/api"),
api_token=os.getenv("IMATS_API_TOKEN", "secret"),
)
```
|