Your First Action: Defining and Executing a Business Operation
In the first three tutorials, we learned how to deploy coomia-dip, create Ontology models, and read/write data. In real enterprise applications however, data operations are rarely simple CRUD — they are composite operations with business rules. For example, "employee onboarding" involves creating an employee record, assigning a department, provisioning access, and sending notifications. Actions in coomia-dip are the core abstraction for encapsulating such business operations.
“Series: S12 Developer Tutorials · Article 4 | Level: Beginner | Reading Time: 15 min
Your First Action: Defining and Executing a Business Operation
#Introduction
In the first three tutorials, we learned how to deploy coomia-dip, create Ontology models, and read/write data. In real enterprise applications however, data operations are rarely simple CRUD — they are composite operations with business rules. For example, "employee onboarding" involves creating an employee record, assigning a department, provisioning access, and sending notifications. Actions in coomia-dip are the core abstraction for encapsulating such business operations.
Action is coomia-dip's implementation of Palantir Foundry's Action Type. It lets you define business operations with parameters, validation rules, and side effects, callable uniformly through the SDK or API. Action execution is transactional — either all steps succeed or everything rolls back.
#Core Concepts
#What is an Action?
An Action is an executable business operation definition containing:
- Name and description: Identifies what the operation does
- Parameters: Required inputs for the operation
- Validations: Business rule checks before execution
- Operations: Actual data mutations to perform
- Side Effects: Post-operation notifications, logging, etc.
#Action vs Direct CRUD
| Dimension | Direct CRUD | Action |
|---|---|---|
| Business semantics | None | Clear business meaning |
| Parameter validation | Manual | Declarative rules |
| Transactions | Manual management | Automatic |
| Audit logging | Manual recording | Automatic |
| Access control | Data-based | Operation-based |
| Discoverability | None | Auto-exposed via API |
#Creating Your First Action
#Scenario: Employee Onboarding
from ontology_sdk import OntoPlatform
platform = OntoPlatform(
control_plane_url="localhost:50051",
data_plane_url="localhost:50052"
)
onboard_action = platform.actions.create(
name="onboard_employee",
display_name="Employee Onboarding",
description="New employee onboarding: create record, assign department, set permissions",
parameters={
"name": {
"type": "string",
"required": True,
"description": "Employee name"
},
"email": {
"type": "string",
"required": True,
"description": "Work email",
"constraints": {"format": "email"}
},
"department_id": {
"type": "string",
"required": True,
"description": "Department ID"
},
"title": {
"type": "string",
"required": True,
"description": "Job title"
},
"level": {
"type": "integer",
"required": True,
"constraints": {"min": 1, "max": 15}
},
"salary": {
"type": "decimal",
"required": True,
"description": "Monthly salary"
},
"hire_date": {
"type": "date",
"default": "today"
}
},
validations=[
{
"name": "check_department_exists",
"type": "object_exists",
"object_type": "Department",
"primary_key": {"ref": "parameters.department_id"},
"error_message": "Department does not exist"
},
{
"name": "check_email_unique",
"type": "unique_check",
"object_type": "Employee",
"property": "email",
"value": {"ref": "parameters.email"},
"error_message": "Email already in use"
}
],
operations=[
{
"type": "create_object",
"object_type": "Employee",
"properties": {
"employee_id": {"ref": "generated.employee_id"},
"name": {"ref": "parameters.name"},
"email": {"ref": "parameters.email"},
"title": {"ref": "parameters.title"},
"level": {"ref": "parameters.level"},
"salary": {"ref": "parameters.salary"},
"hire_date": {"ref": "parameters.hire_date"},
"is_active": True
},
"output_ref": "new_employee"
},
{
"type": "create_relation",
"relation_type": "belongs_to",
"source": {"ref": "new_employee.rid"},
"target": {"ref": "parameters.department_id"},
"properties": {
"joined_at": {"ref": "parameters.hire_date"},
"role_in_dept": "member"
}
}
],
side_effects=[
{
"type": "send_notification",
"channel": "email",
"template": "welcome_employee",
"to": {"ref": "parameters.email"}
},
{
"type": "audit_log",
"event": "employee_onboarded"
}
]
)
#Executing Actions
result = platform.actions.execute(
action_name="onboard_employee",
parameters={
"name": "Alice Chen",
"email": "alice.new@example.com",
"department_id": "DEPT-001",
"title": "Junior Engineer",
"level": 3,
"salary": 80000.00
}
)
print(f"Status: {result.status}")
print(f"Employee RID: {result.outputs['new_employee']['rid']}")
# Handling validation errors
if result.status == "VALIDATION_ERROR":
for error in result.validation_errors:
print(f"Validation failed: {error.validation_name} - {error.message}")
#More Action Examples
#Employee Transfer
transfer_action = platform.actions.create(
name="transfer_employee",
display_name="Employee Transfer",
description="Transfer employee from one department to another",
parameters={
"employee_id": {"type": "string", "required": True},
"new_department_id": {"type": "string", "required": True},
"new_title": {"type": "string"},
"effective_date": {"type": "date", "default": "today"}
},
validations=[
{
"name": "check_employee_active",
"type": "property_check",
"object_type": "Employee",
"primary_key": {"ref": "parameters.employee_id"},
"property": "is_active",
"expected": True,
"error_message": "Employee is inactive"
}
],
operations=[
{
"type": "delete_relation",
"relation_type": "belongs_to",
"source_filter": {"employee_id": {"ref": "parameters.employee_id"}}
},
{
"type": "create_relation",
"relation_type": "belongs_to",
"source": {"ref": "parameters.employee_id"},
"target": {"ref": "parameters.new_department_id"},
"properties": {"joined_at": {"ref": "parameters.effective_date"}}
}
]
)
#Project Status Change
change_status = platform.actions.create(
name="change_project_status",
display_name="Change Project Status",
parameters={
"project_id": {"type": "string", "required": True},
"new_status": {
"type": "enum",
"values": ["planning", "in_progress", "on_hold", "completed", "cancelled"],
"required": True
},
"reason": {"type": "string"}
},
operations=[
{
"type": "update_object",
"object_type": "Project",
"primary_key": {"ref": "parameters.project_id"},
"properties": {"status": {"ref": "parameters.new_status"}}
}
]
)
#Action Permissions
platform.actions.set_permissions(
action_name="onboard_employee",
permissions={
"allowed_roles": ["hr_manager", "admin"],
"require_approval": False
}
)
platform.actions.set_permissions(
action_name="transfer_employee",
permissions={
"allowed_roles": ["hr_manager", "department_head"],
"require_approval": True,
"approval_chain": ["department_head", "hr_director"]
}
)
#YAML Action Definition
# actions/onboard_employee.yaml
name: onboard_employee
display_name: Employee Onboarding
description: New employee onboarding process
parameters:
- name: name
type: string
required: true
- name: email
type: string
required: true
constraints:
format: email
- name: department_id
type: string
required: true
- name: title
type: string
required: true
- name: level
type: integer
required: true
constraints: {min: 1, max: 15}
- name: salary
type: decimal
required: true
validations:
- name: check_department_exists
type: object_exists
object_type: Department
primary_key: $parameters.department_id
- name: check_email_unique
type: unique_check
object_type: Employee
property: email
value: $parameters.email
operations:
- type: create_object
object_type: Employee
properties:
employee_id: $generated.employee_id
name: $parameters.name
email: $parameters.email
title: $parameters.title
level: $parameters.level
salary: $parameters.salary
is_active: true
output_ref: new_employee
- type: create_relation
relation_type: belongs_to
source: $new_employee.rid
target: $parameters.department_id
permissions:
allowed_roles: [hr_manager, admin]
#Execution History and Audit
history = platform.actions.get_execution_history(
action_name="onboard_employee",
limit=20
)
for record in history:
print(f" {record.execution_id}: {record.status} at {record.timestamp} by {record.user}")
#Comparison with Palantir Foundry
| Feature | Palantir Foundry | coomia-dip |
|---|---|---|
| Action Definition | Web UI / TypeScript | SDK + YAML + API |
| Parameter Validation | Built-in | Declarative + Custom Functions |
| Transaction Support | Yes | Yes |
| Approval Workflow | Workshop | Built-in Approval Chain |
| Audit Logging | Automatic | Automatic |
| Access Control | RBAC | RBAC + ABAC |
#Best Practices
- Atomic Design: Each Action should represent one complete business operation
- Thorough Validation: Validate all business rules before operations
- Clear Parameters: Every parameter needs type, description, and constraints
- Separate Side Effects: Notifications and logging should not affect core operations
- Least Privilege: Grant only necessary execution permissions
- Version Control: Keep YAML Action definitions in version control
#Summary
In this tutorial, we learned about coomia-dip's Action mechanism: defining business operations with parameters, validations, operations, and side effects. Actions bridge data models and business processes, encapsulating complex logic into reusable, auditable, and controllable operation units.
This is Article 4 in the coomia-dip Developer Tutorial series. Repository: https://github.com/coomia-dip/coomia-dip↗ | License: Apache License 2.0