The idea · 13 min read
Why federate a systems model?
Models were meant to end the drift between an organisation's documents, and closed tools kept them out of reach. SysML v2 opens the model up, and federation lets other tools join it without learning SysML.
Roar Elias Georgsen, 27 August 2026
Part 1 of 13 in Federating a systems model, written for release v0.3.0.
Every engineering organisation I’ve worked with keeps its system in several places at once. The requirements live in one file and the interface definitions in another. The power budget sits in a spreadsheet, and the hazard analysis is somewhere a third team controls. All of them describe the same engineered system. Keeping them in agreement is manual work that nobody enjoys and everybody puts off until a review forces the issue.
Model-based systems engineering (MBSE) was supposed to end that. The documents give way to a systems model, one structured description holding the system’s parts, the properties they carry, the requirements they must meet and the relationships between all of it. Documents become views of the model. Change a component’s power draw and everything downstream can be recomputed, including which requirements now fail.
That promise has been five years from mainstream adoption for about 25 years, and the least disputed explanation is the tooling.
Systems models live inside proprietary modelling environments. They’re stored in binary formats, or in a vendor’s own flavour of XMI that only that vendor reads back reliably. You reach them through an API that is either missing or shaped differently in every product. Several of the big environments still treat the model as a binary file you lock rather than something you merge, and offer nothing a build server can drive. So the model can’t join the continuous integration workflow every other discipline now takes for granted.
Vendors will tell you integration is a solved problem, and they aren’t exactly lying. Connectors to requirements tools, test management and PLM exist. They are also, with tiresome regularity, a separately licensed product, configured through a dialogue box and liable to break at the next upgrade of either end. Two-way synchronisation exists too, but setting it up takes a skill the engineer who needs it doesn’t have. More often than not the synchronisation stays unconfigured, and everyone falls back to export.
That is what lock-in looks like in practice. It’s a pile of small daily frictions that make export always the cheapest option in the moment, and it’s rarely written into a contract. Requirements go to Word for review, interface definitions reach the software team in a spreadsheet, and the power budget lives in Excel because that’s where the person who owns it works.
Every one of those copies starts drifting the moment it is made.
We’ve tried this before
The industry has had two serious goes at the problem. The service bus generation put a central integration platform in the middle, with a canonical data model owned by an integration team. OSLC took a lighter route through linked data, so tools could refer to each other’s objects without importing them.
Both put the integration in the middle, where it needs a team of its own. That team owns the mapping between systems it didn’t build, so the mapping ends up written by the people furthest from the thing being mapped. A large organisation can afford the team, and still ends up with the knowledge and the responsibility in different places.
A small organisation can’t staff that middle at all. Most engineering happens in organisations with fewer than 25 engineers, and those organisations are who this work is for.
What SysML v2 settles
SysML is the standard modelling language for systems engineering. Its first version was a profile of UML, a language built for software design, so systems engineers described pumps and power lines in a vocabulary shaped for somebody else’s problems. Semantics defined by a profile are semantics defined by convention, and two tools could apply the same profile differently and both still be valid.
Version 2 sits on a foundation of its own, KerML, with semantics defined formally. The Object Management Group announced its adoption on 21 July 2025 (press release), and the language specification is published as formal/26-03-02, which is the version this work targets.
SysML v2 removes two kinds of lock-in, and they’re worth keeping apart.
The first is authoring. The language has a textual notation, so a model is a set of text files that live in Git, show their differences in a pull request and can be edited in any text editor. That’s the change everyone talks about.
The second is access, and for this series it matters more. SysML v2 comes with a standard API and a standard JSON serialisation. Any conforming model repository exposes a model the same way, and any conforming tool can read the same serialised model. For the first time the model has a defined interface that no vendor owns.
What it leaves open
Neither the textual notation nor the standard API fixes the shape of what comes back. The API hands you the metamodel. Elements are generically typed, relationships are themselves elements, and you reach a requirement’s text by walking owned relationships to documentation comments. That design suits a modelling API. The same design also means every consumer has to learn the metamodel before doing anything useful.
So the compliance dashboard team learns SysML. The reliability tool learns SysML. The people building the customer-facing status page learn SysML badly, and write the same traversal again with a different set of bugs. That’s what happened with OSLC, and it’s why lifecycle integration stayed the preserve of organisations that could staff it.
A projection instead
Nobody outside systems engineering should need to know what SysML is.
My proposal is to let the model’s owners publish a projection. Systems engineers understand the metamodel, so they’re best placed to decide, for everyone downstream, that a requirement is an identifier, a name, some text, a limit and a set of things that satisfy it. Everything else stays inside the model. A consumer meets a small, plainly typed object and never runs into KerML.
Three conditions make such a projection worth having:
- It has to be live rather than exported, so it can’t drift.
- It has to be joinable. A service holding data the model doesn’t contain should be able to attach that data to the model’s objects without either side knowing about the other.
- The contract between producer and consumer has to be checked mechanically before deployment, instead of being discovered in production.
A projection is also a read path. Writes belong to the model’s own API.
Federation meets all three conditions. The idea grew up inside GraphQL, which is why this series uses GraphQL. I chose it for the maturity of the tooling, and I have no attachment to the protocol. The claim underneath this whole series is that federation is the missing integration layer for open MBSE. I’ve recorded the choice of federation over a single service, with the alternatives that lost.
Federation, for a systems engineer
GraphQL is a style of API where the server publishes a schema describing its types and their fields, and a client sends a query naming exactly the fields it wants. There is one endpoint, and the response has the same shape as the request.
Federation stretches that across services. Several independently owned services each publish a fragment of a schema, called a subgraph. A build step merges the fragments into one schema. At run time a component called the router accepts a query written against the merged schema, works out which service holds which field, calls each one and assembles the result. The client sees one graph and has no idea how many services stand behind it.
Key term Entity key
The field that tells federation two services are talking about the same object. One service declares that a type is identified by a particular field. Any other service can then add fields to that same type by declaring the same key. When a client asks for fields from both, the router fetches from each and joins the answers on the key.
In this demo the adapter, which serves the model, says a requirement has an identifier and some text (both snippets are trimmed to the fields that matter here):
type Requirement @key(fields: "id") {
id: ID!
text: String
}
and the capacity service, which runs the analysis, separately says a requirement has a verdict, its own answer on whether that requirement is met:
type Requirement @key(fields: "id") {
id: ID!
verdict: Verdict!
}
Neither service imports the other, and neither calls the other. A client asks for a requirement’s text and its verdict in one query, and the router fetches from both and joins them on the key. To a systems engineer, that’s a cross-tool trace link that resolves by itself and gets checked before it ships. For an element of a SysML model, I made the key the short name, with the qualified name as fallback.
One query, two services that never call each other, one answer joined on the key.
Two properties follow, and they matter more than the syntax.
Nobody writes the merged schema. It’s computed, so nobody owns it, which is the opposite of a canonical data model negotiated by committee. The people who build each service, and understand its domain, write its schema. If two services define incompatible things, the merge fails in the pipeline of whoever pushed the change, with a message naming the conflict.
The router holds no logic. It transforms nothing and enforces no rules of its own. Everything the service bus generation put in the middle stays in the services, which keeps the middle from becoming a bottleneck.
A model of models
Once that arrangement is in place, the word model starts to mean something bigger. The merged schema can carry requirements from a systems model, verification results from a test management system, part costs from PLM and field behaviour from an operational data store. Each is a model of some aspect of the same thing, held in whatever formalism its own discipline settled on. Most won’t be SysML, and there’s no reason they should be.
The merged schema describes a system of systems, meaning constituent systems that each have a use of their own, are owned by different parties, and together do something none of them does alone. Each team models what it understands, at its own pace. Adding a twelfth model doesn’t mean renegotiating a schema with the other eleven, because keys are the only thing anyone has to agree on. The alternative the industry keeps trying, one very large model covering everything, concentrates authority in whoever owns its schema.
Why Cosmo
A plain GraphQL server publishes one schema owned by one team, which puts us straight back in the middle. Federation needs a platform: composition to merge the schemas, checks for breaking changes, and a query planner in front. Of the platforms that do this, I chose Cosmo, mostly for reasons that have little to do with GraphQL (Cosmo as the platform).
Cosmo is under the Apache 2.0 licence, where the main alternative uses a licence the Open Source Initiative doesn’t recognise as open source. For an argument aimed at small organisations, that isn’t a footnote. Composition runs locally with no control plane, and the router can start from a pre-built configuration file.
As the vendor ships it, the router sends anonymous usage data. The demo’s container image turns that off with environment variables (telemetry off). With no graph token set, nothing the router does reaches outside the container on any code path I’ve read. I ran the image with its network removed to check. Defence, rail, energy and medical device work all need that kind of isolation, and those are the industries most likely to be modelling in SysML in the first place.
The vendor is candid that starting the router from a file, with no control plane, isn’t the path it recommends. Its page on composition says “it is recommended to not use this for production”, and the router logs “Not recommended for Production” when it starts from a file. I take the warning at face value and answer it on the demo’s own terms. There’s no control plane to fetch from, the composed configuration lives in a committed file, and a test fails when that file drifts from the schemas it was built from. That test is the third condition made concrete, since it checks the contract mechanically before deployment.
The deeper reason is that Cosmo itself is moving away from GraphQL as the transport. Its subgraphs can now be compiled to protobuf and served over gRPC, with GraphQL kept as the schema language. I read that as an admission that the value was always in the rules by which schemas merge, the entity key and the query planner, whatever the wire format. That’s my position too.
What the demo is meant to show
The demo publishes a SysML v2 model of a query processing pipeline as a federated service. A throughput analysis and a requirements document join it, and neither knows anything about SysML.
The pipeline is five servers wired as stages in series and in parallel, each with a throughput. A requirement in the model states the query rate the whole pipeline must sustain, and a requirement for each server is derived from that one. Three services stand behind one router, an arrangement The architecture in one sitting takes apart:
- the adapter serves the model
- the capacity service works out the pipeline’s capacity from the servers and returns a verdict for each requirement
- the document service holds the document’s structure and nothing else
Two small web apps sit in front, a model viewer and a requirements document, and you can make an edit in either. Change one server’s throughput and the document responds at once. The capacity moves, the requirement passes or fails, and where it fails, the reason names the server that limits the capacity.
What doesn’t happen is the instructive part. Raise a server that isn’t the bottleneck and the capacity stays put, because a chain is governed by its worst link. Raise the bottleneck and the capacity rises, but the requirement still fails, because the bottleneck has moved to the next weakest stage. With the shipped values the pipeline sustains 1200 queries per second against a limit of 1500, and the bottleneck sits at the parse stage. It’s obvious once you’ve seen it and reliably surprising before. Twelve use cases and one moving bottleneck walks through it.
The verdict you see against a requirement comes from a service that has never parsed a model file. It sits beside text from a service that has never computed anything.
I also keep a SysML v2 model of the demo itself, which I should have done from the start. A model of the demo itself owns up to that.
Stand-ins
Three pieces of the demo are stand-ins. The adapter reads a directory of files where a real deployment would front a SysML v2 repository. Editing through the projection is scaffolding that contradicts the read-path rule above, and a real deployment would write through the SysML v2 API. The capacity arithmetic is deliberately simple, enough to make the point and not meant for real-world use.
The next step is a fuller mapping from model to graph. SysML v2 has views and viewpoints of its own. A viewpoint names a concern and the stakeholder who holds it, and a view is the portion of the model rendered for that concern. A subgraph, seen from its consumer, is the same thing. If the model already says which stakeholder sees what, the projection should be read from it rather than written out a second time in a schema. Covering the whole language would pull the other way, towards a few hundred types generated mechanically from KerML, which is exactly the abstract syntax the projection exists to spare its consumers. My current position is that coverage should grow as curated projections, one concern at a time. I’m not certain of that, and it’s the part of the design most likely to change.
The adapter and the example are in the repository, and both are small enough to read in an afternoon. Part 2 opens the box.
Index: Federating a systems model · Next: The architecture in one sitting