Federated vs Individual¶
The FCC framework supports two scopes for both its object model and its knowledge graph: a single-project local form and a multi-project federated form. They are deliberately different surfaces — not "the same thing scaled up" — and choosing the right one matters because the federated form pays for cross-project coordination with constraints the local form does not have. This page makes the comparison explicit so you can pick the right one without reading both subtree's source code.
The diagram below contrasts the two scopes at a glance.
graph LR
subgraph LOCAL["Individual project scope"]
L_OM["ObjectModel<br/>ModelFacade + Repository[T]"]
L_KG["KnowledgeGraph<br/>9 node + 11 edge types"]
L_NS[Single namespace fcc:]
end
subgraph FED["Federated scope"]
F_OM["FederatedObjectModel<br/>EntityResolver + ChangeTracker"]
F_KG["FederatedKnowledgeGraph<br/>local + remote graphs + cross-edges"]
F_NS[N namespaces with VocabularyMappings]
end
L_OM -.assemble.-> F_OM
L_KG -.merge_federated.-> F_KG
L_NS -.NamespaceRegistry.-> F_NS
L_OM --> L_NS
L_KG --> L_NS
F_OM --> F_NS
F_KG --> F_NS
The choice is not a smooth slider — at some point you stop using
KnowledgeGraph and start using FederatedKnowledgeGraph, and the
collaborators around it change. Below is the side-by-side comparison.
Object model: local vs federated¶
| Concern | Individual (objectmodel/) |
Federated (objectmodel/federation.py + federation/) |
|---|---|---|
| Entry point | ModelFacade (one per project) |
assess_cross_project(facades) + EntityResolver.resolve() |
| Storage | RepositoryProtocol[T] per entity type |
N project facades, each with their own repos |
| Identity | local_id unique within the project |
(local_id, namespace) pair, plus optional canonical_id |
| Vocabulary | implicit — fields hardcoded in dataclasses | explicit — VocabularyMapping(source_id, source_vocabulary, target_id, target_vocabulary, similarity_score) |
| Search | ModelFacade.search(query) over local repos |
EntityResolver.resolve(local_id, source_ns, target_ns) per namespace pair |
| Change handling | dataclasses are frozen → no in-place change | ChangeTracker.record(ModelChange) audit trail across projects |
| Assessment | EvolutionStage enum (DRAFT / FOUNDATIONAL / STRUCTURED / SEMANTIC / FEDERATED) |
CrossProjectAssessment(cross_project_score, federation_readiness, vocabulary_coverage) |
| Failure mode | a missing entity returns None |
a missing mapping returns FederatedEntity(resolved=False) — caller decides whether to treat as soft or hard miss |
Knowledge graph: local vs federated¶
| Concern | Individual (knowledge/graph.py) |
Federated (knowledge/federation.py) |
|---|---|---|
| Container | KnowledgeGraph — _nodes dict, _edges list, _adjacency map |
FederatedKnowledgeGraph — _local_graph + _remote_graphs dict + _cross_edges list |
| Node identity | node_id unique within graph |
(node_id, namespace) — uniqueness is per-namespace |
| Edge types | 11 standard (INTERACTS_WITH, DEPENDS_ON, ORCHESTRATES, …) | 11 standard plus FEDERATION_LINK, CROSS_NAMESPACE, EQUIVALENT_TO for cross-namespace bridges |
| Merge | KnowledgeGraph.merge(other) returns combined graph |
FederatedKnowledgeGraph.merge_federated() returns flattened KnowledgeGraph with namespace-prefixed ids |
| Resolve entity | get_node(node_id) |
resolve_entity(local_id, source_namespace, target_namespace) — returns the equivalent node in the target graph if a EQUIVALENT_TO edge exists |
| Subgraph | subgraph(node_ids) returns view |
requires merge first; subgraph operates on the merged graph |
| Serializers | OWL, RDF, SKOS, JSON-LD via knowledge/serializers.py |
same serializers, applied to merged form; namespace prefixes are preserved |
When to use which¶
Use the individual form when:
- You are a single project with one canonical vocabulary.
- All entities live in
src/<project>/data/and have stable local ids. - You want fast in-process queries with no I/O between subsystems.
- You do not need to track changes that happen outside the project.
Use the federated form when:
- You are coordinating ≥ 2 projects (e.g. FCC + PAOM, or FCC + AOME + CONSTEL + Sky-Parlour).
- The same conceptual entity is identified differently in each project (e.g. "Persona" in PAOM vs "Agent" in CTO).
- You want a compliance audit trail that survives across project releases.
- You need cross-project entity search (e.g. "find every governance persona regardless of which project owns it").
Migration path¶
Going from individual to federated is additive — the local form does not have to change. The steps are:
- Define the namespace —
NamespaceConfig(namespace="myproject", prefix="mp", base_uri="https://...", version="1.0", description="...")— and register it withNamespaceRegistry.register(config). - Author vocabulary mappings — for each entity that exists in your
project AND in another, create a
VocabularyMappingrow insrc/fcc/data/objectmodel/<namespace>_vocabulary_mappings.yaml. The 175 packaged mappings undersrc/fcc/data/objectmodel/are the reference templates. - Ship a
VocabularyProviderPlugin— see Plugin boundaries. The 12 existing plugins underplugins/fcc-{codename}-plugin/are reference implementations. - Wrap your
ModelFacadewith a federation client — callassess_cross_project([your_facade, other_facade, ...])whenever you want a coordination snapshot. - (Optional) wrap your
KnowledgeGraph— callFederatedKnowledgeGraph.register_local(your_graph)andregister_remote(namespace, other_graph)for each peer project, thenmerge_federated()produces the unified view.
The FCCEvolutionStage enum names this trajectory: FOUNDATIONAL (no
mappings) → STRUCTURED (typed dataclasses) → SEMANTIC (vocabulary
mappings exist) → FEDERATED (active multi-project resolution). A project
can sit at STRUCTURED indefinitely; not every project needs to climb to
FEDERATED.
Cost of federation¶
Federation is not free. The added concerns are:
- Mapping maintenance — every new entity in any peer project needs a
mapping row, or it shows up as
FederatedEntity(resolved=False)and pollutes assessment scores. - Namespace pollution — node ids in the merged graph carry namespace
prefixes (
paom:persona-001vsfcc:persona-001); existing query code may need to learn to ignore the prefix. - Change-tracking volume —
ChangeTracker.record(ModelChange)writes one row per cross-project diff. For active projects this is thousands of records per release. - Vocabulary drift — when a peer project releases a new schema, your mappings need to be re-validated. The 175 packaged mappings include similarity scores so drift is detectable, but somebody has to look.
The feedback_vocabulary_contract_pattern.md memory captures this: sister
projects co-evolve via the VocabularyProviderPlugin pattern so the FCC
side stays declarative (YAML mappings) and the sister side stays decoupled
(no FCC dependency).
See also¶
- Source:
src/fcc/objectmodel/,src/fcc/federation/,src/fcc/knowledge/ - Class diagram: object-model variants
- Class diagram: knowledge-graph variants
- Sequence diagram: federation entity resolution
- Sequence diagram: vocabulary-provider load
- DFD: cross-project entity resolution