The Prototype
A prototype supplies entity declarations, operations, and events that components inherit. Each component can extend the declarations or override implementations with its own behavior, while retaining exclusive ownership of its entities. Inherited operations run as endpoints of that component.
Applications can define prototypes for shared domain behavior, and prototypes can inherit from
other prototypes. A component can choose a prototype or declare prototype: null to inherit
none.
§The generic prototype
A component that declares an entity inherits Toa’s generic prototype by default; a component
without an entity declaration inherits none by default. The generic prototype supplies system
properties, common operations, and state-change events. For example, orders.observe reads an
order even when the component has no observation implementation of its own.
The runtime maintains these properties alongside the entity’s business state:
| Property | Meaning |
|---|---|
id | The entity’s identity. |
CREATED | When it was created. |
UPDATED | When its state was last written. |
DELETED | When it was marked as deleted, or null while active. |
VERSION | Its state revision, used for concurrency control. |
REGION | The rank of the region that last wrote its state. |
Operations read the timestamps as milliseconds since the Unix epoch.
| Endpoint | Type | Behaviour |
|---|---|---|
transit | Transition | Merges input into entity state and returns it. |
observe | Observation | Returns one entity’s state. |
assign | Assignment | Applies input as a changeset. |
enumerate | Observation | Returns a selected set of entities. |
stream | Observation | Returns a stream of entity state. |
ensure | Effect | Returns an entity, creating it from supplied state if it does not exist. |
terminate | Assignment | Marks an entity as deleted. |
The prototype’s events announce changes to entity state so other components can react:
| Event | Published when |
|---|---|
created | A new entity is committed. |
updated | An existing entity is written, including when it is marked as deleted. |
deleted | The committed entity state is marked as deleted. |
§Refinement and forwarding
An inherited operation can be given a more specific contract without replacing its
implementation. The generic transit operation, for example, merges input into entity state:
export function transition (input: Input, entry: State) {
return Object.assign(entry, input)
} A component can refine it to accept only an order total:
operations:
transit:
input:
type: object
properties:
total: { type: integer, minimum: 0 }
required: [total]
additionalProperties: false The runtime validates the input against this contract, then runs the inherited implementation. The component changes what the endpoint accepts, while retaining how it applies the change. This is a refinement of the declaration; overriding would replace the implementation itself.
Alternatively, forwarding exposes that implementation under a different endpoint with its own declaration:
operations:
setTotal:
forward: transit
input:
type: object
properties:
total: { type: integer, minimum: 0 }
required: [total]
additionalProperties: false Here, orders.setTotal uses the inherited transition without a new function or a second
operation call. The domain name and contract belong to setTotal; the implementation comes
from transit.
The prototype marks transit as virtual: it supplies reusable behavior without exposing an
endpoint by default. The first declaration exposes transit explicitly; the second exposes
only setTotal.