Architectural Models

Ownership and RACI Modeling

Build an enterprise ownership and RACI graph using standard CSV assets, model rules, and compliance targets.

Ownership and RACI Modeling

A flat property like owner = "team-alpha" on an application is only a label. To make ownership auditable and queryable, promote the organizational concepts to first-class graph citizens: party, provider, and role. This guide shows how to build that graph with standard rescile features.

Core ontology

Resource Purpose Example primary keys
party Ultimate legal counterparty internal-org, External Vendor Inc.
provider Operational team or service offering team-alpha, external-db-vendor
role Formal organizational function owner, maintainer, data_processor
Relation Direction Meaning
HAS_RESPONSIBILITY asset → provider This asset is managed by that provider for a specific role.
OPERATED_BY provider → party The provider belongs to / is legally covered by that party.

You can extend the ontology with IS_PERFORMED_BY (role → provider) or any other relation by adding the corresponding CSV assets and link rules.

Foundation data

Create the organizational resources as ordinary CSV assets.

data/assets/party.csv

name,jurisdiction,type
internal-org,EU,Internal
External Vendor Inc.,US,Vendor

data/assets/provider.csv

name,operated_by,service_category
team-alpha,internal-org,Engineering
external-db-vendor,External Vendor Inc.,Managed Services

data/assets/role.csv

name,description
owner,Accountable for the asset
maintainer,Responsible for technical uptime and patching
data_processor,Legal entity processing data on behalf of the controller

data/assets/application.csv

name,environment,owner,maintainer
myapp,production,team-alpha,team-alpha

data/assets/database.csv

name,environment,data_processor
mydb,production,external-db-vendor

A single model rule turns the operated_by foreign key into a graph edge.

data/models/provider_party.toml

origin_resource = "provider"

[[link_resources]]
name = "Provider is operated by legal party"
with = "party"
join = { local = "operated_by", remote = "name" }
create_relation = { type = "OPERATED_BY" }

Graph result: (provider:team-alpha) -[OPERATED_BY]-> (party:internal-org)

Step 2: Map asset responsibility properties to providers

Use one compliance control per role. The template in match_on looks up the provider whose name equals the role-named property on the asset, and the relation carries the role as a property.

data/compliance/ownership.toml

[[control]]
id = "OWN-OWNER"
name = "Map owner property to provider"
[control.config]
role = "owner"
[[control.target]]
origin_resource_types = ["application"]
[control.target.resource]
type = "provider"
match_on = [
  { property = "name", value = "{{origin_resource.owner}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]

[[control]]
id = "OWN-MAINTAINER"
name = "Map maintainer property to provider"
[control.config]
role = "maintainer"
[[control.target]]
origin_resource_types = ["application"]
[control.target.resource]
type = "provider"
match_on = [
  { property = "name", value = "{{origin_resource.maintainer}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]

[[control]]
id = "OWN-DATA-PROCESSOR"
name = "Map data_processor property to provider"
[control.config]
role = "data_processor"
[[control.target]]
origin_resource_types = ["database"]
[control.target.resource]
type = "provider"
match_on = [
  { property = "name", value = "{{origin_resource.data_processor}}" }
]
[control.target.relation]
type = "HAS_RESPONSIBILITY"
properties_from_config = ["role"]

Graph result:

(application:myapp) -[HAS_RESPONSIBILITY {role: "owner"}]-> (provider:team-alpha)
(application:myapp) -[HAS_RESPONSIBILITY {role: "maintainer"}]-> (provider:team-alpha)
(database:mydb) -[HAS_RESPONSIBILITY {role: "data_processor"}]-> (provider:external-db-vendor)

Add more controls as you introduce new roles. All controls use the same standard compliance primitives; there is no special-purpose engine for responsibility.

Step 3: Query the ownership graph

The graph now supports precise queries. For example, list every asset, its responsibilities, and the legal party behind each responsible provider.

query AssetResponsibilities {
  vertices(filter: { labels: ["application", "database"] }) {
    name
    label
    HAS_RESPONSIBILITY {
      name
      service_category
      _relation {
        role
      }
      OPERATED_BY {
        name
        jurisdiction
      }
    }
  }
}

Expected result shape:

{
  "data": {
    "vertices": [
      {
        "name": "myapp",
        "label": "application",
        "HAS_RESPONSIBILITY": [
          {
            "name": "team-alpha",
            "service_category": "Engineering",
            "_relation": { "role": "owner" },
            "OPERATED_BY": [
              { "name": "internal-org", "jurisdiction": "EU" }
            ]
          },
          {
            "name": "team-alpha",
            "service_category": "Engineering",
            "_relation": { "role": "maintainer" },
            "OPERATED_BY": [
              { "name": "internal-org", "jurisdiction": "EU" }
            ]
          }
        ]
      }
    ]
  }
}

Variation: explicit responsibility table

If you prefer not to encode responsibilities as scattered properties on each asset, centralize them in a dedicated edge asset.

data/assets/responsibility.csv

source_name,source_type,role,target_name
myapp,application,maintainer,team-alpha
myapp,application,owner,team-alpha
mydb,database,data_processor,external-db-vendor

Then use one model file per source_type to link to the provider:

data/models/application_responsibility.toml

origin_resource = "responsibility"

[[link_resources]]
match_on = [{ property = "source_type", value = "application" }]
with = "application"
join = { local = "source_name", remote = "name" }
create_relation = { type = "HAS_RESPONSIBILITY" }

[[link_resources]]
match_on = [{ property = "source_type", value = "application" }]
with = "provider"
join = { local = "target_name", remote = "name" }
create_relation = { type = "HAS_RESPONSIBILITY", properties = { role = "{{origin_resource.role}}" } }

This keeps all responsibility assignments in one auditable table.

Summary

  • Model organizational entities as ordinary CSV assets: party, provider, role.
  • Use [[link_resources]] to materialize foreign-key relationships such as OPERATED_BY.
  • Use standard compliance targets with match_on templates to create HAS_RESPONSIBILITY edges from asset role properties to providers.
  • Query through the graph to roll responsibilities up to legal parties.

There is no special-purpose responsibility engine; the pattern is built entirely from rescile’s asset, model, and compliance primitives.