# System Topology {#system-topology}



## Network Architecture {#network-architecture}


All services run on a shared Docker bridge network called `mquad-network`. Traefik acts as the ingress gateway, routing external requests to the appropriate service based on path prefixes.





```mermaid
graph TB
    subgraph "External"
        Browser[Browser]
        ODKCollect[ODK Collect]
    end

    subgraph "Edge Layer"
        Traefik[Traefik v3.0<br/>:80 / :443 / :8090]
    end

    subgraph "mquad-network Bridge"
        subgraph "Core"
            Backend[backend<br/>:8000]
        end

        subgraph "Domain"
            Survey[survey-service<br/>:8001]
            Data[data-service<br/>:8002]
            Workspace[workspace-service<br/>:8006]
        end

        subgraph "Analytics"
            Sync[sync-service<br/>:8003]
            Analytics[analytics-service<br/>:8004]
        end

        subgraph "Integration"
            Webhook[webhook-service<br/>:8007]
            Form[form-service<br/>:8010]
            ODK[odk-bridge<br/>:8009]
            Media[media-service<br/>:8011]
        end

        subgraph "Support"
            Notification[notification-service<br/>:8012]
            Audit[audit-service<br/>:8013]
            FeatureFlags[feature-flags-service<br/>:8014]
        end

        subgraph "Documentation"
            Scalar[scalar-service<br/>:8030]
        end

        subgraph "Infrastructure"
            Postgres[(PostgreSQL :5432)]
            Redis[(Redis :6379)]
            MinIO[(MinIO :9000)]
        end

        subgraph "Monitoring"
            Prometheus[Prometheus :9090]
            Grafana[Grafana :3001]
            Loki[Loki :3100]
            Jaeger[Jaeger :16686]
        end
    end

    Browser --> Traefik
    ODKCollect --> ODK
    Traefik --> Backend
    Traefik --> Scalar
    Traefik --> Survey
    Traefik --> Data
    Traefik --> Sync
    Traefik --> Analytics
    Traefik --> Workspace
    Traefik --> Webhook
    Traefik --> ODK
    Traefik --> Form
    Traefik --> Media
    Traefik --> Notification
    Traefik --> Audit
    Traefik --> FeatureFlags

    Backend --> Postgres
    Backend --> Redis
    Survey --> Postgres
    Survey --> Redis
    Data --> Postgres
    Data --> Redis
    Media --> MinIO
```






## Port Allocation {#port-allocation}


| Port | Service | Protocol | Exposed |
|------|---------|----------|---------|
| 8000 | backend | HTTP | via Traefik |
| 8001 | survey-service | HTTP | via Traefik |
| 8002 | data-service | HTTP | via Traefik |
| 8003 | sync-service | HTTP | via Traefik |
| 8004 | analytics-service | HTTP | via Traefik |
| 8006 | workspace-service | HTTP | via Traefik |
| 8007 | webhook-service | HTTP | via Traefik |
| 8009 | odk-bridge | HTTP | via Traefik |
| 8010 | form-service | HTTP | via Traefik |
| 8011 | media-service | HTTP | via Traefik |
| 8012 | notification-service | HTTP | via Traefik |
| 8013 | audit-service | HTTP | via Traefik |
| 8014 | feature-flags-service | HTTP | via Traefik |
| 8030 | scalar-service | HTTP | :8030 |
| 5433 | PostgreSQL (host) | TCP | :5433 |
| 6372 | Redis (host) | TCP | :6372 |


## Traefik Routing {#traefik-routing}


Traefik routes requests based on path prefix rules defined in `api-gateway/traefik/dynamic.yaml`:

```yaml
http:
  routers:
    backend:
      rule: "PathPrefix(`/api/v1`)"
      service: backend
    survey-service:
      rule: "PathPrefix(`/api/v1/surveys`)"
      service: survey-service
    scalar:
      rule: "PathPrefix(`/`)"
      service: scalar-service
```


## Service Discovery {#service-discovery}


Services use **static DNS-based discovery** via Docker Compose service names. Each service resolves others by container name (e.g., `http://survey-service:8001`). The scalar-service performs active discovery by polling health endpoints:

```python
SERVICE_DEFINITIONS = {
    "survey-service": {
        "url": "http://survey-service:8001",
        "port": 8001,
        "health_url": "http://survey-service:8001/api/v1/health",
    }
}
```
