Skip to content

Why Blueprint ​

Blueprint brings your business model and its rules together in one place. You describe the people, information, and work your application manages, how they connect, and which changes are allowed. Blueprint carries that description into the database, server, and user interface.

You write the model in Dart. The engine uses it to check requests, carry out allowed actions, and keep a record of what happened.

The shortest useful model is:

text
Blueprint grammar
  + product declaration
  + effective configuration
  + runtime bindings
  = executable application contract

A declaration describes your business objects and rules. Configuration provides settings that vary between customers, sites, or environments. Bindings connect the model to code and external services. The runtime is the engine that checks a request, carries it out, and records the result.

Why keep the contract together? ​

Consider releasing a batch. The screen needs to know whether release is available, the API needs to authorize it, the transaction needs to check the batch state, and the record needs to retain the evidence. Maintaining those decisions independently makes a business change easy to apply incompletely.

Blueprint puts the action, its rules, and its evidence requirements into an explicit declaration. The UI consumes the resulting metadata; the server enforces the request through the runtime. A hidden button is useful feedback, but server validation remains the authority. The same boundary applies when a workflow or agent makes the request.

This changes the developer's unit of work. Instead of implementing a route, screen, and audit hook independently, start with a business action and follow its contract through those surfaces. Custom code still connects providers and uncommon behavior through bindings. The declaration makes those dependencies visible rather than making them disappear.

Why separate configuration from meaning? ​

Two sites can use the same release action while applying different configured thresholds or grants. Copying the action into two implementations makes later changes harder to compare. A shared declaration with scoped configuration keeps the operation recognizable and makes the selected values inspectable.

Use configuration for supported variation. Change the declaration when the business meaning changes. Bind external services explicitly when execution depends on them. These distinctions let a developer investigate a failure as a declaration, configuration, or binding problem instead of searching an entire application for a conditional branch.

When Blueprint helps ​

Blueprint is useful when several entry points share business rules, when relationships and lifecycle matter, or when a change must retain evidence and causal history. The declaration, bindings, and configuration take work to maintain; a small application with little shared behavior may need less structure. The benefit grows when the same contract serves multiple screens, services, and automated callers.

The developer guide shows how to apply these boundaries. The tutorial follows an executable slice, and the package reference identifies the reusable owners. Model expressiveness alone is not proof that a particular integration is running; verify its bindings and execution path.

One Dart source of truth ​

Blueprint is authored directly as Dart objects:

dart
const equipment = Entity(
  name: 'equipment',
  facets: [
    IdentityFacet(fields: [CodeField(), NameField()]),
  ],
);

Dart provides constructors, generics, sealed types, constants, refactoring, package composition, tests, and IDE navigation. Blueprint does not require an annotation processor, authored YAML, or a second schema that must be kept in sync.

Portable definition documents are derived artifacts. Studio can compose and edit those documents, but an executable application still binds the document to developer-owned Dart types and runtime implementations. Persisted JSON does not manufacture arbitrary executable classes.

Describe the meaning, then choose how to store and show it ​

A ReferenceIdField means “this value identifies another Blueprint record.” A Relationship adds graph semantics. A FieldDb says how storage should treat the field. A FieldUI describes how the interface should present it.

Those are related declarations, not interchangeable ones:

text
semantic token -> validation and wire meaning
relationship   -> graph meaning
DB hint        -> physical lowering
UI hint        -> interaction lowering

This separation keeps a database optimization from changing domain meaning and keeps a UI preference from becoming an authorization rule.

Objects have identities; facets group their details ​

An entity represents one thing, such as a person or a piece of equipment. A facet groups one aspect of that thing, such as its calibration details. Facets can declare fields, relationships, actions, rules, history requirements, and how those details appear in the interface.

Use a facet when the data is one-to-one with the entity and remains part of the same identity. Use a mapping entity when the relationship itself has repeatable rows, dates, status, lifecycle, approval, permissions, or evidence.

text
equipment.calibration       -> facet of one equipment record
user_role_assignment        -> mapping entity with its own truth

The same rule applies to manufacturing genealogy. Blueprint does not define a special direct lot-to-lot edge. A product can model a transformation occurrence as an ordinary entity with typed input, output, resource, quantity, equipment, actor, revision, and evidence relationships. That keeps the occurrence—not a shortcut edge—as the auditable fact.

Make changes through actions ​

Every business change is expressed as an Action. It brings together the input, permission checks, business rules, required evidence, possible errors, and follow-up work for that change.

text
request
  -> validate and authorize
  -> build deterministic plan
  -> acquire locks
  -> revalidate in transaction
  -> mutate state
  -> persist evidence, events, and action record
  -> commit outbox intent
  -> dispatch external work

Tasks, approvals, workflow starts, and downstream actions do not bypass this boundary. They either gate an action or leave through its typed effect channel.

Record what happened, then request the next step ​

An event records what committed. An effect asks another action, workflow, task, or external binding to do work.

Blueprint keeps all authored effects in one ordered Action.effects list:

  • ActionEffect invokes another Blueprint action;
  • WorkflowEffect starts a registered durable workflow through the outbox;
  • TaskEffect specializes action invocation to create work from a declared task template through the ordinary action-effect channel.

The source transaction persists an eventual effect's intent atomically. The external operation happens after commit and records delivery state and receipt without repeating the business mutation.

Define once. Extend through contributions. ​

Blueprint is both a way to define a business model and a way to extend it. A feature does not need to copy an existing entity or change its owning module just to contribute another aspect of that same thing.

Modules define bounded contexts and explicit exports. Entity descriptors target an entity owned by another module and contribute at several levels:

  • Entity: add derived values, named queries, and entity-level presentation.
  • Facet: introduce another dimension or enrich an existing named facet.
  • Inside a facet: contribute fields, actions, relationships, and governing behavior through the facet grammar.
  • Presentation: describe facet sections, field editors, and action placement at their respective levels.

For example, Equipment owns an equipment identity. Calibration contributes its calibration details and actions; Maintenance contributes service settings and actions. All refer to the same equipment. Repeated calibration runs and service jobs have their own identities and link back to it.

In the current engine, these cross-feature contributions are expressed through modules and descriptors assembled into one effective Blueprint. This is not an automatic extension of an independently deployed remote system. Adding a module still requires validation, generation, and the applicable installation or migration steps.

Composition must preserve clear ownership. Contributions do not silently win conflicts: validation rejects cases such as duplicate fields, duplicate named queries, and competing lifecycle declarations for the same facet.

blueprint.assemble() links those declarations into an EffectiveBlueprint:

  • module dependencies are ordered and checked;
  • same-name facet contributions are composed;
  • Blueprint, module, and entity rules are lifted onto mutating actions;
  • action and projection catalogs are derived;
  • an origin graph preserves where each effective member came from.

Compilers and runtimes consume this effective graph. They do not independently reconstruct composition rules.

Use shared building blocks and clear extension points ​

Common semantics use sealed or structurally validated Dart vocabulary. This lets validators, compilers, Studio, and runtimes handle them exhaustively.

Imperative behavior remains possible through typed references:

text
HostEvaluatorRef      -> server evaluator binding
effect reference       -> durable effect binding
WorkflowRef            -> workflow adapter
RendererRef/EditorRef  -> client UI binding

A missing required binding is a readiness failure. An untyped callback hidden inside a declaration is not.

Connect to standards while keeping one source of truth ​

Semantic graphs, provenance formats, event interchange, and industry models can be valuable interoperability views. Blueprint keeps the transactional runtime and PostgreSQL constraints authoritative for operational truth.

This avoids two common mistakes:

  • open-world semantic inference cannot silently weaken closed-world business invariants;
  • an external workflow or ontology model does not become a second transaction engine or source of truth.

Connect applications through shared meaning ​

Directory, Docs, Qualify, Operations, and Case Management can share stable identities, governing revisions, and evidence relationships. Each application owns its experience while the operational contract makes cross-application meaning explicit. See Connected Applications for an illustrative composition and its implementation boundaries.

Preserve meaning as the model changes ​

Evolution is part of the contract. Classify semantic impact, cover required changes with typed migration operations, and retain compiler transformation receipts. Require executable witnesses for capability claims. Treat target deployment observations as a separate qualification step.

or explore the connected presentation.