architecture-as-code icon indicating copy to clipboard operation
architecture-as-code copied to clipboard

Is RelationshipType defined properly in CALM?

Open LeighFinegold opened this issue 10 months ago • 5 comments

Support Question

For a lot of tooling e.g. visualiser, generator we are defining a model to work with.

Whilst experimenting with json schema to pojo generator as an additional replacement for the java generator module (#702) , a question arose around the relationship type modelling

Option 1

export interface Relationship {
  "unique-id": string;
  description?: string;
  "relationship-type": {
    interacts?: InteractsType;
    connects?: ConnectsType;
    "deployed-in"?: DeployedInType;
    "composed-of"?: ComposedOfType;
    [k: string]: unknown;
  };

represented by this json schema definition

 "relationship": {
      "type": "object",
      "properties": {
        "unique-id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "relationship-type": {
          "type": "object",
          "properties": {
            "interacts": {
              "$ref": "#/defs/interacts-type"
            },
            "connects": {
              "$ref": "#/defs/connects-type"
            },
            "deployed-in": {
              "$ref": "#/defs/deployed-in-type"
            },
            "composed-of": {
              "$ref": "#/defs/composed-of-type"
            }
          },
          "oneOf": [
            {
              "required": [
                "deployed-in"
              ]
            },
            {
              "required": [
                "composed-of"
              ]
            },
            {
              "required": [
                "interacts"
              ]
            },
            {
              "required": [
                "connects"
              ]
            }
          ]
        }
}

Even if we keep with this option, in all json schema examples, I don't think the property section should exist

Option 2

export interface Relationship {
  "unique-id": string;
  description?: string;
  "relationship-type": ComposedOfType | InteractsType | ConnectsType | DeployedInType;

which is represented

"relationship": {
      "type": "object",
      "properties": {
        "unique-id": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "relationship-type": {
          "oneOf": [
            { "$ref": "#/defs/composed-of-type" },
            { "$ref": "#/defs/interacts-type" },
            { "$ref": "#/defs/connects-type" },
            { "$ref": "#/defs/deployed-in-type" }
          ]
        }
}

This seems more inline with Device Type example on https://json-schema.org/learn/json-schema-examples

LeighFinegold avatar Jan 23 '25 16:01 LeighFinegold

Option 2 certainly seems cleaner if it's valid.

rocketstack-matt avatar Jan 23 '25 18:01 rocketstack-matt

I think this is a good example of #818

I agree with @LeighFinegold and @rocketstack-matt that option 2 is better, but it does loosen the JSON Schema validation of a relationship. This might not be an issue, if we agree that our JSON schema isn't perfect but pragmatic.

Current Implementation:

Enforces a stricter validation logic since the relationship-type must be an object with defined properties and exactly one property from the oneOf list.

Proposed Implementation:

Looser validation since relationship-type can directly resolve to one of the four $ref types without enforcing additional structure.

Summary:

  • If the intent is to have relationship-type always be an object with exactly one of the specified properties (interacts, connects, etc.), we need what we have.
  • If the intent is for relationship-type to be one of several types (not necessarily an object with a specific property). However, if we say that we're happy to loosen the constraint here and have spectral assert that it is valid CALM - then that would be fine.

jpgough-ms avatar Jan 24 '25 08:01 jpgough-ms

Just to add some support for option 2 as well, I actually already modeled it this way in the manual TypeScript types that I wrote for CALM a while back https://github.com/finos/architecture-as-code/blob/main/shared/src/types.ts#L19

I think having the weaker JSON Schema validation is a worthwhile trade off.

aidanm3341 avatar Jan 24 '25 13:01 aidanm3341

I'd missed the subtlety . . . but is it looser or just different? Isn't the difference that in Option 2 it simply can't have additional properties, but is still strict in the sense that it must be one of the declared relationship types?

rocketstack-matt avatar Jan 27 '25 12:01 rocketstack-matt

I think the outcome of this discussion is that yes, it is defined correctly in CALM, however there is a question about whether we change the schema for a slightly looser definition and use validation etc going forward.

@LeighFinegold I think we should close this issue and then put it through @rocketstack-matt new Schema Proposal flow after we get v1 out. We can leave it open until that happens - I'll assign you and tag as won't fix

jpgough-ms avatar Feb 28 '25 09:02 jpgough-ms

I believe Option 2 could not distinguish between a composed-of and deployed-in relationship, as both types have two properties - container and nodes. There is no other required distinguishing property.

Option 3

Directly aligned with the Device Type example on https://json-schema.org/learn/json-schema-examples#device-type, using the tagged union concept.

"relationship": {
   "type": "object",
   "properties": {
     "unique-id": {
       "type": "string"
     },
     "description": {
       "type": "string"
     },
     "relationship-type": {
       "type": "string"
     }
   },
   "oneOf": [
      { "properties": { "relationship-type" : { "const" : "composed-of" } }, "$ref": "#/defs/composed-of-type" },
      { "properties": { "relationship-type" : { "const" : "interacts" } }, "$ref": "#/defs/interacts-type" },
      { "properties": { "relationship-type" : { "const" : "connects" } }, "$ref": "#/defs/connects-type" },
      { "properties": { "relationship-type" : { "const" : "deployed-in" } }, "$ref": "#/defs/deployed-in-type" },
   ]
}

which would give a much flatter relationship object to look like:

{ 
   "unique-id" : "my-id",
   "description": "widget containment",
   "relationship-type": "deployed-in",
   "container": "node1",
   "nodes" : [ "node2", "node3", "node4" ]
}

markscott-ms avatar Jul 17 '25 20:07 markscott-ms

As mentioned, this needs to go through the new new schema proposal workflow if we feel that this is still needed

jpgough-ms avatar Sep 23 '25 17:09 jpgough-ms