Skip to main content
Documentation

System architecture

OpenDDE is a microservices platform orchestrated by Docker Compose. Six containers work together to provide a complete computational drug design workflow.

System diagram

┌─────────────────────────────────────────────────────────┐
│                    Docker Compose                       │
│                                                         │
│  ┌──────────┐    ┌──────────┐    ┌──────────────────┐  │
│  │ Frontend  │───▶│ Backend  │───▶│  Microservices   │  │
│  │ Next.js   │    │ FastAPI  │    │                  │  │
│  │ :3000     │    │ :8000    │    │  P2Rank  :5001   │  │
│  └──────────┘    │          │    │  RDKit   :5002   │  │
│                  │          │───▶│  Immune  :5003   │  │
│                  │          │    │                  │  │
│                  │          │    └──────────────────┘  │
│                  │          │                           │
│                  │          │───▶ Redis :6379           │
│                  │          │───▶ Supabase (external)   │
│                  │          │───▶ ChEMBL API (external) │
│                  │          │───▶ UniProt API (external) │
│                  │          │───▶ Claude API (external)  │
│                  └──────────┘                           │
└─────────────────────────────────────────────────────────┘

Services

ServicePortTechnologyPurpose
frontend3000Next.js 14React UI with App Router
backend8000FastAPI + PythonREST API, orchestration, caching
p2rank5001Java + Flask wrapperML pocket prediction
rdkit5002Python + RDKitMolecular properties, depiction, similarity
immunebuilder5003Python + PyTorchAntibody structure prediction
redis6379Redis 7Response caching, session data

Data flow

Here’s what happens when you search for a protein target:

  1. Frontend sends POST /api/v1/target/resolve with the UniProt ID or gene name.
  2. Backend checks Supabase for a cached target. If not found, it queries the UniProt API and downloads the AlphaFold structure (CIF file).
  3. Backend sends the CIF file to the P2Rank service, which returns predicted binding pockets.
  4. Backend queries ChEMBL for known ligands targeting this protein.
  5. Backend stores everything in Supabase and returns the full target payload to the frontend.
  6. Frontend renders the 3D structure, pocket list, and ligand table.

Engine swap layer

OpenDDE is designed so that any computational engine can be replaced without changing the rest of the system. Each engine is accessed through a standardized adapter interface:

# backend/engines/pocket_engine.py
class PocketEngine:
    """Abstract interface for pocket prediction engines."""

    async def predict(self, structure_path: str) -> list[Pocket]:
        raise NotImplementedError

class P2RankEngine(PocketEngine):
    """P2Rank implementation."""

    async def predict(self, structure_path: str) -> list[Pocket]:
        response = await httpx.post(
            f"{self.base_url}/predict",
            files={"structure": open(structure_path, "rb")}
        )
        return self._parse_response(response.json())

# Future: swap in a different engine
class FPocketEngine(PocketEngine):
    """Alternative pocket predictor."""
    ...

This modular design means you could swap P2Rank for FPocket, AlphaFold for Boltz-2, or ImmuneBuilder for ABodyBuilder3 — all by implementing a single adapter class. See Engine swap layer for details.

Caching strategy

OpenDDE uses two layers of caching:

  • In-memory response cache — GET requests to /api/v1/* are cached in memory for 1 hour (max 200 entries). Returns x-cache: HIT or x-cache: MISS headers.
  • Structure file caching — CIF files are served with Cache-Control: public, max-age=86400 (24 hours).

Learn more