A layered architecture for cross-resource joins and Dublin Core discovery in CSAPI #40
Sam-Bolling
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Discovery, Enrichment, and Composition in OGC API – Connected Systems: A Layered Architecture for Cross-Resource Data Access
Introduction
OGC API – Connected Systems (CSAPI) is a standards-based interface: it gives a server a modern, consistent way to expose systems, observations, and related data for discovery and access. It is not a mandate for how a client system organizes its own domain logic, and adopting CSAPI as a data source does not require a client to restructure itself around the standard's resource model. A client system that consumes CSAPI data continues to be governed by its own operational requirements, exactly as it would with any other external data source. Connecting an independently designed system to an external data source without surrendering that system's own architecture is a well-established discipline with a name — enterprise integration patterns — built from decades of practice connecting systems that were never designed to share a single architecture (Hohpe and Woolf 2003). That discipline covers the general case by default, not the exception: a client may be connected to a single CSAPI server or to several at once, potentially operated by different organizations that never coordinated with each other.
CSAPI's design reflects a well-precedented choice within that discipline: it organizes data into discrete, independently addressable resource types rather than a single unified source. This design is efficient for operating on any one resource type, and it is well understood outside CSAPI specifically: industry guidance on REST API design names this trade-off directly, observing that APIs built around many small, focused resources expose a large number of small resources are known as chatty, with the standard prescription being for clients to compose views by following the hyperlinks and identifiers each resource provides rather than expecting the server to pre-assemble them (Microsoft, n.d.). GitHub's REST API follows this same convention at production scale, instructing integrators that you should not try to parse these URLs or predict resource structure, and to instead follow the fields and link relations a response provides (GitHub, n.d.). Composing a client-side view from more than one CSAPI resource type is the same integration work — described later in this paper using named patterns from that discipline — that any client consuming a well-designed, resource-oriented API is expected to do. Hohpe and Woolf built their patterns explicitly as a pattern language, in the sense Christopher Alexander gave that term: elements meant to be selected and combined for a concrete problem, not applied one per problem in isolation — a practice the authors themselves describe as "careful selection and combination of patterns based on specific messaging needs" (Zimmermann et al. 2016). This paper follows that practice directly, and Part III makes the resulting composition explicit rather than incidental.
Overview: Three Problems, One Shared Architecture
This paper addresses three problems that arise because CSAPI organizes data into discrete, independently addressable resource types rather than a single unified source. Past that shared origin, they differ in shape, in the technique that resolves them, and in which standard — if any — supplies the solution.
Cross-Resource Joins Within CSAPI: a family of problems, not a single one. A client wants to enrich a resource of one type with descriptive detail that lives on a different, related resource type — an Observation enriched with the System that produced it, a Command enriched with its target System, a Datastream enriched with the Procedure its System implements, or a System annotated with the Deployments it has been or currently is part of. Each instance hits the same technical dilemma: CSAPI does not model these resources as self-contained, so resolving the reference means a traversal the standard doesn't yet support as a single query. It is a family because the shape varies — which side is high-volume fact-type data, which is low-volume and descriptive, and whether resolving the reference takes a bare ID or something more, like a timestamp — and Part I develops all four recognizable variants in turn, at equal depth, rather than treating any single one as representative of the rest.
Discovery Across All Resource Types: a requirement whose solution, it turns out, is not fully independent of enrichment. Building a single, browsable discovery view across every resource type a deployment exposes is not a variant of the enrichment problem above: a client wants to browse and search, not attach an attribute to a resource it already has. It is solved by a different standard, OGC API – Records, a Dublin Core–style catalog interface built for exactly this. But producing a Record worth having recruits the same resolution mechanism enrichment needs, applied to the resource being cataloged rather than to a UI row — a dependency Part III develops once all three problems are on the table.
Filtering Fact Data by Dimension-Side Conditions. Enrichment resolves a fact row's dimension reference in order to attach an attribute to it — the operation dimensional-modeling practice calls a join, a distinction Part I develops in full before working through how it plays out four ways. The same join can be pointed in a different direction: instead of attaching a dimension attribute to a fact row a client already has, a dimension condition can be used to decide which fact rows come back in the first place. Data warehousing has names for this — a slice for a single dimension condition, a dice for more than one (Codd, Codd, and Salley 1993) — and it is exactly what a client wants when three Systems deployed together for a shared purpose need to be monitored as a unit: every Observation those Systems have produced, filtered by Deployment membership, without needing anything else about the Systems themselves. A dice extends the same idea to a compound condition, such as every Observation from Systems that are both part of a given Deployment and running a given Procedure. The technique is a close cousin of enrichment; what changes is where the resolved dimension data is used — to decorate a returned row, or to decide whether the row is returned at all. CSAPI's native support for this case is narrower than for either of the other two, and resolving it is developed in Part IV, where the same composition layer built for enrichment turns out to handle it as well.
Why Three Problems Share One Foundation. These three problems could be solved independently, with three unrelated pieces of infrastructure. What makes solving them together necessary, not merely convenient, is a dependency chain rather than a shared coincidence: producing a catalog entry worth having recruits the same resolution mechanism enrichment needs, and the third problem's resolution begins by querying that same catalog for its qualifying dimension set. Each problem's solution is a working ingredient in the next one's, not just a neighbor drawing on the same well. Independent of that chain, building the shared registry this way is also more efficient than the alternative: one indexing pass produces both outputs Layer 2 needs, rather than two pipelines each scanning the same dimension data separately. Part III develops the dependency chain directly; Part IV uses it, together with the registry's efficiency, as the basis for a single layered architecture — necessary in its ordering, and efficient in its construction.
Part I: Cross-Resource Joins Within CSAPI
The Use Case
CSAPI is not a single flat data source; it is organized around discrete resource types — Systems, Deployments, Procedures, Datastreams, Observations, and Commands — each with its own collection endpoint (Robin 2025a). Because of this organization, any client view that draws descriptive detail from a second resource type to enrich or contextualize records from a first constitutes a distinct integration requirement, and a CSAPI deployment of realistic scope will accumulate a number of such requirements over time: an Observation enriched with the System that produced it, a Command enriched with its target System, a Datastream enriched with the Procedure its System implements, or a System Event enriched with the System it describes. Each pairing is an instance of the same general use case type — a resource of one type displayed alongside descriptive attributes that live on a different, related resource type — rather than an isolated request.
The diagram shows these pairings as edges of one underlying resource graph rather than as unrelated cases: Observation, Command, and System Event each reference a resource one or two hops away, and every such reference is a candidate for the same kind of enrichment requirement. Deployment is included as well, but with a dashed edge to mark a different shape of relationship: it lists the Systems deployed during a given period, a one-to-many, time-scoped lookup rather than the single-reference pattern the other edges share.
Before working through these four types individually, it helps to name a distinction that organizes all of them. Dimensional-modeling practice — the discipline data warehousing draws its own vocabulary from — distinguishes facts from dimensions: facts are specific, well-defined attributes recorded repeatedly and at high volume, while dimensions are the surrounding descriptive context needed to interpret them, comparatively low in volume and slow-changing (Kimball 2003a). Mapped onto CSAPI's own resource types: Observations, Commands, and System Events are fact-like, appended continuously at high volume; Systems, Procedures, Deployments, and Datastreams are dimension-like, comparatively few in number and descriptive. Each of the four types below is named for which side of this distinction each resource in the pairing falls on, and whether the reference between them is a single value or something more — the vocabulary this Part uses throughout, and the same distinction Part III returns to when explaining why enrichment, discovery, and selection share one architecture.
Rather than developing one representative pairing and treating the rest as a footnote, this Part works through all four recognizable shapes of this relationship at equal depth, each with its own worked example grounded in one running scenario. They are numbered here to match the order they are presented in, not an arbitrary classification order: Type 1 — an Observation enriched with the System that produced it — opens the discussion, since a discovery table of Observations is the natural entry point for the investigation that carries through the rest of this Part, and the friction it exposes is the most common instance of this problem a CSAPI client will encounter. Types 2, 3, and 4 follow directly from where Type 1 leaves off, each picking up a different facet of the same investigation rather than starting over with an unrelated case.
Type 1 — Fact-to-Dimension, Single Reference
The most common relationship type in practice pairs a high-volume fact resource with a single low-volume dimension resource. Consider Priya, a water-quality analyst at the Calvert Sound Observing Network, scanning a table of recent Observations for anomalies. Without the producing System's name attached to each row, confirming that a suspicious reading came from the Herring Neck buoy rather than Tanner Shoal or Osprey Point means leaving the table entirely — opening a separate System resource, for every row in question — before returning to the table to continue the scan. The enrichment this section develops keeps Priya's attention where it belongs: on the table.
The technical cause. An Observation does not carry System detail directly; it references a parent Datastream, which is in turn associated with exactly one System, so resolving an Observation's producing System is a fixed, two-hop traversal rather than a search (Robin 2025b). Observation resources are always associated with a parent Datastream, and identifying details that would otherwise repeat on every observation — including the link back to the observing System — are deliberately left off the Observation and only provided at the datastream level. CSAPI does not model an Observation as a self-contained resource, and the standard does not yet define a built-in way to request that join in one call: certain types of join queries may be defined in a future extension (Open Geospatial Consortium Connected Systems Standards Working Group, n.d.).
Resolved naively, this becomes a classic N-plus-one problem for every row in a table Priya is looking at, in which each row triggers its own round trip to resolve descriptive metadata — a scaling and latency issue common to any resource-oriented or microservice API, not specific to CSAPI (Richardson, n.d.).
Before and after. The following is an illustrative, representative shape — not reproduced from the specification — of what a server might return for a single Observation from Herring Neck's Water Temperature Datastream:
{ "id": "obs-herring-neck-0091", "datastream@id": "ds-herring-neck-temp", "phenomenonTime": "2026-07-04T14:30:00Z", "result": 21.4 }Nothing in this record names the System that produced the reading; Priya has that only after a second lookup on
ds-herring-neck-temp. The row her discovery table actually wants looks more like this composed, illustrative shape:{ "observationId": "obs-herring-neck-0091", "phenomenonTime": "2026-07-04T14:30:00Z", "result": 21.4, "systemId": "sys-herring-neck", "systemName": "Calvert Sound Buoy — Herring Neck" }Producing the second shape from the first is exactly the two-hop traversal described above, performed once per row unless the shared registry this paper develops intervenes.
Where else this shape recurs. The same structural cause applies to other pairings in CSAPI's resource model, since it follows from how CSAPI separates resource types rather than from anything specific to Observations or Systems. A Command resource reaches its target System through a parent ControlStream — the same two-hop shape as Observation reaching System through a parent Datastream. A System Event references its parent System directly. Any client view that spans these resource types will encounter the same multi-hop traversal and the same absence of a native join query.
Caption (structured user story):
The wireframe shows the discovery table in two states. Before: four columns (Observation ID, Phenomenon Time, Result, Datastream), the Herring Neck row flagged as a suspected anomaly, and a disabled "View producing System" button gesturing at the extra step Priya would otherwise need. After: a System column inserted between Result and Datastream, the Herring Neck row now reading directly "Calvert Sound Buoy — Herring Neck," and no button — nothing left to click through to.
Type 2 — Dimension-to-Dimension, Single Reference
The simplest relationship type links two low-volume, descriptive resources through a direct reference. Continuing from Type 1: having identified Herring Neck as the source of the anomalous reading, Priya clicks through to that System's profile page — the natural next step from a discovery table, and the first screen in this reference UI that isn't itself a table of Observations. The profile lists Herring Neck's Datastreams, and each one raises the same question Type 1 already solved for Observations, one level up: which instrument and channel actually produced this data.
The technical cause. A Datastream's relationship to its Procedure is a single, direct reference — "the procedure used to generate observations in the datastream," modeled as a single Procedure resource, optionally present (Robin 2025b). Because both sides of this relationship are dimension-type data, resolving it never touches CSAPI's high-volume Observation or Command endpoints — this is the least demanding relationship type in the family. The same shape recurs one resource over: a ControlStream's relationship to its Procedure is defined identically, a single Procedure resource, optionally present (Robin 2025b).
Before and after. The following is an illustrative, representative shape — not reproduced from the specification — of what a server might return for Herring Neck's two Datastreams before resolution:
[ { "id": "ds-herring-neck-temp", "name": "Water Temperature Datastream", "observedProperty": "Temperature", "procedure@id": "proc-exo2-temp" }, { "id": "ds-herring-neck-do", "name": "Dissolved Oxygen Datastream", "observedProperty": "Dissolved Oxygen", "procedure@id": "proc-exo2-do" } ]Priya sees two Datastreams and two bare procedure identifiers, with no way to tell from this screen alone that both channels come from the same physical instrument. The profile page Priya actually wants resolves both references:
[ { "id": "ds-herring-neck-temp", "name": "Water Temperature Datastream", "observedProperty": "Temperature", "procedureName": "YSI EXO2 Multiparameter Sonde — Water Temperature Channel" }, { "id": "ds-herring-neck-do", "name": "Dissolved Oxygen Datastream", "observedProperty": "Dissolved Oxygen", "procedureName": "YSI EXO2 Multiparameter Sonde — Dissolved Oxygen Channel" } ]Producing the second shape from the first is a single ID-keyed lookup against the shared registry per row — the same DataLoader-style batching Type 1 already established, resolving a different association on a different resource type.
A related case, handled by the same mechanism, worth naming rather than glossing over. Not every dimension-to-dimension reference is one-to-one. A System's own relationship to the Procedures it implements is a list, not a single reference — "the Procedures that can be implemented by the System," modeled as a list of Procedure resources (Robin 2025a). This is a materially different claim than the essay's own earlier description of this relationship as a single reference, and it is worth being precise about the correction: resolving Herring Neck's own procedures list still reduces to the same registry lookup described above, simply returning a set instead of a scalar. Nothing about the resolution mechanism changes; only the cardinality of the result does.
Caption (structured user story):
The wireframe shows Herring Neck's profile page in two states, continuing directly from Type 1's discovery table via a breadcrumb ("Recent Observations > Calvert Sound Buoy — Herring Neck"). Before: a Datastream table with Datastream name, Observed Property, and an unresolved Procedure column showing only a bare reference. After: the same table with each Datastream's Procedure resolved by name — revealing, incidentally, that both Datastreams share the same physical instrument (the EXO2 sonde), just different channels — information the raw references alone did not make visible.
Type 3 — Dimension-to-Dimension, Multi-Valued and Temporal
Not every dimension-to-dimension relationship is a single reference. Continuing on Herring Neck's profile page — the same screen Type 2 just resolved — Priya scrolls to its Deployment History, wanting to confirm which Deployment the buoy was actually part of on July 4th, the date of the anomalous reading.
The technical cause. A System's relationship to the Deployments it has been part of is a list, not a single reference, and each Deployment carries a mandatory time period:
validTime, "the time period during which the systems are deployed," required on every Deployment resource (Robin 2025a). A System can appear in more than one Deployment across its lifetime, and determining which one applies at a particular moment requires filtering by that period, not just retrieving a fixed value — a materially different query shape than Type 2's plain reference. The standard supports resolving this directly: a nested endpoint exposes a System's Deployments (Robin 2025a), and the reverse query — filtering Deployments by an associated System — is supported as a dedicated parameter on the Deployment endpoint itself (Robin 2025a).Before and after. The following is an illustrative, representative shape — not reproduced from the specification — of what a server might return for Herring Neck's deployment history before resolution:
[ { "deployment@id": "dep-cs-winter25" }, { "deployment@id": "dep-cs-summer26" } ]Two bare references, with nothing to indicate which one — if either — covers July 4th. The resolved view Priya actually wants looks like this:
[ { "deploymentId": "dep-cs-summer26", "name": "Calvert Sound Summer 2026 Deployment", "validTime": {"start": "2026-05-01", "end": "2026-10-15"} }, { "deploymentId": "dep-cs-winter25", "name": "Calvert Sound Winter 2025 Deployment", "validTime": {"start": "2025-11-01", "end": "2026-04-30"} } ]The anomalous reading's timestamp — 2026-07-04T14:30:00Z — falls inside the Summer 2026 Deployment's
validTimeand outside the Winter 2025 Deployment's. Resolving that correctly requires comparing the Observation's own timestamp against each candidate Deployment's period; an ID-only lookup, of the kind sufficient for Type 2, cannot make this determination on its own.A distinctive case, not a representative one. Unlike Types 1, 2, and 4, this shape does not recur elsewhere in CSAPI's resource model. A Datastream's or ControlStream's own relationship to Deployment is a single reference — "the deployment during which the datastream was generated," one Deployment resource (Robin 2025b) — the same shape as Type 2, not this one. The multi-valued, temporal shape belongs specifically to how Deployment itself is modeled: a resource that lists the Systems it covers over a defined period, rather than a property any other resource type shares. Worth stating plainly rather than forcing a parallel structure onto this section that the resource model doesn't actually support: Type 3 is a singular case in this family, not one example among several.
This shape is also worth noting as normatively solid but not guaranteed in practice: a Deployment's list of covered Systems (
deployedSystems) is a Required association in the standard (Robin 2025a), not merely Optional — a stronger footing than several other associations discussed elsewhere in this paper. Independent field testing against OpenSensorHub, the most widely used open-source CSAPI reference implementation, has documented that this Required association is silently dropped in practice, forcing a workaround pattern of one Deployment paired with one System rather than relying on the list (OS4CSAPI 2026). This is the same shape of gap this paper found earlier for a different Deployment-related capability — real and Required in the normative text, absent from what a client actually encounters against a live server — and it reinforces rather than complicates the point: knowing what the standard requires and knowing what a given deployment of it actually does remain two different questions.Caption (structured user story):
The wireframe continues directly from Type 2's Datastream table, adding a Deployment History section to the same profile page. Before: two bare deployment references, no dates, no way to tell which applies to July 4th. After: both Deployments resolved with names and valid-time ranges, with Summer 2026 — the one whose period actually contains the reading's timestamp — flagged, resolved by date comparison rather than by list order.
Type 4 — Fact-to-Fact via a Conformed Dimension
Back on the discovery table Type 1 resolved, Priya wants one more thing about the flagged Herring Neck reading: did anyone act on it? An operator noticing the same anomaly might have sent a command to recalibrate the sensor. Confirming that means correlating two fact-type resources with each other — an Observation and a Command — rather than enriching one with a dimension attribute, which is a different kind of question than Types 1 through 3 have asked so far.
The technical cause. Observations and Commands are not linked to one another directly in CSAPI; they are related only through the System they share, optionally narrowed to a common time window. Data warehousing practice has a specific term for this pattern: a drill-across query, defined as a case where all fact tables in a drill-across query must use conformed dimensions, resolved by issuing separate requests against each fact source and merging the results on the shared dimension key (Kimball 2003b). System serves as the conformed dimension here — the same role it plays in Types 1 through 3, now connecting two fact streams instead of connecting a fact to a dimension attribute.
Before and after. The following is an illustrative, representative shape — not reproduced from the specification — of what a server holds for the Observation and a nearby Command, as two entirely separate resources:
{ "observation": { "id": "obs-herring-neck-0091", "datastream@id": "ds-herring-neck-temp", "phenomenonTime": "2026-07-04T14:30:00Z", "result": 21.4 }, "command": { "id": "cmd-0512", "controlStream@id": "cs-herring-neck-control", "issueTime": "2026-07-04T14:42:00Z", "commandType": "RecalibrateSensor" } }Nothing here connects the two records to each other; each references its own System only indirectly, through a different parent collection (Datastream for the Observation, ControlStream for the Command). Priya wants to see them as one correlated picture:
{ "observationId": "obs-herring-neck-0091", "phenomenonTime": "2026-07-04T14:30:00Z", "result": 21.4, "relatedCommand": { "commandId": "cmd-0512", "commandType": "RecalibrateSensor", "issueTime": "2026-07-04T14:42:00Z" }, "systemId": "sys-herring-neck" }Producing the second shape from the first means resolving both records' shared System — Observation through Datastream, Command through ControlStream — and merging on that shared key, rather than following any reference that connects the two records directly, because none exists.
Where else this shape recurs. The correlation is not limited to Observation and Command. A System Event — CSAPI's own record of things like calibrations, configuration changes, or relocations — references its System directly (Robin 2025b), and correlates with either fact type the same way. In Herring Neck's case, the Recalibrate Sensor command would plausibly be followed by a System Event of the standard's own "Calibration" type, "system was calibrated or recalibrated" (Robin 2025b), recorded once the recalibration completed — a third fact-type resource, correlatable with both the Observation and the Command through the same shared System, using the identical mechanism.
What this section leaves for Part IV. Resolving this correlation — issuing the separate requests, merging on the shared key, handling the case where many Systems rather than one qualify — is a composition-layer concern, the same split Type 1 already established between stating a problem here and resolving it there. Part IV develops the mechanism as an application of the Scatter-Gather pattern.
Caption (structured user story):
The wireframe shows the Herring Neck row from Type 1's table with an expandable "Related Activity" section. Before: collapsed, with a note that Commands and System Events are separate collections with no direct link back to this Observation — confirming a response requires a manual, separate search. After: expanded, showing the Recalibrate Sensor command issued twelve minutes after the reading, and the resulting Calibration System Event recorded shortly after that — both surfaced through the shared System, not through any reference on the Observation itself.
Short Synthesis
Each of the four relationship shapes developed above exists because CSAPI deliberately keeps its resource types lean and separate — the same design choice "The Use Case" opened with. None of the four is available today as a single call: Type 1 requires a two-hop traversal through an intermediate Datastream; Type 2 requires following a direct reference that simply isn't inlined on the resource a client already has; Type 3 requires that same kind of reference resolved against a timestamp, since more than one candidate can apply; and Type 4 requires correlating two resources that reference no third resource in common by name, only by an indirect, shared System. What none of them require is any change to CSAPI itself — every one of these is a client-side integration problem, not a defect in the standard, which is why the architecture developed in Part IV is additive infrastructure rather than a proposed revision to the API.
Where the four differ matters as much as what they share. Type 1 and Type 4 involve CSAPI's fact-type resources — Observations and Commands — and inherit the volume and write-frequency concerns that come with them; Type 2 and Type 3 stay entirely on the dimension side, where volume is never a concern but the shape of the reference itself is. Type 3 alone requires a timestamp to resolve correctly. And of the four, only Type 3 turned out to be a genuinely singular case in CSAPI's resource model, without a second instance elsewhere to generalize to — Types 1, 2, and 4 each recur at least once more among CSAPI's other resource types, which is what makes them representative examples of a family rather than isolated cases.
Taken together, these four shapes are instances of one problem — enrichment — not four separate problems needing four separate solutions. That distinction matters for what comes next. Discovery, developed in Part II, is not a fifth variant of this family; it is a different requirement entirely, solved by a different standard, though its own solution turns out not to be fully independent of enrichment's. Selection, developed in Part IV, is closer in mechanism to enrichment than it first appears, but is not a fifth type of this family either — it uses the same join, pointed in a different direction. Part III takes up why these three — one family and two standalone problems — end up sharing a single architecture rather than three independent ones.
Part II: Discovery Across All Resource Types
From One Join to Many: The Dublin Core Use Case
Confirming the Herring Neck anomaly's full context meant tracing four separate relationships one at a time — its Datastreams, its Deployment history, the Command that may have answered it. Priya's next need, closing out her incident report, is different in kind rather than degree: she wants to browse and search across every resource type Calvert Sound Observing Network exposes in one place, rather than querying Systems, Procedures, Deployments, and Datastreams separately. This is not a variant of the enrichment problem developed in Part I — a client wants to browse and discover, not attach an attribute to a resource it already has — though, as Overview already noted, building a good answer to it turns out not to be fully independent of enrichment either. It maps to a different part of the OGC API family: OGC API – Records.
Records addresses the fact that CSAPI-style resource APIs are built to operate on data, not to browse across it. Its atomic building block, the Record, is defined as an atomic unit of information in a catalog that provides a description (i.e. metadata) about a resource that the provider of the resource wishes to make discoverable, built from a small set of properties common across all resource types and extensible per resource type as needed (Vretanos 2025a). This common-core-plus-extension design reflects established lineage: Records is explicitly designed to be compatible but not conformant with the OGC Catalogue Service for the Web (CSW) Standard, and CSW's own metadata model is based on an extension of Dublin Core (Vretanos 2025a). Records carries that same common-denominator-across-resource-types design forward into a modern, OpenAPI-based interface. This is a specific instance of a general integration pattern, the Canonical Data Model, in which a common representational format is defined once and reused across otherwise independent systems rather than translating between every pair of them individually (Hohpe and Woolf 2003). This same argument appears independently in service-oriented architecture literature: Erl distinguishes services that maintain their own custom-tailored schemas — clean in isolation, but capable of undermining an architecture's overall interoperability — from services that instead reference one shared, centrally defined schema per information set, which Erl argues establishes "a meaningful extent of intrinsic interoperability" before any composition is attempted (Erl 2008, 134–137). Records' common Record schema, referenced rather than duplicated by CSAPI, Features, and Coverages, is this same pattern — Schema Centralization — applied across the OGC API family.
Records' actual core schema is deliberately thin. Checked directly against the standard's own tables: of its complete set of core properties — covering identification, timestamps, language, links, and resource-level descriptors including title, description, spatial and temporal extent, keywords, external identifiers, formats, contacts, license, and rights — only the record's own identifier is mandatory. This is a deliberate design choice, not an oversight: the standard's own text states plainly that its core properties "can be extended as required by specific communities of interest and/or use cases" (Vretanos 2025a).
Populating even this minimal core well is not automatic. A Datastream's own resource carries no name for the System that produced it, only a reference — the same by-reference pattern Part I develops at length. Building a Record worth having for a Datastream means resolving that reference first: the Content Enricher mechanism already established for a single association, or Composed Message Processor where a Record needs to pull in more than one — a System's name, a Procedure's name, a Deployment's dates — before it is worth publishing at all.
Calvert Sound Observing Network needs more from a Record than the bare core provides, for a reason specific to compiling Priya's incident report: knowing not just what a resource is, but who is answerable for it. Records already includes an optional property for exactly this kind of information — a list of contacts, each tagged with whatever role fits, with no fixed vocabulary built into the schema itself. Checked directly against the standard's own definition, that openness is deliberate: rather than mandating a specific set of role values, the standard points communities toward existing external vocabularies, such as ISO 19115's contact-role codelist, where a more formal listing is needed. The standard's own editor, answering an implementer's question on the working group's mailing list, confirmed the same pattern directly: a contact entry can consist of an organization name alone, with no individual person required, tagged with whatever role applies (Vretanos 2025b). That same exchange shows roles such as creator, publisher, and contributor already in informal use, borrowed from Dublin Core, before any single community adds its own.
The profile Calvert Sound adopts settles on four such roles and fields, adapted from a real, if otherwise unrelated, precedent: the U.S. Department of Defense's own baseline metadata guidance, which mandates ten governance fields — including Originator, Custodian, Authorization Reference, Disclosure & Releasability, and Handling Restrictions — for the same underlying reason, ensuring anyone consuming the data can identify who is answerable for it (U.S. Department of Defense 2023). Nothing about this essay's civilian monitoring network has any connection to that document's actual defense context; it is used here only because it documents, clearly and specifically, a real profile built on the same governance need Calvert Sound has. Two of its fields — Originator and Custodian — need no new property at all, only Records' existing contacts list, populated with those two role values. Three more are genuine additions, adapted for a civilian setting: an Authorization Reference recording the permit or grant governing a given monitoring activity; a Disclosure & Releasability tier — Public, Research Partners Only, or Embargoed Pending QA/QC — reflecting the provisional-data practice real monitoring networks already follow; and Handling Restrictions, covering cases such as a SamplingFeature's location being deliberately masked where it coincides with a sensitive habitat. One field from the DoD original, Security Classification, is deliberately left out: civilian environmental data has no classification system to map onto, and forcing one in for the sake of symmetry would misrepresent what the profile is actually for.
One distinction is worth stating plainly, since it has tripped up implementers working with the standard directly, not just a hypothetical reader of this essay. Records'
timeproperty describes a resource's spatio-temporal extent — the period a Datastream covers, for instance — not when the Record itself was created or last updated, whichcreatedandupdatedalready handle separately. The working group has had to clarify this exact point in response to an implementer's question, since Dublin Core's original "Date" element conflates several senses — creation, publication, update — that Records deliberately keeps apart from its resource-extent property (Little 2025).Deployment Patterns
The finalized Records standard — OGC 20-004r1, approved April 2025 and published May 2025 — defines three catalog patterns (Vretanos 2025a): Crawlable Catalog, Searchable Catalog, and Local Resources Catalog. The first two are true top-level patterns; the third, checked directly against the standard's own text, is defined as a special use case of the Searchable Catalog rather than a fully independent pattern — it satisfies Searchable Catalog's own requirements and adds one further condition: the catalog's endpoint points at the deployment's own local resources rather than a separately curated collection.
Crawlable Catalog gets the least attention in what follows, and that is a stated choice, not an oversight: it is a collection of web-accessible static files at fixed URLs, browsable by a search-engine crawler or a human following links, with no query parameters at all. That is the right shape for making a catalog indexable by external search engines; it is the wrong shape for Priya's interactive discovery UI, which needs to filter and sort in real time. Nothing about Crawlable Catalog is deficient — it simply answers a different question than the one this essay's use case asks.
Searchable Catalog is the pattern that actually does the work for an interactive discovery UI. Its core query parameters, checked directly against the standard's own table, include
bboxanddatetime(both inherited from OGC API – Features),qfor free-text search,typefor filtering by resource type,idsandexternalIdsfor identifier-based lookup, andprop=valuefor any other queryable property (Vretanos 2025a) — withsortby, defined as a separate, composable requirements class, available for ordering results (Vretanos 2025a). Priya's own incident-report catalog, browsed rather than queried by ID, is a natural fit for combining several of these at once: finding everything related to the Herring Neck anomaly published or updated during the deployment window, sorted for easy scanning —GET /items?q=temperature&datetime=2026-05-01/2026-10-15&sortby=title
— is a different combination of the same query parameters from the one Local Resources Catalog's own worked example below uses, which is the point: Searchable Catalog's parameter set is shared and composable, not a fixed menu of pre-built queries.
Local Resources Catalog, as a special case of Searchable Catalog, is the most directly relevant to this essay's own use case, since the mechanism it describes maps closely onto the single-endpoint, multi-resource-type discovery requirement. The pattern originated as a working-group proposal to allow cataloging of a deployment's own collections and processes directly, and its design rationale was documented in the standard's public review process. In that discussion, the standard's editor described building a single searchable catalog by crawling a deployment's entire resource tree and harvesting metadata from every resource type into one collection, then narrowing results with a type predicate — for example, retrieving only feature collections that satisfy a bounding-box and free-text search by combining a type=collection parameter with bbox and q parameters against one endpoint (Vretanos 2024):
GET /collections?type=collection&bbox=-71.2,42.3,-70.9,42.5&q=temperature
The request above is illustrative rather than reproduced from the source, but it shows concretely what "one endpoint, many resource types" means in practice. The stated rationale was that this is an easier way to locate local resources since you only need to hit a single endpoint (Vretanos 2024), rather than requiring a client to understand and separately query every resource type a deployment exposes. The Local Resources Catalog requirements class was carried through public comment into the final, approved standard alongside Searchable and Crawlable Catalog (Vretanos 2025a).
Records as a Metadata Layer Over Other OGC APIs
The Local Resources Catalog pattern is documented as extending resource collections defined by other OGC API standards, not only resources native to Records itself. OGC's developer documentation for the pattern notes that the /collections endpoint construct is shared across the OGC API family, including Features and Coverages, and describes the Local Resources Catalog pattern as one that extends other OGC APIs endpoints to behave like catalogs, naming processes and coverage scenes explicitly as additional endpoint types that can be repurposed this way (Open Geospatial Consortium, n.d.-a). Records here functions as a metadata and search layer laid over collections that are natively defined and served by separate OGC API standards — the same relationship a CSAPI deployment's Systems, Procedures, Deployments, and Datastreams would have to a Records-based catalog.
The diagram generalizes the point beyond this essay's own use case: the same Local Resources Catalog pattern applied to a CSAPI deployment's Systems and Datastreams applies equally to a Features deployment's feature collections or a Coverages deployment's coverage scenes, all cataloged into one searchable index without duplicating the underlying data. The same generalization holds in the other direction as well: nothing about the pattern restricts it to one server per standard, and a catalog built this way can just as naturally harvest from several independently operated CSAPI servers as from one, which is the scenario this paper's architecture is scoped to support.
This design intent is corroborated by the standard's own relationship to OGC API – Features. Records is described in OGC's implementation guidance as leveraging OGC API - Features as a baseline, sharing its endpoint and request/response structure for the Searchable and Local Resources Catalog patterns, with the specific value it adds being a common record schema and queryable set that allows for interoperability and integration across catalogs so that resources originating from different, independently governed APIs can be described and searched in a single consistent shape (Open Geospatial Consortium, n.d.-b). Taken together, these sources indicate that cataloging heterogeneous resource types drawn from more than one OGC API standard under a single searchable schema is the specific problem the Local Resources Catalog pattern was designed to solve, rather than an incidental capability. Independent of this design evidence, the standard also carries a substantial adoption record — including use as part of the World Meteorological Organization's WIS2 baseline, where a working-group participant noted that 193 countries voted and approved to use OGC API - Records (Kralidis 2024) — which indicates the pattern is deployed at scale rather than purely theoretical.
A resource's own attributes can do some of this summarizing work before a Record is even built. A CSAPI Datastream's
phenomenonTime,resultTime, andobservedPropertiesattributes are mandatory and normatively required to be computed by the server from the Observations linked to that Datastream (Robin 2025b) — the temporal and property extent of a fact stream, surfaced at the dimension level. This is structurally identical to how a STAC Collection carries anextentandsummariesfield describing the temporal, spatial, and property range of the Items beneath it, giving users a sense of what a Collection contains without inspecting every Item individually — the same standard's own best-practices guidance separately notes that catalogs at the scale of hundreds of millions of records need to keep indexed fields limited to what is actually searched, precisely because that scale is a real, designed-for case rather than a hypothetical one (Radiant Earth Foundation, n.d.). A Datastream's Record can carry this extent information forward directly, at no cost beyond forwarding data CSAPI already requires the server to compute.Applying Calvert Sound's profile across everything Priya needs for her incident report produces a catalog spanning all nine of CSAPI's resource types, plus one resource from a sibling standard — illustrating the metadata-layer point above concretely rather than only in the abstract. The following is an illustrative, representative shape — not reproduced from any specification — of the full set:
[ { "id": "sys-herring-neck", "recordType": "System", "title": "Calvert Sound Buoy — Herring Neck", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public" }, { "id": "proc-exo2-temp", "recordType": "Procedure", "title": "YSI EXO2 Multiparameter Sonde — Water Temperature Channel", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public" }, { "id": "dep-cs-summer26", "recordType": "Deployment", "title": "Calvert Sound Summer 2026 Deployment", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public" }, { "id": "ds-herring-neck-temp", "recordType": "Datastream", "title": "Herring Neck Water Temperature Datastream", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public" }, { "id": "cs-herring-neck-control", "recordType": "ControlStream", "title": "Herring Neck Command & Control Stream", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Research Partners Only" }, { "id": "sf-herring-neck-watercolumn", "recordType": "SamplingFeature", "title": "Herring Neck Water Column Sampling Point", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public", "handlingRestrictions": "Coordinates generalized to nearest 0.01 degree; adjacent to a shellfish restoration site" }, { "id": "obs-herring-neck-0091", "recordType": "Observation", "title": "Herring Neck Water Temperature Observation, 2026-07-04T14:30:00Z", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Embargoed Pending QA/QC" }, { "id": "cmd-0512", "recordType": "Command", "title": "Recalibrate Sensor — Herring Neck, 2026-07-04T14:42:00Z", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Research Partners Only" }, { "id": "se-herring-neck-calibration", "recordType": "SystemEvent", "title": "Herring Neck Calibration Event, 2026-07-04T14:58:00Z", "contacts": [{"organization": "Calvert Sound Observing Network", "roles": ["originator", "custodian"]}], "authorizationReference": "Maryland Coastal Monitoring Grant #MD-CZM-2026-114", "disclosureReleasability": "Public" }, { "id": "feat-calvert-sound-estuary-boundary", "recordType": "Feature (OGC API - Features)", "title": "Calvert Sound Estuary Boundary", "contacts": [ {"organization": "Maryland Department of Natural Resources", "roles": ["originator"]}, {"organization": "Calvert Sound Observing Network", "roles": ["custodian"]} ], "authorizationReference": "Maryland DNR Critical Area Designation Order 2019-07", "disclosureReleasability": "Public" } ]Two entries are worth pausing on. The Observation carries a Disclosure value of "Embargoed Pending QA/QC" — the anomalous reading Priya flagged in Part I, not yet confirmed as valid, and the profile lets the catalog say so rather than publishing it as though it were settled. The Estuary Boundary — the one resource from a sibling standard — shows an Originator distinct from its Custodian: Maryland's Department of Natural Resources defined the boundary, but Calvert Sound Observing Network now stewards it within its own catalog, exactly the case the DoD guidance's own distinction between these two roles anticipates.
This profile is a real instance of the ISO 19106 mechanism this paper develops further in Part IV: a community selecting and constraining a base standard's optional territory for its own function, rather than inventing a new standard. Records already invites this directly — its core properties are meant to be extended, and its
contactslist is deliberately open precisely so a community can constrain it. Part IV's Escalation Path returns to this same mechanism in the abstract; this catalog is its worked example.Caption (structured user story):
The wireframe shows two states. Before: ten separate endpoint queries — one per CSAPI resource type, plus a separate call into the Features API for the Estuary Boundary — none carrying any of the governance fields Priya needs, requiring her to cross-reference each by hand. After: one catalog table, a representative slice of the full ten-resource set shown above, with Type, Title, Originator, Custodian, and Disclosure columns populated directly — including the embargoed Observation and the Estuary Boundary's split Originator/Custodian, both visible at a glance rather than requiring a separate lookup to notice.
Part III: Why Three Problems Share One Foundation
One Distinction, Three Operations
Part I established the vocabulary this Part now uses rather than re-derives: CSAPI's Observations, Commands, and System Events are fact-like, high in volume and appended continuously; its Systems, Procedures, Deployments, and Datastreams are dimension-like, comparatively few and slow-changing (Kimball 2003a).
The three problems this paper addresses are not three unrelated requirements that happen to touch this same distinction — they are three different operations performed on it.
Enrichment is a join: attach a dimension attribute to a fact row a client already has. Type 1's own case is the clearest instance — an Observation, a fact row, enriched with the name of the System that produced it, a dimension attribute it does not carry directly.
Discovery touches no fact table at all. It is dimension-to-dimension browsing: Systems, Procedures, Deployments, and Datastreams described uniformly so a client can find and navigate them, without any Observation or Command entering the picture. Priya's incident-report catalog in Part II is built almost entirely from this side of the distinction — nine of its ten entries are dimension-type resources, described and searched, not enriched.
Selection points the same join in the opposite direction: instead of attaching a dimension attribute to a fact row a client already has, a dimension condition decides which fact rows come back in the first place — a slice for one condition, a dice for more than one (Codd, Codd, and Salley 1993). This is why selection is a close cousin of enrichment rather than of discovery, despite Overview introducing it last: both operations resolve the same dimension reference; only the direction the result is used in differs.
One entry in Part II's own catalog crosses this line deliberately, and is worth returning to directly:
obs-herring-neck-0091, the anomalous Observation itself, cataloged as a Record alongside System, Procedure, and Deployment resources that are dimension-type through and through. A discovery catalog does not need to index every fact individually to be useful — the sensible default remains dimension-only, exactly as the rest of Part II's catalog is built — but nothing about the fact/dimension distinction forbids cataloging a fact when a specific need calls for it, and Priya's need here is specific: an embargoed reading, not yet confirmed, that her incident report needs to reference directly rather than through a Datastream's general description. STAC's own best-practices guidance confirms this is not a fragile exception: catalogs of individual, fact-grade records operate at the scale of hundreds of millions in production, with the cost of doing so managed by limiting which fields are actually indexed, not by refusing to catalog facts at all (Radiant Earth Foundation, n.d.). The boundary is a deliberate default, not an architectural wall — a distinction this paper's own catalog already depends on.Why the Chain Holds
These three operations could be resolved by three unrelated pieces of infrastructure. What makes solving them together necessary, not merely convenient — the claim Overview made in compact form — is that each one's solution is a working ingredient in the next, checked link by link rather than asserted.
Enrichment's resolution mechanism is not incidental to discovery's; it is a working ingredient in it. Producing a catalog entry worth having means resolving whatever a resource only references rather than states outright — the same by-reference pattern Part I develops at length, and the same mechanism (Content Enricher for a single association, Composed Message Processor for several) Part II relies on to populate a Datastream's Record with its Procedure's actual name rather than a bare identifier. Without that resolution step, Part II's catalog would be exactly as thin as the raw CSAPI resources it describes — a list of bare references, not something worth browsing.
Discovery's catalog, in turn, is not a passive index once selection is on the table. Selection's own resolution begins by querying that same catalog for its qualifying dimension set — the mechanism Part IV develops in full, but the dependency itself is visible without it: filtering Observations down to three Systems that share a Deployment means first resolving which Systems that Deployment actually covers, and that resolution is exactly what the discovery catalog already provides. Selection does not require a second, separate index; it requires the first one to already exist and already be queryable.
Each problem's solution is therefore a working ingredient in the next one's, not a neighbor drawing on the same well by coincidence. Part IV takes this dependency chain as given and builds the shared architecture it justifies — one registry, produced by a single indexing pass, serving enrichment's lookups and discovery's catalog at once, with selection's fan-out resolved against the same catalog rather than a structure built separately for it.
Part IV: A Layered Architecture
Part III took the dependency chain as established; this Part builds the architecture it justifies. Client applications consume it through four modes, shown above: a discovery UI that queries the catalog directly; enrichment views that compose fact rows with dimension attributes through the composition layer; selection views that filter fact rows by dimension membership, also through the composition layer, reusing the same catalog discovery already built; and an analytics UI that queries a separate escalation index for the harder cases ordinary fan-out cannot answer. Each is described in turn below.
Layer 1 — CSAPI as the System of Record
Systems, Deployments, Procedures, Datastreams, Observations, and Commands remain exactly where CSAPI Parts 1 and 2 define them, exposed exactly as specified, on whichever server or servers hold them. Where a client is connected to more than one CSAPI server, each remains the system of record for its own data; this layer does not merge or reconcile them. Standards compliance is unaffected; every subsequent layer is additive.
Layer 2 — A Shared Dimension Registry
A single indexing process — run on a schedule or driven by change events from CSAPI writes, against one CSAPI server or several at once — reads the dimension-type resources (Systems, Procedures, Deployments, Datastreams) and produces two outputs from that one pass:
The same indexing process is not limited to dimension-type resources by necessity, only by default. Part III established that cataloging a fact individually is a deliberate exception, not an architectural impossibility — Priya's embargoed Observation in Part II's own catalog is exactly this exception exercised, not a hypothetical one. The registry's indexing pass simply needs to be told which fact-type resources, if any, warrant their own Record; the mechanism that produces one is identical either way.
Keying that lookup interface safely across more than one server is not a detail that can be assumed away. CSAPI resource identifiers are local by design: the standard requires only that an ID be unique within its own server, and separately mandates a globally unique identifier on feature resources for exactly the multi-server case, since multiple servers may host different representations/descriptions of the same object (Robin 2025a). The registry in this architecture keys its lookup on that globally unique identifier rather than on a resource's local ID whenever more than one server is in scope, since two different servers may legitimately reuse the same local ID for two unrelated resources, and only the standard's own identifier is guaranteed to disambiguate them. This is not a mechanism invented for this architecture; it is the standard's own answer to the same problem, and the registry's role is simply to use it consistently.
The risk this avoids is concrete, not hypothetical. The following is an illustrative, representative shape — not reproduced from the specification — of two independently operated CSAPI servers that happen to assign the same local ID to two unrelated Systems:
A registry keyed on the local id alone would conflate these into a single System — Herring Neck itself, misidentified as an unrelated weather station operated by a different organization entirely; keyed on the uid, they remain correctly distinct, which is exactly the disambiguation the standard's dual-identifier scheme is designed to provide.
Layer 3 — A Composition Layer for Enrichment and Selection
Between the UI and its data sources — one or more CSAPI servers' dynamic endpoints, plus the Layer 2 registry — sits a thin composition layer responsible for the actual join. Architecturally, this is the API Composition pattern: implement a query by invoking the services that own the data and performs an in-memory join of the results (Richardson, n.d.). For the specific case of enriching one fact record with descriptive detail from a related resource, this is a direct instance of the Content Enricher pattern, which uses a specialized component to access an external data source in order to augment a message with missing information (Woolf, n.d.). Where a resource needs more than one association resolved at once — a Datastream's Record wanting its Procedure's name and its System's name together, the way Part II's catalog builds each entry — the same composition layer applies Composed Message Processor instead, splitting the resolution into its component associations and reassembling them into one result (Hohpe and Woolf 2003, 294–296). Layer 3's role can also be named at a higher level than either mechanism: CSAPI's deliberately fine-grained, independently addressable resources are exactly the "web of fine-grained objects" a Remote Facade is built to sit in front of, translating coarse-grained client requests onto them without containing domain logic of its own (Fowler 2002, 388). What the composition layer hands back — an enriched row, or a multi-field Record — is a Data Transfer Object in Fowler's sense, an object built specifically to aggregate data from several sources into one payload for a single call (Fowler 2002, 401). Where this layer is maintained specifically for a given client experience, it also functions as a Backend for Frontend, in that it is tightly coupled to a specific user experience and owned by the team building that UI rather than a general-purpose backend team (Newman 2015). Whether implemented as a small set of REST endpoints, a GraphQL schema with resolver fields backed by the Layer 2 lookup interface, or an in-process join within the client, the underlying principle is constant: new UI requirements become new queries against existing infrastructure rather than new integration projects.
This closes the loop on Type 1's own example. The raw Datastream JSON Part I showed, with only a bare
procedure@id, is resolved through exactly this mechanism — a single lookup against the Layer 2 cache, not a direct call to the Procedure endpoint — producing the resolved profile page Type 1 already walked through in full.Type 4's own worked example — Herring Neck's anomalous Observation correlated with the Command that may have answered it — is the simplest case of a second, genuinely different mechanism: drill-across, Kimball's term for correlating two or more fact tables through a conformed dimension (Kimball 2003b). Observation and Command share no direct reference to each other; both reference System, one through a Datastream, the other through a ControlStream. Resolving the correlation means issuing one request against each fact endpoint — Herring Neck's Observations, Herring Neck's Commands — narrowed to a shared time window, and merging on the System they have in common: an application of the Scatter-Gather pattern, send a message to a set of recipients and return a single message that combines their responses (Hohpe 2003), here parameterized by two fact endpoints rather than a dynamic list. No registry lookup is required beyond resolving the shared System once; the correlation itself is a merge, not a join.
Selection's own resolution, developed in Overview and Part III as a cousin of enrichment rather than of drill-across, uses the same Scatter-Gather shape for a different purpose: filtering a single fact type by a dimension-side condition, rather than correlating two different fact types through one. Resolving "Systems in this Deployment that implement this Procedure" — the paper's own dice example — or the simpler slice case, "Observations from Systems in this Deployment," begins by querying the Layer 2 Searchable Catalog, which already supports the compound, multi-attribute filtering either case requires, for the qualifying set of Systems. The composition layer then issues one fact-endpoint request per qualifying System and merges the results on the shared key — mechanically the same Scatter-Gather shape as drill-across, but resolving a different kind of question: not "what else is true of the thing I already have," but "which things qualify in the first place." No new registry capability is required either way; the Searchable Catalog built for discovery in Part II is reused here for dimension-side selection, and because that catalog can aggregate Systems from more than one CSAPI server, the qualifying set is not assumed to share a single origin — each per-System request is simply routed to whichever server actually hosts that System.
The composition layer's reach extends further than either of these on their own. Consider a full provenance trace: given one Observation, resolve the Command that produced it, the System both are associated with, the Procedure that System's Datastream implements, and the Deployment the System was part of — all as of that Observation's own timestamp. This is Composed Message Processor at its fullest extent in this essay: four separate associations, on three different resource types, resolved together and reassembled into one trace, rather than a single reference followed once. Each hop exercises a shape already developed: Observation-to-System is fact-to-dimension (Type 1), resolved via the Datastream two-hop path; Observation-to-Command is fact-to-fact via the conformed System dimension (Type 4), here the single-value case rather than fan-out; System-to-Datastream-to-Procedure is dimension-to-dimension single reference (Type 2), resolved as of the Observation's timestamp against the Datastream's own versioned description; and System-to-Deployment is dimension-to-dimension multi-valued and temporal (Type 3), resolved by that same timestamp against the set of Deployments that included the System. No new mechanism answers this case: Layer 3 issues the same lookups already described, in sequence, with the Observation's timestamp threaded through the two temporal-as-of resolutions rather than resolved as of the current moment. The one requirement this case makes explicit is that Layer 2's lookup interface must accept a timestamp alongside an ID for temporal-as-of queries — a small, already-anticipated extension rather than a new capability.
Escalation Path
Lookup-and-enrich, and selection's ordinary fan-out, do not address every case. When a dimension attribute must act as a filter or sort key over high-volume fact data — for example, retrieving Observations from Systems located within a region, sorted by System name — neither mechanism can satisfy the query: the qualifying set may be far larger than a handful of Systems in one Deployment, and a sort across the merged result needs the dimension attribute already sitting alongside the fact data, not stitched together after fan-out completes. This scenario requires denormalizing the relevant dimension attributes directly into whatever store answers the fact-level query — a search index or materialized view kept current from the same event stream feeding Layer 2 — which represents a heavier, CQRS-style investment (Richardson, n.d.). This investment is best triggered by a concrete requirement reaching this limit rather than built speculatively in advance.
A lighter-weight alternative is worth considering first, when it applies: if a deployment already spans multiple CSAPI servers under coordinated governance, some of these gaps can be closed by profiling the standard rather than building around it. ISO 19106 formally defines a profile as a set of one or more base standards together with the chosen clauses, classes, and options of those standards necessary for a particular function (International Organization for Standardization 2004) — the same mechanism OGC API's own Requirements and Conformance Classes already provide natively, and the same mechanism behind Calvert Sound's own Records profile in Part II. A federation of CSAPI servers can agree to elevate a currently optional Requirements Class — Deployment support, for instance — to mandatory across all participating servers, closing a normative-floor gap at the governance level rather than the application level. This depends on the community being able to see what the base standard actually requires, not just what its developer-facing tooling happens to surface; this paper's own finding that Requirement 9 is absent from the bundled OpenAPI specification file distributed alongside CSAPI Part 2 (Open Geospatial Consortium Connected Systems Standards Working Group, n.d.), despite being normatively real in the published standard itself (Robin 2025b), is a concrete instance of exactly the visibility problem profiling depends on solving first.
Conclusion
Priya's investigation into one anomalous reading from Calvert Sound's Herring Neck buoy touched all three of the problems this paper addresses, in the order she actually needed them: enriching that reading with the System, Procedure, and Deployment behind it; browsing a catalog built to answer who was responsible for each resource before she could close her incident report; and, had the anomaly implicated more than one buoy, filtering by Deployment membership rather than checking each System in turn. CSAPI's resource-oriented design is what created all three needs in the first place, by keeping each resource lean and pushing descriptive detail onto whichever related resource actually owns it — an Observation pointing to its Datastream and System, a Command to its ControlStream and System, a Datastream to the Procedure it implements, a System to the Deployments it has been part of (Robin 2025a; Robin 2025b).
OGC API – Records answers the second need with more than a shared schema. Calvert Sound's own profile — built on Records' already-open contacts property and its explicit invitation to extend its core — added exactly the governance fields the DoD's own metadata guidance suggested by analogy, and its catalog cataloged the anomalous reading itself as a Record, embargoed pending confirmation, a deliberate exception to dimension-only cataloging rather than an architectural impossibility (Vretanos 2025a). The Local Resources Catalog pattern this profile builds on is not merely theoretical; it is already deployed at scale, including as part of the World Meteorological Organization's WIS2 baseline (Vretanos 2024; Kralidis 2024).
Distinguishing between fact and dimension data (Kimball 2003a) does more than describe why these problems differ; it explains why solving them together is necessary rather than convenient. Producing a catalog entry worth having recruits the same resolution mechanism enrichment needs; selection's own resolution begins by querying that same catalog for its qualifying dimension set. A single architecture — a shared dimension registry exposed through both a standards-based catalog and a fast lookup interface, joined via a thin composition layer — serves all three problems for this reason, not merely because they happen to draw on the same data. Selection resolves through that same composition layer's ordinary fan-out, reusing the discovery catalog rather than requiring escalation; escalation itself is reserved for the narrower case where a dimension attribute must also serve as a filter or sort key over fact data at a scale ordinary fan-out cannot answer, and aggregating across one CSAPI server or several is possible throughout by keying on the standard's own globally unique identifiers rather than server-local ones.
None of this asks a client system to restructure itself around CSAPI's resource model; the architecture is additive integration infrastructure, not a replacement for a client's own requirements-driven design. Each layer names a real pattern, and there are more of them here than any single problem would have needed on its own: Content Enricher and Composed Message Processor for resolving one association or several, API Composition and Remote Facade for the composition layer as a whole, Data Transfer Object for what it returns, Scatter-Gather for both drill-across and selection's fan-out, Canonical Data Model and Schema Centralization for the shared catalog schema, and ISO 19106 profiling for the cases a federation of servers can close by agreement rather than by building around a gap. None of these patterns was chosen to solve one problem in isolation; they compose, in the sense a pattern language is built for (Zimmermann et al. 2016) — which is the more precise way to describe what adopting a standards-based API like CSAPI actually requires of the systems that consume it, and the reason this paper's own title names composition alongside discovery and enrichment rather than treating it as an afterthought.
References
Byron, Lee, and Nicholas Schrock. n.d. "DataLoader." GitHub repository, graphql/dataloader. Accessed July 3, 2026. https://github.com/graphql/dataloader.
Codd, E. F., S. B. Codd, and C. T. Salley. 1993. Providing OLAP to User-Analysts: An IT Mandate. Technical report. Arbor Software Corporation.
Erl, Thomas. 2008. SOA: Principles of Service Design. Upper Saddle River, NJ: Prentice Hall.
Fowler, Martin. 2002. Patterns of Enterprise Application Architecture. Boston: Addison-Wesley.
GitHub. n.d. "Best Practices for Using the REST API." GitHub Docs. Accessed July 3, 2026. https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api.
Hohpe, Gregor. 2003. "Enterprise Integration Patterns." Conference presentation, JAOO. Accessed July 4, 2026. https://www.enterpriseintegrationpatterns.com/docs/jaoo_hohpeg_enterpriseintegrationpatterns.pdf.
Hohpe, Gregor, and Bobby Woolf. 2003. Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Boston: Addison-Wesley.
International Organization for Standardization. 2004. ISO 19106:2004 — Geographic Information — Profiles. Geneva: ISO.
Kimball, Ralph. 2003a. "Fact Tables and Dimension Tables." Kimball Group Design Tip, January 2003. https://www.kimballgroup.com/2003/01/fact-tables-and-dimension-tables/.
Kimball, Ralph. 2003b. "The Soul of the Data Warehouse, Part 2: Drilling Across." Kimball Group Design Tip, April 2003. https://www.kimballgroup.com/2003/04/the-soul-of-the-data-warehouse-part-two-drilling-across/.
Kralidis, Tom. 2024. Comment on "Local Resource Catalogs and req. class organization." GitHub Issue #300, opengeospatial/ogcapi-records repository. Accessed July 3, 2026. opengeospatial/ogcapi-records#300.
Little, Christopher. 2025. Email to OGCAPI-records.swg mailing list, "RE: API Records Implementation Questions." February 19, 2025. https://lists.ogc.org/pipermail/ogcapi-records.swg/.
Microsoft. n.d. "Web API Design Best Practices." Azure Architecture Center, Microsoft Learn. Accessed July 3, 2026. https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design.
Newman, Sam. 2015. "Pattern: Backends for Frontends." samnewman.io, November 18, 2015. https://samnewman.io/patterns/architectural/bff/.
Open Geospatial Consortium. n.d.-a. "Records Deployment Patterns." OGC API – Records Developer Guide. Accessed July 3, 2026. https://records.developer.ogc.org/patterns.html.
Open Geospatial Consortium. n.d.-b. "OGC API - Records — API Deep Dive." OGC API Workshop. Accessed July 3, 2026. https://ogcapi-workshop.ogc.org/api-deep-dive/records/.
Open Geospatial Consortium Connected Systems Standards Working Group. n.d. "ogcapi-connected-systems." GitHub repository. Accessed July 3, 2026. https://github.com/opengeospatial/ogcapi-connected-systems.
OS4CSAPI. 2026. "CSAPI Deployed Systems Design Pattern." Research document, OSHConnect-Python repository. Accessed July 6, 2026. https://github.com/OS4CSAPI/OSHConnect-Python/blob/main/docs/research/CSAPI_Deployed_Systems_Design_Pattern.md.
Radiant Earth Foundation. n.d. "STAC Best Practices." GitHub repository, radiantearth/stac-spec. Accessed July 6, 2026. https://github.com/radiantearth/stac-spec/blob/master/best-practices.md.
Richardson, Chris. n.d. "Pattern: API Composition." Microservices.io. Accessed July 3, 2026. https://microservices.io/patterns/data/api-composition.html.
Robin, Alexandre. 2025a. OGC API — Connected Systems — Part 1: Feature Resources. OGC 23-001, version 1.0. Open Geospatial Consortium. Published July 16, 2025. https://docs.ogc.org/is/23-001/23-001.html.
Robin, Alexandre. 2025b. OGC API — Connected Systems — Part 2: Dynamic Data. OGC 23-002, version 1.0. Open Geospatial Consortium. Published July 16, 2025. https://docs.ogc.org/is/23-002/23-002.html.
U.S. Department of Defense, Chief Digital and Artificial Intelligence Officer. 2023. DoD Metadata Guidance, Version 1.0. Washington, DC: Department of Defense.
Vretanos, Panagiotis (Peter) A. 2024. Comment on "Local Resource Catalogs and req. class organization." GitHub Issue #300, opengeospatial/ogcapi-records repository. Accessed July 3, 2026. opengeospatial/ogcapi-records#300.
Vretanos, Panagiotis (Peter) A. 2025a. OGC API — Records — Part 1: Core. OGC 20-004r1, version 1.0. Open Geospatial Consortium. Published May 2, 2025. https://docs.ogc.org/is/20-004r1/20-004r1.html.
Vretanos, Panagiotis (Peter) A. 2025b. Email to OGCAPI-records.swg mailing list, "Re: API Records Implementation Questions." February 19, 2025. https://lists.ogc.org/pipermail/ogcapi-records.swg/.
Woolf, Bobby. n.d. "Content Enricher." EnterpriseIntegrationPatterns.com. Accessed July 4, 2026. https://www.enterpriseintegrationpatterns.com/patterns/messaging/DataEnricher.html.
Zimmermann, Olaf, Cesare Pautasso, Gregor Hohpe, and Bobby Woolf. 2016. "A Decade of Enterprise Integration Patterns: A Conversation with the Authors." IEEE Software 33, no. 1: 13–19. doi:10.1109/MS.2016.11.
Note on Citation Mechanics
Beta Was this translation helpful? Give feedback.
All reactions