Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions architecture/backend/apim/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ model {
color muted
}

apigeeproxy dosReadProxy "DoS Read Proxy" {
description 'Proxy to read data from DoS'
apigeeproxy dosSearchProxy "DoS Search Proxy" {
description 'Proxy for external consumers to search DoS data'
}

apigeeproxy dosWriteProxy "DoS Write Proxy" {
description 'Proxy to write data to DoS'
apigeeproxy dosIngestProxy "DoS Ingest Proxy" {
description 'Proxy for ingesting and querying data in the DoS'
}
}
}
10 changes: 5 additions & 5 deletions architecture/backend/crud-apis/healthcare-service/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ model {
}
}

apim.dosReadProxy -> healthcareServiceAPI.getHealthcareService "Routes requests to"
apim.dosReadProxy -> healthcareServiceAPI.getHealthcareServices "Routes requests to"
apim.dosWriteProxy -> healthcareServiceAPI.deleteHealthcareService "Routes requests to"
apim.dosWriteProxy -> healthcareServiceAPI.createHealthcareServices "Routes requests to"
apim.dosWriteProxy -> healthcareServiceAPI.updateHealthcareService "Routes requests to"
apim.dosIngestProxy -> healthcareServiceAPI.getHealthcareService "Routes requests to"
apim.dosIngestProxy -> healthcareServiceAPI.getHealthcareServices "Routes requests to"
apim.dosIngestProxy -> healthcareServiceAPI.deleteHealthcareService "Routes requests to"
apim.dosIngestProxy -> healthcareServiceAPI.createHealthcareServices "Routes requests to"
apim.dosIngestProxy -> healthcareServiceAPI.updateHealthcareService "Routes requests to"



Expand Down
4 changes: 2 additions & 2 deletions architecture/backend/crud-apis/healthcare-service/views.c4
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ views {
and to retrieve data for the Healthcare Service dashboard
'
include *
include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy
include healthcareServiceAPI
include healthcareServiceAPI.getHealthcareService
include healthcareServiceAPI.getHealthcareServices
Expand Down
10 changes: 5 additions & 5 deletions architecture/backend/crud-apis/location/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ model {
}
}

apim.dosReadProxy -> locationAPI.getById "Routes requests to"
apim.dosReadProxy -> locationAPI.getAllLocations "Routes requests to"
apim.dosWriteProxy -> locationAPI.createLocation "Routes requests to"
apim.dosWriteProxy -> locationAPI.updateLocation "Routes requests to"
apim.dosWriteProxy -> locationAPI.deleteLocation "Routes requests to"
apim.dosIngestProxy -> locationAPI.getById "Routes requests to"
apim.dosIngestProxy -> locationAPI.getAllLocations "Routes requests to"
apim.dosIngestProxy -> locationAPI.createLocation "Routes requests to"
apim.dosIngestProxy -> locationAPI.updateLocation "Routes requests to"
apim.dosIngestProxy -> locationAPI.deleteLocation "Routes requests to"

getById -> ftrs.db.locationTable "Queries data from"
getAllLocations -> ftrs.db.locationTable "Queries data from"
Expand Down
10 changes: 5 additions & 5 deletions architecture/backend/crud-apis/organisation/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ model {
}
}

apim.dosReadProxy -> organizationAPI.orgByOdsCode "Routes requests to"
apim.dosReadProxy -> organizationAPI.getByOrganizationId "Routes requests to"
apim.dosWriteProxy -> organizationAPI.addOrg "Routes requests to"
apim.dosWriteProxy -> organizationAPI.updateOrg "Routes requests to"
apim.dosWriteProxy -> organizationAPI.deleteOrg "Routes requests to"
apim.dosIngestProxy -> organizationAPI.orgByOdsCode "Routes requests to"
apim.dosIngestProxy -> organizationAPI.getByOrganizationId "Routes requests to"
apim.dosIngestProxy -> organizationAPI.addOrg "Routes requests to"
apim.dosIngestProxy -> organizationAPI.updateOrg "Routes requests to"
apim.dosIngestProxy -> organizationAPI.deleteOrg "Routes requests to"

orgByOdsCode -> ftrs.db.orgTable "Queries data from"
getByOrganizationId -> ftrs.db.orgTable "Queries data from"
Expand Down
3 changes: 1 addition & 2 deletions architecture/backend/crudApis/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,5 @@ model {
ftrs.crudApis.locationApi -> ftrs.db.locationTable "Reads/Writes"

// External access via APIM
apim.dosReadProxy -> ftrs.apiGateway "Proxies read requests"
apim.dosWriteProxy -> ftrs.apiGateway "Proxies write requests"
apim.dosIngestProxy -> ftrs.apiGateway "Proxies CRUD requests"
}
28 changes: 16 additions & 12 deletions architecture/backend/crudApis/views.c4
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ views {
include ftrs.db.orgTable
include ftrs.db.healthCareServiceTable
include ftrs.db.locationTable
include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy

autoLayout LeftRight 70 77
}
Expand All @@ -36,8 +36,8 @@ views {
include *
include ftrs.apiGateway
include ftrs.db.orgTable
include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy

autoLayout LeftRight 70 77
}
Expand All @@ -51,8 +51,8 @@ views {
include *
include ftrs.apiGateway
include ftrs.db.healthCareServiceTable
include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy

autoLayout LeftRight 70 77
}
Expand All @@ -66,8 +66,8 @@ views {
include *
include ftrs.apiGateway
include ftrs.db.locationTable
include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy

autoLayout LeftRight 70 77
}
Expand All @@ -78,12 +78,14 @@ views {
Sequence showing the read path through the CRUD APIs
'

apim.dosReadProxy -> ftrs.apiGateway "1. GET request via APIM"
include apim

apim.dosIngestProxy -> ftrs.apiGateway "1. GET request via DoS Ingest Proxy"
ftrs.apiGateway -> ftrs.crudApis.organisationApi "2. Route to Organisation API Lambda"
ftrs.crudApis.organisationApi -> ftrs.db.orgTable "3. Query DynamoDB"
ftrs.db.orgTable -> ftrs.crudApis.organisationApi "4. Return data"
ftrs.crudApis.organisationApi -> ftrs.apiGateway "5. FHIR response"
ftrs.apiGateway -> apim.dosReadProxy "6. Return to APIM"
ftrs.apiGateway -> apim.dosIngestProxy "6. Return to DoS Ingest Proxy"

autoLayout TopBottom
}
Expand All @@ -94,12 +96,14 @@ views {
Sequence showing the write path through the CRUD APIs
'

apim.dosWriteProxy -> ftrs.apiGateway "1. POST/PUT/DELETE request via APIM"
include apim

apim.dosIngestProxy -> ftrs.apiGateway "1. POST/PUT/DELETE request via DoS Ingest Proxy"
ftrs.apiGateway -> ftrs.crudApis.organisationApi "2. Route to Organisation API Lambda"
ftrs.crudApis.organisationApi -> ftrs.db.orgTable "3. Update DynamoDB"
ftrs.db.orgTable -> ftrs.crudApis.organisationApi "4. Confirm write"
ftrs.crudApis.organisationApi -> ftrs.apiGateway "5. FHIR response"
ftrs.apiGateway -> apim.dosWriteProxy "6. Return to APIM"
ftrs.apiGateway -> apim.dosIngestProxy "6. Return to DoS Ingest Proxy"

autoLayout TopBottom
}
Expand Down
2 changes: 1 addition & 1 deletion architecture/backend/dosSearch/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ model {
ftrs.dosSearch.ftrsService -> ftrs.db.orgTable "Queries organisation by ODS code"

// External access via APIM
apim.dosReadProxy -> ftrs.apiGateway "Proxies search requests"
apim.dosSearchProxy -> ftrs.apiGateway "Proxies search requests"
ftrs.apiGateway -> ftrs.dosSearch.getOrganizationByOds "Routes GET /Organization requests"
}
5 changes: 3 additions & 2 deletions architecture/backend/dosSearch/views.c4
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ views {
include *
include ftrs.apiGateway
include ftrs.db.orgTable
include apim.dosReadProxy
include apim
include apim.dosSearchProxy

autoLayout TopBottom 70 77
}
Expand All @@ -27,7 +28,7 @@ views {
Sequence showing the data flow through DoS Search service
'

apim.dosReadProxy -> ftrs.apiGateway "1. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}"
apim.dosSearchProxy -> ftrs.apiGateway "1. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}"
ftrs.apiGateway -> ftrs.dosSearch.getOrganizationByOds "2. Route to Lambda"
ftrs.dosSearch.getOrganizationByOds -> ftrs.dosSearch.ftrsService "3. Call service"
ftrs.dosSearch.ftrsService -> ftrs.db.orgTable "4. Query by ODS code"
Expand Down
20 changes: 9 additions & 11 deletions architecture/backend/etlOds/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ model {
odsTerminologyApi = system "ODS Data Terminology API" {
description 'ODS holds information for many different types of organisation related to health and social care'
technology 'External NHS API'
style {
color muted
}
}

// ========================================
Expand All @@ -70,26 +73,21 @@ model {
// Data flow: Transform stage
// ========================================
ftrs.etlOds.transformQueue -> ftrs.etlOds.odsTransformer "Triggers Lambda (SQS event, batch size 10)"
ftrs.etlOds.odsTransformer -> apim.dosReadProxy "Fetches organisation UUIDs by ODS code"
ftrs.etlOds.odsTransformer -> apim.dosIngestProxy "Fetches organisation UUIDs by ODS code - GET /Organization"
ftrs.etlOds.odsTransformer -> ftrs.etlOds.loadQueue "Sends transformed FHIR payloads"

// ========================================
// Data flow: Load stage
// ========================================
ftrs.etlOds.loadQueue -> ftrs.etlOds.odsLoader "Triggers Lambda (SQS event, batch size 10)"
ftrs.etlOds.odsLoader -> apim.dosWriteProxy "PUT /Organization/{uuid} updates"
ftrs.etlOds.odsLoader -> apim.dosIngestProxy "PUT /Organization/{uuid} updates"

// ========================================
// Data flow: Through CRUD APIs (write path)
// Data flow: Through CRUD APIs
// ========================================
apim.dosWriteProxy -> ftrs.apiGateway "Proxies PUT request"
apim.dosIngestProxy -> ftrs.apiGateway "Proxies GET/PUT requests"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.getOrgByOdsCode "Routes to query handler (UUID lookup)"
ftrs.crudApis.organisationApi.getOrgByOdsCode -> ftrs.db.orgTable "Queries organisation by ODS code"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.updateOrg "Routes to update handler"
ftrs.crudApis.organisationApi.updateOrg -> ftrs.db.orgTable "Persists organisation data"

// ========================================
// Data flow: Through CRUD APIs (read path for UUID lookup)
// ========================================
apim.dosReadProxy -> ftrs.apiGateway "Proxies GET request"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.getOrgByOdsCode "Routes to query handler"
ftrs.crudApis.organisationApi.getOrgByOdsCode -> ftrs.db.orgTable "Queries organisation by ODS code"
}
8 changes: 4 additions & 4 deletions architecture/backend/etlOds/views.c4
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ views {
- Queues raw organisation data to Transform Queue
2. TRANSFORM: Transformer Lambda processes raw data
- Transforms ODS FHIR to internal FHIR Organisation format
- Looks up internal UUID via APIM read endpoint -> CRUD APIs
- Looks up internal UUID via DoS Ingest Proxy -> CRUD APIs
- Queues transformed payloads to Load Queue (batch size 10)
3. LOAD: Loader Lambda sends to CRUD APIs
- Sends PUT requests to APIM -> CRUD APIs -> DynamoDB
- Sends PUT requests to DoS Ingest Proxy -> CRUD APIs -> DynamoDB

Each queue has a Dead Letter Queue (DLQ) for failed messages.
See architecture/data-flows.c4 for end-to-end data flow diagrams.
Expand All @@ -24,8 +24,8 @@ views {
include odsTerminologyApi
include ftrs.etlOds.*

include apim.dosReadProxy
include apim.dosWriteProxy
include apim
include apim.dosIngestProxy

include ftrs.apiGateway
include ftrs.crudApis.organisationApi
Expand Down
2 changes: 1 addition & 1 deletion architecture/backend/searchByTriageCode/model.c4
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ model {
}
}

apim.dosReadProxy -> searchByTriageCode "Proxies calls to"
apim.dosSearchProxy -> searchByTriageCode "Proxies calls to"
search -> roadDistance "Queries data from"
search -> ftrs.db.orgTable "Queries data from"
search -> ftrs.db.locationTable "Queries data from"
Expand Down
3 changes: 2 additions & 1 deletion architecture/backend/searchByTriageCode/views.c4
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ views {
include ftrs.db

include ftrs
include apim.dosReadProxy
include apim
include apim.dosSearchProxy
include ftrs.db.orgTable
include ftrs.db.locationTable
include ftrs.db.healthCareServiceTable
Expand Down
50 changes: 27 additions & 23 deletions architecture/data-flows.c4
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ model {
}

// Relationship: External consumer queries DoS Search
healthcareConsumer -> apim.dosReadProxy "Searches for GP endpoints by ODS code"
healthcareConsumer -> apim.dosSearchProxy "Searches for GP endpoints by ODS code"
}

views {
Expand All @@ -36,28 +36,30 @@ views {
- DoS Search (query by ODS code)
'

include apim

// ---- EXTRACT: Fetch from ODS ----
odsTerminologyApi -> ftrs.etlOds.odsExtractor "1. Fetch organisations modified on date (FHIR Bundle)"
ftrs.etlOds.odsExtractor -> ftrs.etlOds.transformQueue "2. Queue raw organisation data for transformation"

// ---- TRANSFORM: Process and lookup UUID ----
ftrs.etlOds.transformQueue -> ftrs.etlOds.odsTransformer "3. SQS event triggers transformer (batch size 10)"
ftrs.etlOds.odsTransformer -> apim.dosReadProxy "4. GET /Organization?identifier=odsOrganisationCode|{code} to lookup UUID"
apim.dosReadProxy -> ftrs.apiGateway "5. Proxy GET request to internal API"
ftrs.etlOds.odsTransformer -> apim.dosIngestProxy "4. GET /Organization?identifier=odsCode to lookup UUID"
apim.dosIngestProxy -> ftrs.apiGateway "5. Proxy GET request to internal API"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.getOrgByOdsCode "6. Route to CRUD API query endpoint"
ftrs.crudApis.organisationApi.getOrgByOdsCode -> ftrs.db.orgTable "7. Query DynamoDB by ODS code index"
ftrs.etlOds.odsTransformer -> ftrs.etlOds.loadQueue "8. Queue transformed FHIR payloads for loading"

// ---- LOAD: Send to CRUD APIs ----
ftrs.etlOds.loadQueue -> ftrs.etlOds.odsLoader "9. SQS event triggers loader (batch size 10)"
ftrs.etlOds.odsLoader -> apim.dosWriteProxy "10. PUT /Organization/{uuid} with FHIR body"
apim.dosWriteProxy -> ftrs.apiGateway "11. Authenticate via JWT and proxy PUT request"
ftrs.etlOds.odsLoader -> apim.dosIngestProxy "10. PUT /Organization/{uuid} with FHIR body"
apim.dosIngestProxy -> ftrs.apiGateway "11. Authenticate via JWT and proxy PUT request"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.updateOrg "12. Route to CRUD API update endpoint"
ftrs.crudApis.organisationApi.updateOrg -> ftrs.db.orgTable "13. Persist updated organisation to DynamoDB"

// ---- DoS Search: External consumer queries ----
healthcareConsumer -> apim.dosReadProxy "14. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}&_revinclude=Endpoint:organization"
apim.dosReadProxy -> ftrs.apiGateway "15. Authenticate and proxy search request"
healthcareConsumer -> apim.dosSearchProxy "14. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}&_revinclude=Endpoint:organization"
apim.dosSearchProxy -> ftrs.apiGateway "15. Authenticate and proxy search request"
ftrs.apiGateway -> ftrs.dosSearch.getOrganizationByOds "16. Route to DoS Search Lambda"
ftrs.dosSearch.getOrganizationByOds -> ftrs.dosSearch.ftrsService "17. Invoke FTRS service layer"
ftrs.dosSearch.ftrsService -> ftrs.db.orgTable "18. Query organisation by ODS code"
Expand Down Expand Up @@ -85,18 +87,19 @@ views {

// Transform stage
ftrs.etlOds.transformQueue -> ftrs.etlOds.odsTransformer "Trigger transformer to process batched messages"
ftrs.etlOds.odsTransformer -> apim.dosIngestProxy "Lookup UUID via Ingest Proxy"
ftrs.etlOds.odsTransformer -> ftrs.etlOds.loadQueue "Queue transformed FHIR payloads"

// Load stage (write path)
ftrs.etlOds.loadQueue -> ftrs.etlOds.odsLoader "Trigger loader to send to CRUD APIs"
ftrs.etlOds.odsLoader -> apim.dosWriteProxy "Send PUT request with JWT authentication"
apim.dosWriteProxy -> ftrs.apiGateway "Proxy authenticated request to internal gateway"
ftrs.etlOds.odsLoader -> apim.dosIngestProxy "Send PUT request with JWT authentication"
apim.dosIngestProxy -> ftrs.apiGateway "Proxy authenticated request to internal gateway"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.updateOrg "Route to organisation update handler"
ftrs.crudApis.organisationApi.updateOrg -> ftrs.db.orgTable "Persist organisation changes to DynamoDB"

// Read path (DoS Search)
healthcareConsumer -> apim.dosReadProxy "Search for GP endpoints by ODS code"
apim.dosReadProxy -> ftrs.apiGateway "Proxy authenticated search request"
healthcareConsumer -> apim.dosSearchProxy "Search for GP endpoints by ODS code"
apim.dosSearchProxy -> ftrs.apiGateway "Proxy authenticated search request"
ftrs.apiGateway -> ftrs.dosSearch.getOrganizationByOds "Route to DoS Search endpoint"
ftrs.dosSearch.getOrganizationByOds -> ftrs.dosSearch.ftrsService "Process FHIR search request"
ftrs.dosSearch.ftrsService -> ftrs.db.orgTable "Query organisation and endpoints from DynamoDB"
Expand All @@ -111,11 +114,11 @@ views {
dynamic view writePathFlow {
title "Write Path: Data Ingestion Flow"
description '
How data enters the system via the write path (ETL → APIM → CRUD APIs → DynamoDB):
How data enters the system via the write path (ETL → DoS Ingest Proxy → CRUD APIs → DynamoDB):

1. Extractor fetches from ODS API, queues raw data
2. Transformer processes data, looks up UUID, queues FHIR payloads
3. Loader sends PUT requests via APIM to CRUD APIs
2. Transformer looks up UUID via DoS Ingest Proxy, queues FHIR payloads
3. Loader sends PUT requests via DoS Ingest Proxy to CRUD APIs
'

// Extract stage
Expand All @@ -124,14 +127,15 @@ views {

// Transform stage
ftrs.etlOds.transformQueue -> ftrs.etlOds.odsTransformer "3. SQS triggers transformer Lambda"
ftrs.etlOds.odsTransformer -> ftrs.etlOds.loadQueue "4. Transform to FHIR format, queue for loading"
ftrs.etlOds.odsTransformer -> apim.dosIngestProxy "4. GET /Organization to lookup UUID"
ftrs.etlOds.odsTransformer -> ftrs.etlOds.loadQueue "5. Transform to FHIR format, queue for loading"

// Load stage
ftrs.etlOds.loadQueue -> ftrs.etlOds.odsLoader "5. SQS triggers loader Lambda"
ftrs.etlOds.odsLoader -> apim.dosWriteProxy "6. PUT /Organization/{uuid} with authenticated JWT"
apim.dosWriteProxy -> ftrs.apiGateway "7. Validate JWT and route to internal API"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.updateOrg "8. Invoke update handler with FHIR payload"
ftrs.crudApis.organisationApi.updateOrg -> ftrs.db.orgTable "9. Update organisation record in DynamoDB"
ftrs.etlOds.loadQueue -> ftrs.etlOds.odsLoader "6. SQS triggers loader Lambda"
ftrs.etlOds.odsLoader -> apim.dosIngestProxy "7. PUT /Organization/{uuid} with authenticated JWT"
apim.dosIngestProxy -> ftrs.apiGateway "8. Validate JWT and route to internal API"
ftrs.apiGateway -> ftrs.crudApis.organisationApi.updateOrg "9. Invoke update handler with FHIR payload"
ftrs.crudApis.organisationApi.updateOrg -> ftrs.db.orgTable "10. Update organisation record in DynamoDB"

autoLayout TopBottom
}
Expand All @@ -144,11 +148,11 @@ views {
title "Read Path: DoS Search Query Flow"
description '
How external consumers query the system via the read path:
Consumer -> APIM -> API Gateway -> DoS Search -> DynamoDB -> FHIR Bundle response
Consumer -> DoS Search Proxy -> API Gateway -> DoS Search -> DynamoDB -> FHIR Bundle response
'

healthcareConsumer -> apim.dosReadProxy "1. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}&_revinclude=Endpoint:organization"
apim.dosReadProxy -> ftrs.apiGateway "2. Validate API key and proxy to internal gateway"
healthcareConsumer -> apim.dosSearchProxy "1. GET /Organization?identifier=https://fhir.nhs.uk/Id/ods-organization-code|{code}&_revinclude=Endpoint:organization"
apim.dosSearchProxy -> ftrs.apiGateway "2. Validate API key and proxy to internal gateway"
ftrs.apiGateway -> ftrs.dosSearch.getOrganizationByOds "3. Route to DoS Search Lambda handler"
ftrs.dosSearch.getOrganizationByOds -> ftrs.dosSearch.ftrsService "4. Call FTRS service to retrieve organisation"
ftrs.dosSearch.ftrsService -> ftrs.db.orgTable "5. Query DynamoDB by ODS code index"
Expand Down
Loading