Skip to main content

Custom Workflow Types

The engine supports using any identifier as a workflow type instead of being limited to the workflow keyword. This allows for more semantic and descriptive workflow definitions.

Overview​

Previously, all workflow definitions had to use the workflow keyword. Now you can use any identifier that better describes the purpose of your workflow, such as project, application, feature, solution, or any custom type.

Basic Usage​

Standard Workflow​

workflow example {
start: Initialize

state Initialize {
action services/common/response.ResponseValue(message: "Hello", statusCode: 200)
end ok
}
}

Custom Types​

project my_project {
start: Setup

state Setup {
action project/initialize.Setup()
on success -> Complete
}

state Complete {
end ok
}
}
application user_app {
start: Launch

state Launch {
action app/startup.Initialize()
on success -> Ready
}

state Ready {
action response/ResponseValue(message: "App ready", statusCode: 200)
end ok
}
}
microservice auth_service {
start: Authenticate

state Authenticate {
action auth/verify.CheckCredentials()
on success -> GenerateToken
on error -> Unauthorized
}

state GenerateToken {
action auth/token.Generate()
end ok
}

state Unauthorized {
action response/ResponseError(message: "Unauthorized", code: 401)
end error
}
}

Common Type Keywords​

While you can use any identifier, these are commonly used types:

workflow​

Standard workflow for general-purpose task orchestration.

workflow data_processing {
start: Process
// ...
}

feature​

Groups related workflows together. Features can orchestrate multiple workflows.

feature user_management {
start: CreateUser
// Can call workflow items
}

solution​

High-level orchestration that can combine features and workflows.

solution complete_system {
start: Initialize
// Can call feature and workflow items
}

project​

For project-specific implementations or prototypes.

project analytics_dashboard {
start: Setup
// ...
}

application​

For full application workflows.

application mobile_app {
start: Launch
// ...
}

Custom Types​

Create your own types based on your domain:

pipeline data_pipeline {
start: Ingest
// ...
}

job batch_job {
start: Execute
// ...
}

task scheduled_task {
start: Run
// ...
}

Type in Code​

The workflow type is available in the Flow domain object and can be used for conditional processing:

// The workflow type is available in the Flow domain object
flow := engine.GetFlow()
workflowType := flow.Type

// Use the type for conditional processing
switch workflowType {
case "project":
// Handle project-specific logic
case "application":
// Handle application-specific logic
case "workflow":
// Handle standard workflow logic
default:
// Handle custom types
}

Use Cases​

1. Semantic Clarity​

Use type names that clearly communicate intent:

pipeline etl_pipeline {
start: Extract

state Extract {
action data/extract.FromSource()
on success -> Transform
}

state Transform {
action data/transform.Apply()
on success -> Load
}

state Load {
action data/load.ToDestination()
end ok
}
}

2. Organizational Hierarchy​

// Top-level solution
solution ecommerce_platform {
start: InitializeFeatures
// Orchestrates features
}

// Mid-level features
feature payment_processing {
start: InitializePayments
// Orchestrates workflows
}

// Low-level workflows
workflow process_payment {
start: ValidateCard
// Executes steps
}

3. Domain-Specific Language​

Create types that match your business domain:

order order_fulfillment {
start: ReceiveOrder
// ...
}

shipment track_shipment {
start: CreateShipment
// ...
}

invoice generate_invoice {
start: CalculateTotal
// ...
}

Implementation Details​

The workflow type is captured and propagated through the entire parsing and execution pipeline:

  1. Parser - Accepts any identifier as a workflow type token
  2. CST (Concrete Syntax Tree) - Stores the type token
  3. AST (Abstract Syntax Tree) - Includes a Type field
  4. IR (Intermediate Representation) - Includes a WorkflowType field
  5. Schema - Includes a type field
  6. Domain Flow - The Flow.Type field is populated from the schema

Best Practices​

1. Be Consistent​

Use consistent type names across your project:

// Good - consistent naming
workflow user_login { ... }
workflow user_logout { ... }
workflow user_profile { ... }

// Avoid - inconsistent types
workflow user_login { ... }
process user_logout { ... }
handler user_profile { ... }

2. Use Hierarchical Types​

Match your type to the level of abstraction:

  • solution - Highest level, orchestrates features
  • feature - Mid level, orchestrates workflows
  • workflow - Base level, executes steps

3. Choose Meaningful Names​

Pick names that convey the purpose:

// Good - clear purpose
pipeline data_ingestion { ... }
validator input_validator { ... }
transformer json_transformer { ... }

// Avoid - vague names
thing my_thing { ... }
stuff process_stuff { ... }

4. Document Custom Types​

When using custom types, document their purpose:

// Custom type for scheduled background jobs
// Runs periodically to clean up old data
cleanup_job database_cleanup {
start: ScanTables
// ...
}

Backward Compatibility​

This feature is fully backward compatible. All existing workflows using the workflow keyword continue to work exactly as before. The workflow keyword is simply treated as one of many possible type values.

Examples​

E-commerce System​

module ecommerce

// Solution orchestrates the entire system
solution ecommerce_platform {
start: InitializeSystem

state InitializeSystem {
action feature user_management
on success -> StartPayments
}

state StartPayments {
action feature payment_processing
on success -> Ready
}

state Ready {
end ok
}
}

// Feature for user management
feature user_management {
start: SetupAuth

state SetupAuth {
action workflow user_authentication
on success -> SetupProfile
}

state SetupProfile {
action workflow user_profile_management
end ok
}
}

// Individual workflow for authentication
workflow user_authentication {
start: ValidateCredentials

state ValidateCredentials {
action auth/validate.Credentials()
on success -> GenerateToken
on error -> LoginFailed
}

state GenerateToken {
action auth/token.Generate()
end ok
}

state LoginFailed {
action response/ResponseError(message: "Login failed", code: 401)
end error
}
}

Data Processing Pipeline​

module analytics

pipeline real_time_analytics {
start: Ingest

state Ingest {
action stream/kafka.Consume(topic: "events")
on success -> Filter
}

state Filter {
action processing/filter.Apply(rules: $constants.filterRules)
on success -> Transform
}

state Transform {
action processing/transform.Execute()
on success -> Aggregate
}

state Aggregate {
action processing/aggregate.Compute()
on success -> Store
}

state Store {
action storage/timeseries.Write()
end ok
}
}