Skip to main content
Version: V2-Next

Architecture

Model Forge is an embedded Java/Spring Boot runtime for data modelling, versioning and integration configuration within the CIVITAS/CORE platform.

It is not a standalone REST service. Host applications integrate Model Forge as Maven dependencies and call the public Java facade.

Embedded architecture

Core Goals​

  • JSON Schema 2020-12 as canonical data modelling format.
  • PostgreSQL-backed artifact registry as source of truth.
  • Global, stable artifact identities based on CORE URNs.
  • Traceable schema and artifact dependencies.
  • CORE-IR artifacts and DataSet manifests for downstream pipeline engines.
  • A small, transport-neutral Java integration boundary.

The decisions behind these goals are documented as Architecture Decision Records.

Module Model​

host application
-> model-forge-spring-boot-starter (auto-configuration)
-> model-forge-contract (ModelForge facade + records)
-> model-forge-runtime (facade impl -> use cases -> ports -> adapters)
ModuleResponsibility
model-forge-contractPublic records (commands, results, queries), the ModelForge facade interface and URN helpers. No Spring, web, JDBC, Flyway, PostgreSQL, Servlet, OpenAPI or HAL dependencies.
model-forge-runtimeThe embedded runtime: facade implementation, ports, use cases (import, validation, views, graph), the JDBC/JdbcClient PostgreSQL adapter with Flyway migrations, and the HTTP integration adapters. Internal slice boundaries (core/application/persistence/integrations packages) are enforced by ArchUnit tests.
model-forge-spring-boot-starterSpring Boot auto-configuration that wires the facade and adapters into a host application.
model-forge-admin-uiLocal Wicket developer/debug UI. A consumer of the library through the public facade, not part of the library boundary.

Public Boundary​

Host code should depend on the public facade, for example ModelForge, and on contract records. It should not call application services, persistence classes or internal utilities directly.

import de.civitascore.modelforge.facade.ModelForge;
import org.springframework.stereotype.Service;

@Service
class ImportWorkflow {
private final ModelForge modelForge;

ImportWorkflow(ModelForge modelForge) {
this.modelForge = modelForge;
}
}

The host application owns external API design, authentication, authorization, request limits and operational exposure. Model Forge provides embedded domain capabilities. See portal-backend Integration & Division of Labor for how this splits data ownership once a host integrates Model Forge.

Ports And Adapters​

Application code depends on ports, not on infrastructure:

PortProductive adapter
ArtifactRegistryPostgresArtifactRegistryClient in model-forge-runtime
RemoteSchemaRepositoryRemoteSchemaFetcher in model-forge-runtime (SSRF-guarded HTTP schema fetching)
XRepositoryCatalogXRepositoryClient in model-forge-runtime
XsdSchemaConverterXSD to JSON Schema converter

This keeps import, validation, graph and view logic testable without web or database concerns.

PostgreSQL Registry​

The host provides spring.datasource. Model Forge reuses that datasource and runs its own Flyway migration in a dedicated schema:

model-forge:
registry:
schema: model_forge
migration-table: model_forge_schema_history

Model Forge does not register JPA entities. The registry stores dynamic JSON-/XSD-payloads, version metadata and artifact references using explicit SQL.

Stored Artifacts​

DataSet Manifest​

{
"$schema": "https://civitasconnect.digital/core-dataset/v1",
"id": "urn:core:platform:civitas:dataset:common:SensorData:nxsphawneu:1.0.0",
"title": "Sensordaten-Import",
"version": "1.0.0",
"datastructureRefs": ["urn:core:...:datastructure:...:SensorData:1.0.0"],
"mappingRefs": ["urn:core:...:mapping:...:sensor-to-obs:1.0.0"],
"pipelineRefs": ["urn:core:...:pipeline:...:MQTT-to-SQL:1.0.0"],
"dataSourceRefs": ["urn:core:...:datasource:...:mqtt-sensor:1.0.0"],
"dataSinkRefs": ["urn:core:...:datasink:...:sql-obs:1.0.0"]
}

Element​

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "urn:core:platform:civitas:element:common:SensorReading:d28s38wfmi:1.0.0",
"title": "SensorReading",
"type": "object",
"required": ["sensorId", "temperature", "observedAt"],
"properties": {
"sensorId": { "type": "string" },
"temperature": { "type": "number" },
"observedAt": { "type": "string", "format": "date-time" },
"location": { "$ref": "urn:core:platform:civitas:element:common:GeoPoint:3ak90vqrog:1.0.0" }
}
}

Every $ref to a CORE URN becomes a stored artifact reference and a graph edge. The full format of Mappings, Pipelines, DataSources and DataSinks is specified in the CORE-IR Reference.

Runtime State​

PostgreSQL is the source of truth. The only in-memory state is the dependency graph, derived and rebuilt from the registry at startup.

For the first embedded version the documented operating model is single-writer. If a host later runs multiple write-capable instances, concurrency and cache invalidation need a separate architecture decision.

Enforced Boundaries​

ArchUnit tests guard the important boundaries:

  • Contract is transport- and infrastructure-free.
  • Application code does not depend on Spring Web, Servlet, HAL or OpenAPI.
  • Application code depends on registry ports, not PostgreSQL adapters.
  • The Spring Boot starter registers no controllers or web-security chains.

Technology Decisions​

DecisionChosen technologyRationale
Schema formatJSON Schema 2020-12Open standard, JSON-native, broad tooling
Artifact registryPostgreSQL + FlywayFull reference graph, JSONB payloads, SemVer metadata, no additional service
Persistence accessJDBC/JdbcClientExplicit fit for dynamic artifact payloads and version tables
Validationnetworknt json-schema-validatorJSON Schema 2020-12 support and custom annotation keywords
Host integrationSpring Boot 4 starterMatches portal-backend and reuses host datasource/transactions