Product–Platform Separation
§The problem
Backend services combine business logic with supporting machinery.
Consider a service that approves an order. The business logic is one line:
order.status = 'approved' Running that line in a real system also requires the service to:
- receive the request over some transport and deserialize it,
- validate the request against a schema,
- load the current state of the order from a database,
- handle concurrent modifications to the order,
- persist the new state, with versioning and timestamps,
- publish an “order approved” event so other services can react,
- reply to the caller — or report a failure in a way the caller can handle,
- and, around all of that: configuration, secrets, service discovery, retries, connection management, logging, tracing, deployment.
In a typical codebase, the one line of business logic is buried inside hundreds of lines of this machinery. The machinery is not unique to the order service — every service in the system reimplements the same patterns, each with its own subtle bugs. Worse, the machinery and the logic are entangled: the business rule “an order becomes approved” cannot be read, tested, or changed without touching transport, storage, and serialization code.
§Operations and declarations
Toa splits a service into two parts:
Logic — functions written by the application developer. In Toa they are called operations. The order approval above is a complete, valid Toa operation:
// operations/approve.ts
export async function transition (input: Input, entry: State, context: Context) {
entry.status = 'approved'
} Mechanics — everything else, provided by the runtime and driven by declarations. The developer declares what the service is, not how it works:
# manifest.toa.yaml
name: orders
entity:
schema:
properties:
status: { type: string, enum: [pending, approved] }
operations:
approve:
concurrency: retry From these declarations the runtime derives the machinery: it validates incoming requests against the schema, retrieves the order from storage before calling the function, persists the returned state after, guards the write with optimistic concurrency control, and emits events when the state changes.
Notice what the approve function above does not contain: no database client, no message
broker, no HTTP. It receives plain values and returns a plain value.
Business logic stays within the boundaries set by the runtime: state and external interactions pass through the interfaces it provides.
These constraints are what make the mechanics possible to outsource. The runtime establishes a common model for application logic, state, and communication. Applications follow that model so the runtime can take responsibility for their execution. The trade-off is freedom in how the application is structured in exchange for less machinery to build and maintain.
This is what low-code means in Toa. Not visual programming, and not “less capable” — it means the code that remains is almost entirely business logic, while the mechanics are declared and outsourced to the runtime.
The result is a system where a service is often a handful of one-liner functions and a YAML manifest — yet runs as a set of independently scalable, fault-tolerant processes with eventual consistency guarantees.