Core concepts
Every Blueprint concept, in the order you meet it, shown on one running example: making and releasing a batch of tablets.
The running example
A plant makes tablets in batches. These are the pieces you will meet on this page:
| Piece | What it is |
|---|---|
| Batch | One run of tablets, such as B-2417 |
| Product | What the batch makes |
| Equipment | Where the batch is made |
| Material lot | What the batch consumes |
| Batch step | One recorded step of the batch |
| Deviation | Something that did not go as planned |
| QA batch release | A reviewer's signed decision to release or reject the batch |
| Master batch record | The governed instructions every batch runs against |
| Cleaning module | A separate feature that adds cleaning details to equipment |
Snippets are shortened sketches. The tutorial has complete declarations. Items marked planned are designed but not built yet; the primitives catalog shows the status of every part.
What exists: the model
Entity
A thing with an identity. An entity is one kind of thing your business tracks. It has a stable identity that every screen, API, and record refers to.
In batch production: Batch, Product, Equipment, Material lot, Batch step, and Deviation are entities.
const batch = Entity(
name: 'batch',
facets: [
IdentityFacet(fields: [CodeField(), NameField()]),
],
);Facet
One aspect of a thing. A facet groups the fields, actions, and rules for one aspect of an entity. Another module can add a facet to an entity it does not own, through a descriptor, without copying the entity.
In batch production: a batch has an identity facet, a production facet (quantity, line, start time), and a release facet (decision, reason). The cleaning module adds a cleaning facet to Equipment.
production.equipment
identity ← owned by the equipment module
cleaning ← contributed by the cleaning moduleRelationship
How things connect. Relationships give the model its graph: what belongs to what, and what was used for what. When the connection itself has dates, status, or evidence, make it an entity of its own.
In batch production: a batch is of a product and runs on equipment. Each material lot it consumes is recorded as its own consumption record, so genealogy can be traced lot by lot.
static const product = Relationship(
name: 'product',
target: EntityRef(module: ModuleRef('batch_production'), name: 'product'),
cardinality: RelationshipCardinality.hasOne,
field: BatchFields.productId,
);What changes: the behaviour
Action
The only way to change. Every change is an action. It declares its input, who may run it, the rules it must pass, the evidence it needs, and what follows. The server runs it in one transaction.
In batch production: start batch, record step, raise deviation, release batch, reject batch.
request → check access and rules → plan → lock
→ recheck in the transaction → write → record evidence
→ save effects to the outbox → deliver them after commitRule
When a change is allowed. A rule holds a test an action must pass. The test is a typed expression that is true, false or unknown; only a known true passes, so a rule that cannot be decided blocks. The screen uses rules to show what is possible; the server enforces them. A hidden button is feedback; the server is the authority.
In batch production: release needs every step complete, no open deviation, and a signer who is not the person who made the batch.
release batch
✓ the actor holds the release grant
✓ the batch is under review
✓ the signer did not start the batch (separation of duties)
◇ no deviation on this batch is still open (planned)Rules over the acting record run today. Rules that look at related records, such as an open deviation, need cross-record tests and relationship aggregates. planned
Lifecycle
The states a thing moves through. A lifecycle names the states of a facet and the actions that move between them. The database guards the state too.
In batch production:
planned → in production → under review → released
↘ rejectedTrigger
What starts an action. Today an action starts from a request, made by a person or another system through the API, or from a step in a workflow. A facet can also subscribe to another facet's state change.
In batch production: an operator starts the batch. A step in the review workflow opens the release task.
Declared triggers for a time, a schedule, a change to another record, and an inbound event from an instrument are planned, along with recording on every ledger record which trigger caused it.
planned Then a hold time that expires at06:00, or a balance that sends a weight, can start an action directly.
Effect
What an action asks for next. An event records what committed. An effect asks for follow-up work: another action or a workflow. Effects are saved with the change and delivered after it commits, so none are lost or repeated.
In batch production: completing the last step starts the batch review workflow.
effects: [
Effect.workflow(
workflow: WorkflowRef(module: ModuleRef('production'), name: 'batch_review'),
),
],Notification and integration effects, such as telling the ERP that stock is available, and effects guarded by a test, are planned.
planned Today you reach another systemthrough a durable effect binding.
Task and workflow
Work for people, in order. A workflow orders the steps of a process. Its definition is authored and versioned, and each run is pinned to the version it started with. When a step needs a person, Blueprint opens a task: who can do it, what they must fill in, and the outcomes they can choose. Completing the task is an action like any other, so it is checked, signed, and recorded the same way.
In batch production: batch review opens a task for the QA pool. One reviewer claims it, checks the record, and chooses release or reject, with a reason and an e-signature.
workflow batch_review
step open task → QA pool (not the person who started the batch)
task claim → submit → decide: release | reject
step run the decision as the release or reject actionWho may act and who is assigned are both an actor expression, built with a builder such as Actors.role(...):
AssignmentSpec(actors: Actors.role(const RoleRef('production.reviewer')))Transferring, delegating, and escalating a task are planned.
plannedWhat happened: the record
Ledger
Every action, remembered. Each action writes one ledger record: who acted, when, why, what changed, and the electronic signature when one is required. The signature is checked inside the same transaction. Any record can be replayed from the ledger.
In batch production:
QA reviewer released B-2417 at 14:32
meaning Approved
reason All steps complete, no open deviations
via review task T-88Versions and revisions
Technical history and business history. They are two different things.
| Version | Revision | |
|---|---|---|
| What | An immutable snapshot on every save | A governed snapshot people work to |
| Status | None; it has a number | Draft, in review, effective, superseded |
| Approval | Nobody approves it | Approved before it takes effect |
In batch production: the master batch record is revisioned, so batches run against "Rev 4, effective 1 October". Each batch itself only keeps versions.
Approval is declared in one place, on the history:
History.revisions(
approval: RevisionApprovalPolicy(),
)That puts the master batch record under approval. The governing policy in effect for the tenant sets how many approvals a manual submit, publish, or retire needs. With none, the actor's own grant decides and the record says no approval was required; with one or more, only the approval workflow moves a revision. A missing or ambiguous policy is refused, never read as zero. Without approval:, revisions are submitted, published, and retired by hand.
Retiring a revision through its approval workflow, and approving one now to take effect on a future date, are planned.
plannedThe behaviour loop
Anything can start an action. Every action is remembered, and its effects can start new work.
Time, record-change, and instrument triggers join the same loop when they land. planned
How it is assembled
Module
A collection of entities. A module owns a set of entities and their actions, and declares what it depends on. An application is a Blueprint: a list of modules, assembled and checked together. Identity, access, and workflow are modules too.
In batch production: Production owns batches and steps. Equipment owns equipment. Cleaning adds its facet to Equipment.
Blueprint 'plant'
├─ Identity and access
├─ Workflow
├─ Equipment ← Cleaning adds a facet
├─ Cleaning
└─ Production → depends on Equipmentfinal blueprint = Blueprint(
name: 'plant',
version: '1.0.0',
modules: [iamModule, workflowModule, equipmentModule, cleaningModule, productionModule],
);
final effective = blueprint.assemble();Typed module definitions, where a module is its own Dart type with typed parameters and one member per aspect, are planned.
plannedConfiguration
What varies per site. Configuration supplies the values a deployment may choose: access grants, who may approve, policy values, and connected services. Change configuration for supported variation; change the declaration when the meaning changes.
In batch production: Plant A and Plant B run the same release action. Each plant grants release to its own QA reviewers.
Using a policy value directly inside a rule or a schedule, such as a bulk hold time of 72 hours at Plant A and 48 hours at Plant B, is planned.
plannedEvolution
Changing the model safely. When the model changes, Blueprint classifies the impact, plans typed migrations, and lets you rehearse them before they run.
In batch production: adding a yield field to the production facet produces a migration you review before deploying.
blueprint db emit --product plant
blueprint db migration new add_yield --product plantSee product migrations.
Where to go next
| To | Read |
|---|---|
| See why the model is shaped this way | Why Blueprint |
| Keep meaning, configuration, and execution apart | The three planes |
| Check what runs today | Primitives catalog |
| Build it step by step | Tutorial |