In the Backstage software catalog, relationships provide a glue between the different software, systems, resources and people in your organisation. They can allow you to easily answer questions which otherwise might be much harder to find out in a large or rapidly growing organisation.
Plugins like the Catalog Graph Plugin allow powerful visualisation of these relationship graphs.
Similarly, relationships can become a core piece of information displayed on entity Overview pages.
Relationships can be added in one of two ways - via a YAML file update in an SCM repository, or in a third party source like GitHub Teams or Okta. A YAML update looks something like this:
kind: Component
metadata:
name: identity-backend
spec:
...
+ dependsOn:
+ - component:default/users-backend
+ - resource:aws/identity-backend-ec2
Restrictive Relationships
There are a very limited and restrictive set of allowed relationships between different Kinds in Backstage by default. Knowing what is allowed requires looking up the Backstage documentation each time to check the schemas.
For instance, a Domain cannot “depend on” anything in the default Backstage relationship model. However, maybe you have a domain like Shipping that depends on another like Identity. The Identity domain exposes APIs for customer addresses that the Shipping domain breaks without access to. You might even want to directly model the hard dependency on specific external API’s inside the Identity domain so that its easy to identify issues and notify the right people in charge of the Shipping domain when something breaks in those APIs.
Confusingly, many of the default relationships terms that can be used in the YAML spec
definition don not map directly to the actual relationship in the catalog. For instance if you want to add a partOf
relationship for a Domain to another Domain, you would need to use subdomainOf
. Adding it as an explicit partOf
field would not work unless you add that in the extended processor as we’ll see shortly. Each Kind has its own differing semantics for each relationship type, which needs to be referenced and checked to ensure a relationship is correctly added.
Expanding available relationships
Luckily its an easy engineering task to add a processor that can handle more expansive relationships and even new terms for those relationships such as manages/managedBy
for Users. This processor would run in addition to the BuiltinKindsEntityProcessor
that does the default relationship processing for the catalog and would look something like this:
import { CatalogProcessor, CatalogProcessorEmit, processingResult } from '@backstage/plugin-catalog-node';
import { Entity, getCompoundEntityRef, parseEntityRef, RELATION_DEPENDENCY_OF, RELATION_DEPENDS_ON } from '@backstage/catalog-model';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { RELATIONSHIP_MANAGED_BY, RELATIONSHIP_MANAGES } from '../constants';
export class ExtendedRelationshipsProcessor implements CatalogProcessor {
getProcessorName(): string {
return 'ExtendedRelationshipsProcessor';
}
postProcessEntity(
entity: Entity,
_location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity> {
const selfRef = getCompoundEntityRef(entity);
function doEmit(
targets: string | string[] | undefined | null,
context: { defaultKind?: string; defaultNamespace: string },
outgoingRelation: string,
incomingRelation: string,
): void {
if (!targets) {
return;
}
for (const target of [targets].flat()) {
const targetRef = parseEntityRef(target, context);
emit(
processingResult.relation({
source: selfRef,
type: outgoingRelation,
target: {
kind: targetRef.kind,
namespace: targetRef.namespace,
name: targetRef.name,
},
}),
);
emit(
processingResult.relation({
source: {
kind: targetRef.kind,
namespace: targetRef.namespace,
name: targetRef.name,
},
type: incomingRelation,
target: selfRef,
}),
);
}
}
if (entity.kind === 'Domain') {
doEmit(
entity.spec?.dependsOn as string[],
{ defaultKind: 'System', defaultNamespace: selfRef.namespace },
RELATION_DEPENDS_ON,
RELATION_DEPENDENCY_OF,
);
doEmit(
entity.spec?.dependencyOf as string[],
{ defaultNamespace: selfRef.namespace },
RELATION_DEPENDENCY_OF,
RELATION_DEPENDS_ON,
);
}
if (entity.kind === 'User') {
doEmit(
entity.spec?.managedBy as string,
{ defaultKind: 'User', defaultNamespace: selfRef.namespace },
RELATIONSHIP_MANAGED_BY,
RELATIONSHIP_MANAGES,
);
doEmit(
entity.spec?.manages as string[],
{ defaultKind: 'User', defaultNamespace: selfRef.namespace },
RELATIONSHIP_MANAGES,
RELATIONSHIP_MANAGED_BY,
);
}
if (entity.kind === 'Group') {
doEmit(
entity.spec?.managedBy as string,
{ defaultKind: 'User', defaultNamespace: selfRef.namespace },
RELATIONSHIP_MANAGED_BY,
RELATIONSHIP_MANAGES,
);
}
return Promise.resolve(entity);
}
}
.
Questions to consider
There is a rational that restricting the available relationships for each kind in the catalog prevents misuse of relationships by users adding things to the catalog. However it is worth bearing in mind that relationships in the catalog are mostly just syntactic sugar over the same link between two entities. There is nothing different about dependsOn
and partOf
in Backstage except the perceived meaning of these terms.
Educating users
Easily accessible documentation on standards and definitions is the best way to educate contributing users to your Backstage catalog. Backstage has its own schema docs and descriptions on existing relationships but you can also host internal documentation in a more compact format that is easier to reference quickly.
What we did to fix it
At Roadie, we’ve added expanded relationships for all kinds in the catalog to make it easier for people to correctly add relationships using the terms they understand. We’ve documented them in a compact format and are working on a UI based editor for these relationships so that users do not have to manually edit each YAML file and go through a PR process to build up a comprehensive mapping of dependencies in an organization.
We've also added a new card that can show all relationships for an entity, regardless of what kind of relationship it is in list format.
Contact us via Discord on on this website's chat to find out more about what Roadie can help with or to talk to our engineers about this topic or other issues you might be having adopting Backstage.
Top comments (2)
Relationships in Backstage can be quite confusing for people. This definitely helps add some clarity.
Nice read, thanks @sam_blausten_ad9891c7a528 - Backstage is quite opinionated about how relationships are handled but fortunately it is also extensible enough to permit new ones to be added.