System architecture

High level

Basic components of the system without implementation detail

@startuml
hide methods

class Client {
    Web browser
    Client app
}

package "Back-end" {
    class "Web Server" {
        + Django site
        + Celery job server
        + business logic
    }

    class "Static Server" {
        + javascript
        + css
        + site graphics
    }

    database "Database" {
    }

    database "Cache" {
    }

    class "File Storage" {
        Stores user-uploaded files
    }

    class "Backup Storage" {
    }
}

Client <===> "Web Server"
Client <=== "Static Server"
"Web Server" --> "Database"
"Web Server" --> "Cache"
"Web Server" --> "File Storage"

"Backup Storage" <... "Database"
"Backup Storage" <... "File Storage"
@enduml

Azure

Regions

High level architecture showing interaction between regions

@startuml

package "European Union" {
    [Azure EU]
    note right of "Azure EU": Central server, handles\nredirects to other regions\nbased on username\nHolds mapping of users to regions.
}

package "United Kingdom" {
    [Azure UK]
    [Azure EU] ...> [Azure UK]: downstream\n(authentication)
    [Azure EU] <... [Azure UK]: upstream\n(user sync)
}

package "United Arab Emirates" {
    [Azure UAE]
    [Azure EU] ...> [Azure UAE]: downstream\n(authentication)
    [Azure EU] <... [Azure UAE]: upstream\n(user sync)

    User --> [Azure EU]: Login request
    User <-- [Azure EU]: Login response redirecting\n to local Azure instance
    User ==> [Azure UAE]: Data request
    User <== [Azure UAE]: Data response

    note right of "Azure UAE": Regional server,\nstores all client data in the region.
}

@enduml

High level inter-region communication

Kubernetes

Current architecture implemented in the Azure cloud using Azure Kubernetes Service

@startuml
hide methods

package "Client" {
    class "Website" {
        - Dashboard
        - Public Reports
    }
    class "Mobile App" {
        - json feed
        - submitting audits
    }
}

package "Azure Services" {
    database "Container Registry" {
    }

    artifact Gateway [
        Azure Gateway
    ]

    database "Postgres" {
    }

    cloud "Kubernetes Cluster" {
        interface Ingress {
            public IP
            terminates SSL
        }

        class "Migration runner" {
            - Runs database migrations during deployment
        }

        class "CMS" {
            + "Runs on 3+ load-balanced pods"
            - autoscales based on CPU usage
            - Render dynamic Python pages
        }

        class "Static files server" {
            + Serve static files (js, css etc)
        }

        class "Media files server" {
            - serve media files (uploaded by users)
        }

        package Celery {
            class "Celery worker" {
                - Execute jobs
                - Schedule periodic jobs
            }
            class "Celery scheduler" {
                - Execute jobs
                - Schedule periodic jobs
            }
        }

        database Redis {
        }

        class "Backup runner" {
            Runs nightly backups
            - database backups
            - media file backups
        }
        class "Maintenance runner" {
            Runs scheduled maintenance worker
            - deletes submission logs
            - creates initial revisions for objects
        }
        Ingress === CMS
        Ingress === "Static files server"
        Ingress === "Media files server"
        CMS -> Postgres
        CMS ---> Redis: Add job
        CMS <---> Redis: Cache
        "Celery worker" -> Redis: Take job
        "Celery scheduler" --> Redis: Add job
        "Maintenance runner" ..> Postgres
        "Backup runner" -- Postgres: Dump database
        "Migration runner" ....> Postgres : run\nmigrations
    }

    "Container Registry" ==> "Kubernetes Cluster": Pull updated build

    package "Azure File" {
        class media {
            User-generated files
            - QIP photos
            - QIP Audio recordings
            - User documents
            - Question photos
        }
        class backups {
            Stores backups of
            - database
            - media files
        }
        "Backup runner" ---> backups: Store database dump\nand media files
        "CMS" ---> media: Mounted volume
        "Media files server" ---> media: Mounted volume
        "Backup runner" <-- media: Copy\nmedia files
    }

    Client ...> "Gateway": HTTPS requests
    Gateway ...> "Ingress"
}

package "MEG Internal" {
    class Builder {
        Runs CI/CD tasks:
        - tests
        - builds
        - deploys
    }
    class "Internal Tools" {
        - Git server
        - Redmine
    }

    Builder <- "Internal Tools": Trigger job
    Builder ==> "Container Registry": Upload\nimages
    Builder <== "Container Registry": Test\nimage
    Builder ...> "Migration runner": Trigger migrations\nbefore deployment
    Builder ...> "Kubernetes Cluster": Trigger\ndeployment
}

@enduml

Deployed architecture

On-prem

On premises deployment does not take advantage of Kubernetes. It can be hosted in a VM on a single machine. Further details in On-prem documentation.

@startuml

hide methods

class Client {
    Web browser
}

package "Windows Server" {

    package "Virtual Machine" {
        class "Web Server" {
            + nginx
            + reverse proxy
        }

        class "App Server" {
            back-end
        }
        class "Static Server" {
            + JavaScript for back-end
            + css
            + images
        }
        class "Client web app" {
            MEG app
        }
        database Cache {
        }

        Client <====> "Web Server"
        "Web Server" --> "Static Server"
        "Web Server" --> "Client web app"
        "Web Server" --> "App Server"
    }

    database "Postgres" {
    }

    class "File Storage" {
        + User-uploaded files
    }

    class "Backup Storage" {
        + database backups
        + file backups
    }

    class "Log Storage" {
        + Application logs
        + Application errors
    }

    "App Server" --> "Postgres"
    "App Server" --> "File Storage"
    "App Server" ...> "Cache"
    "App Server" ...> "Log Storage"

    "Backup Storage" <... "Postgres"
    "Backup Storage" <... "File Storage"
}

package "Existing on-prem services" {
    database "Active Directory" {
    }
    class "Rhapsody Server" {
        + Formulary
    }
    class "BI System" {
    }
    class "E-mail server" {
        MS Exchange 2016
    }
}

"App Server" ====> "Active Directory": User authentication
"App Server" ====> "Rhapsody Server": Integrations
"App Server" ====> "E-mail server": Outgoing mail
' BI system planned. Implementation details in #27335
"BI System" =====> "Web Server": API

@enduml

Architecture planned for King Fahd Military Medical Complex in Saudi Arabia

Security

This diagram outlines at a high level the security architecture the meg qms product.

@startuml
hide methods

package "Client" {
    class "Website" {
        - dashboard
        - public reports
    }
    class "Mobile app" {
        - json feed
        - submitting audits
    }
}

package "Azure services" {
    database "Container registry" {
    }

    artifact Gateway [
        Azure gateway
    ]

    database "Postgres" {
    }
    note left of "Postgres"
      - Full Backup: nightly
      - Incremental Backups: every five minutes
      - Encryption: at rest using platform managed keys
    end note

    class "Azure backup vault" {
      + encrypted at rest using platform managed keys
      + immutable
    }

    cloud "Kubernetes cluster" {

    }

    class "Azure file" {
        + User-generated files
    }

    "Postgres" ---> "Azure backup vault": Store database dump
    "Kubernetes cluster" ---> "Azure file": Mounted volume
    "Azure file" ---> "Azure backup vault": Store media files
    "Kubernetes cluster" ---> "Container registry": Pull updated images

    Client ...> "Gateway": HTTPS encryption in transit
    Gateway ...> "Kubernetes cluster"
    "Kubernetes cluster" -> Postgres
}

class Staff {
  + sophos endpoint protection
  + google workspace account with mfa enforced
  + employee VPN
}

package "Office building" {

  class "Physical security" {
    - keycard access required to access office
    - front desk security requiring visitor sign in
  }

  class "Office router" {
    - default credentials changed
  }

  package "Office network" {
    class "On-prem servers" {
      + sophos endpoint protection for servers
      + ssh access via private key authentication and VPN only
    }
  }
}

class "Gitlab" {
  + security scanning: SAST, DAST, Dependency scanning
  + code reviews
  + build new container images
  + deploy to kubernetes
}

Staff ...> "Gitlab": SSO
Staff ...> "Azure services": MFA enforced
Staff ...> "Client": SSO, IP restriction, mfa enforced, session timeout, brute force security
Staff ...> "On-prem servers": ssh IP restricted
"Gitlab" ...> "Kubernetes cluster": deploy update
"Gitlab" ...> "Container registry": push new builds
"Gitlab" ...> "On-prem servers": run pipeline jobs

@enduml

Security architecture