Cohesive Systems logoCOHESIVE SYSTEMS

Search Cohesive Systems

Ready

Search Cohesive Systems

Find product pages, building blocks, technical articles, and graph definitions.

Building Blocks

Cohesive.Processes

Coordinate entity changes, requests, events, waits, and longer-running business workflows using familiar asynchronous C#.

Core Idea

A Process coordinates work that spans more than one decision, entity, participant, or moment in time. It can read information, invoke entity Transitions, request work from another capability, wait for an event or deadline, and return a typed outcome.

Cohesive.Processes lets you write that coordination with async, await, local variables, conditionals, and pattern matching. The source remains recognizable C#, while the Process runtime can pause, persist progress, resume after external input, and avoid repeating completed work.

This Process reads a customer, requests a document review, and invokes the customer's approval Transition when the review succeeds:

[GenerateProcessDefinition(nameof(Run))]
public static partial class ApproveCustomerProcess
{
    static async ProcessTask<ApproveCustomerResult> Run(
        ProcessContext process,
        ApproveCustomerInput input)
    {
        var customer = await process.Read<Customer>(
            relation: CustomerById,
            input: input.CustomerId);
 
        if (customer.Status == CustomerStatus.Suspended)
            return new(customer.Id, ApprovalStatus.NotEligible);
 
        var review = await process.Effect<DocumentReviewResult>(
            contract: RequestDocumentReview,
            outcome: ReviewCompleted,
            input: new DocumentReviewRequest(
                customer.Id,
                input.Reason));
 
        if (!review.Approved)
            return new(customer.Id, ApprovalStatus.Rejected);
 
        await process.Transition(
            transition: ApproveCustomer,
            subject: customer.Id,
            input: new CustomerApproval(input.Reason));
 
        return new(customer.Id, ApprovalStatus.Approved);
    }
}

CustomerById, RequestDocumentReview, and ApproveCustomer identify definitions supplied by the surrounding domain model. The code focuses on the business flow: obtain the customer, stop when approval is not allowed, wait for the review obligation to complete, apply the entity decision, and return the Process result.

Getting Started

The guided walkthrough installs the Process package and analyzer, authors a small asynchronous Process, and then adds a durable event-or-deadline wait. It introduces persistence and compilation only after the workflow itself is clear.

Open the Getting Started guide →

A Small Mental Model

A Process reads like an asynchronous application method, but each awaited operation represents durable business progress:

Input

Question it answers
What starts this Process?
Approval example
A customer identity and the reason approval was requested.

Process context

Question it answers
Which semantic operations can the workflow perform?
Approval example
Read a customer, request document review, and invoke ApproveCustomer.

Local values

Question it answers
What has the Process learned so far?
Approval example
The customer and completed review remain available to later decisions.

Wait or interaction

Question it answers
What external progress must occur before execution continues?
Approval example
A reviewer must complete the requested document review.

Outcome

Question it answers
How did the larger business flow finish?
Approval example
Approved, Rejected, or NotEligible with the customer identity.

The ProcessContext does not expose arbitrary services. Its operations name the reads, entity changes, requests, events, child Processes, and temporal behavior that form the workflow. This keeps the coordination visible instead of hiding it behind injected callbacks.

Why Make the Upfront Investment?

An ordinary async method is easy to write, but its call stack and local variables disappear when the process stops. Applications then rebuild durability through job records, status columns, retry flags, message handlers, and recovery scripts. Cohesive asks you to make the workflow explicit so those concerns can work from the same Process definition.

Structured workflow model

What it gives you
Reads, entity Transitions, requests, waits, branches, parallel work, and outcomes have explicit roles instead of being spread across handlers, jobs, and callbacks.

Durable progress

What it gives you
A Process can wait for external input or a deadline and continue after a restart without depending on an in-memory call stack.

Replay-safe operations

What it gives you
Completed reads, entity changes, and interactions retain receipts, so recovery can reuse accepted results instead of logically repeating work.

Explicit obligations

What it gives you
Requests state which participant owes work and which outcomes can discharge it. Events, signals, replies, and deadlines keep their distinct meanings.

Multi-entity workflows

What it gives you
One Process can invoke typed Transitions on several entities while each entity remains authoritative for its own state and invariants.

Structured diagnostics and control

What it gives you
Invalid definitions, incompatible links, failed operations, unresolved waits, and runtime state produce attributable evidence that operational tooling can explain, pause, continue, cancel, or terminate.

Deterministic testing and simulation

What it gives you
Tests can supply operation results, events, and time, then inspect each continuation and outcome. The same structure supports broader automated scenario exploration and simulation tooling.

Infrastructure agnostic

What it gives you
The workflow is independent of a particular scheduler, queue, database, or hosting model. Runtime integrations must demonstrate that they preserve its durability and interaction guarantees.

How a Process Runs

A caller starts a Process with typed input. The Process execution environment advances the definition until it reaches an operation or wait. Relation adapters perform reads, entity adapters execute Transitions, and interaction adapters deliver requests or accept events. Their typed results become the values used by the next part of the workflow.

When execution cannot continue immediately, the durable runtime commits a checkpoint containing the current continuation, accepted inputs, completed operation receipts, outgoing interactions, and control state. A later result, event, deadline, or control command starts another activation from that checkpoint.

This environment owns storage, scheduling, delivery, retries, time, and recovery. The authored C# method describes the coordination and is not kept alive as a running async method. Capability diagnostics report when a configured runtime cannot preserve the guarantees the Process requires.

Interactions, Obligations, and Time

A Process can coordinate several forms of interaction:

  • A request creates an obligation and declares the terminal outcomes that can complete it.
  • An event reports an occurrence without creating a response obligation.
  • A signal targets a particular running Process occurrence.
  • A reply completes a retained request identity.
  • A deadline makes the passage of time an explicit alternative.

The Process can wait for one interaction, or race several typed events and deadlines. It can state how simultaneous matches are resolved and what happens to late, stale, duplicate, or misdirected input. The overview example uses the simplest form: continue when the selected ReviewCompleted outcome arrives.

Parallel, Child, and Recurring Work

Some workflows need several branches to make progress together. Fork and Join can require every branch, choose the first eligible result, or continue after a required number completes. Concurrency and item limits remain explicit so a runtime cannot silently turn a bounded workflow into unbounded work.

A parent Process can start a child Process for an independently meaningful piece of work. It can also process a bounded partition of items or repeat work across activations under a declared limit. Compensation and reconciliation remain named purposes rather than hidden exception callbacks.

These constructs support patterns such as sagas without pretending that a long-running workflow provides ACID rollback across independent participants.

Where the Model Fits

Coordinate people, policies, and systems

A customer or employee onboarding Process can span several entities and wait for human or external-system outcomes.

  • Read the current applicant and policy context
  • Request document review or identity verification
  • Wait for results and deadlines
  • Invoke entity Transitions after each accepted decision

Works with the Other Blocks

  • Cohesive.Entities owns the state, invariants, and Transitions invoked as steps in a Process.
  • Cohesive.Relations supplies reusable reads and queries without making the Process responsible for data-access logic.
  • Cohesive.Storage persists Process checkpoints, operation receipts, accepted inputs, control state, and outgoing interactions.
  • Cohesive.Api can expose start, status, control, and explain operations.
  • Cohesive.Presentation can project progress, outcomes, and human tasks from the same runtime evidence.

Going Deeper

Durable coordination introduces questions that normal async code does not answer: how source becomes a restorable definition, how continuation identities survive revisions, how competing events are arbitrated, how completed operations replay, and what must commit together.

The Internals page covers source generation, canonical Process documents, compilation, identity and compatibility, wait policies, durable checkpoints, replay evidence, and runtime control.

Read Process Internals →