Help us shape the future of Go! Take the 2026 Go Developer Survey

infomodel

package
v0.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package infomodel holds Atlas's process information model: a UML class-diagram subset that gives BPMN's data objects a type they can share across processes (ADR-0230).

BPMN scopes a data object to one process definition and leaves its type slot — `itemSubjectRef` → `<itemDefinition structureRef="…">` — deliberately opaque, pointing "into some other schema language". So two processes that both handle an order have two unrelated strings named `order`, and nothing anywhere says they mean the same thing. This package is the other schema language: an application-owned document of classes, their attributes, and the associations between them, against which `itemSubjectRef` resolves.

It models *concepts, not storage.* An entity-relationship diagram's whole vocabulary — entities, columns, foreign keys — is persistence, and drawing one would assert a decision nobody has made. Where a datum is persisted is the data store's question (ADR-0036), settled per store; this is about what a datum *is*.

Index

Constants

View Source
const (
	// RuleDataUnresolvedType marks a data object whose itemSubjectRef names no class
	// in the owning application's information model.
	RuleDataUnresolvedType = "data.unresolved-type"
	// RuleDataUntyped marks a data object that declares no type at all, in an
	// application that does model its data.
	RuleDataUntyped = "data.untyped"
	// RuleDataUnknownMember marks a write targeting a member the resolved class has
	// no attribute for.
	RuleDataUnknownMember = "data.unknown-member"
	// RuleDataMemberThroughScalar marks a dotted write path that walks through a
	// primitive or an enumeration, which has no members to walk into.
	RuleDataMemberThroughScalar = "data.member-through-scalar"
	// RuleDataNotALiteral marks a write of a *constant* value into a member an
	// «enumeration» types, where that constant is none of the literals it declares. The
	// model wrote down the complete set of values that member may hold, so a value
	// outside it is matched by nothing, for ever — the RuleDataUnknownState case one
	// level down (ADR-0351).
	RuleDataNotALiteral = "data.not-a-literal"
	// RuleDataMemberThroughCollection marks a dotted write path that walks through a
	// collection attribute — legal to write, but it sets a member of the list value
	// rather than of each element (ADR-0060 leaves list indexing to a follow-up).
	RuleDataMemberThroughCollection = "data.member-through-collection"
	// RuleDataNeverWritten marks a data object an activity reads that nothing in the
	// process ever writes.
	RuleDataNeverWritten = "data.never-written"
	// RuleDataReadBeforeWrite marks a read no writer can precede — the parallel
	// branch that reads what the other branch produces.
	RuleDataReadBeforeWrite = "data.read-before-write"
	// RuleDataUnknownStore marks a <dataStoreReference> naming a store the owning
	// application's information model does not declare.
	RuleDataUnknownStore = "data.unknown-store"
	// RuleDataStoreUnbound marks a store that is modeled but has no Worker behind it,
	// so nothing can actually reach what it holds.
	RuleDataStoreUnbound = "data.store-unbound"

	// RuleDataUnknownState: a process moves an object into a state its class's
	// lifecycle does not declare (ADR-0259). The typo check, and the one that finds
	// something on the first application it runs against.
	RuleDataUnknownState = "data.unknown-state"

	// RuleDataIllegalTransition: both states are declared and no transition joins
	// them. Deliberately conservative — see checkLifecycles for what it stays quiet
	// about, and why that is the right direction to be wrong in.
	RuleDataIllegalTransition = "data.illegal-transition"

	// RuleDataUnreachableState: a class declares a state that nothing the application
	// deploys ever writes. Reported by CheckApplication rather than by CheckDataFlow,
	// because it is the one lifecycle question a single compiled process cannot answer
	// — see unreachablestate.go.
	RuleDataUnreachableState = "data.unreachable-state"
)

The rule slugs. Like the compiler's own, they are stable machine names a UI can group and filter by without parsing the message.

View Source
const (
	// GapNoBusinessKey is stated once for the whole reading, not per class. Nothing in
	// BPMN says which attribute identifies a thing — it is the single fact ADR-0230
	// exists for, every cross-process capability rests on it, and derivation can never
	// produce it. Every derived class is therefore keyless.
	GapNoBusinessKey = "no-business-key"
	// GapNoAttributeTypes, likewise once: a FEEL expression's result type is not a
	// static fact of the model, so a derived attribute is untyped.
	GapNoAttributeTypes = "no-attribute-types"
	// GapNamedAfterObject marks a class named after the data object because no
	// itemSubjectRef named it — usually the wrong name, and the commonest real case.
	GapNamedAfterObject = "named-after-object"
	// GapStructuredMember marks a member a dotted write path proved has members of its
	// own, whose class nothing in BPMN names.
	GapStructuredMember = "structured-member"
	// GapWholeObjectWrite marks a class some write replaces entirely. The value is a
	// FEEL expression evaluated at run time, so what is *inside* it cannot be read from
	// the model at all — and a reader who takes the resulting empty member list for the
	// class's members concludes that fields the process demonstrably writes are missing.
	GapWholeObjectWrite = "whole-object-write"
)

The kinds of thing a derivation could not read. They are stable machine names for the same reason the rule slugs are: a view groups by them without parsing prose.

View Source
const (
	// SidePlanned is in the model and in no process — the backlog, and the direction
	// the pair exists for. A class declaring `cancelled` that nothing writes is not a
	// defect; it is a decision taken and not yet implemented.
	SidePlanned = "planned-not-built"
	// SideBuilt is in the processes and in no model. Usually that means write it down;
	// occasionally it means a process is doing something nobody agreed to, which is the
	// more interesting reading and the reason this direction is not dropped.
	SideBuilt = "built-not-described"
)

Which document a finding is about. They are never mixed into one list: a reader acts on them differently, so they are counted, grouped and labelled apart.

View Source
const (
	KindDiffClass      = "class"
	KindDiffMember     = "member"
	KindDiffState      = "state"
	KindDiffTransition = "transition"
)

What kind of thing differs.

View Source
const (
	ImportFormatJSON = "json"
	ImportFormatXMI  = "xmi"
)

The formats an import may arrive in.

View Source
const (
	// NoteDropped: the element is not in the imported model at all.
	NoteDropped = "dropped"
	// NoteAdjusted: the element is here, saying something slightly different from
	// what the document said — a widened multiplicity, a type read as text.
	NoteAdjusted = "adjusted"
	// NoteInfo: nothing was lost; this is a fact about the import worth stating.
	NoteInfo = "info"
)

What became of one element of the source document. The three levels are what a reader acts on differently: something is gone, something is here but not as it was written, or something is worth knowing.

View Source
const (
	ViaContainment = "containment"
	ViaKey         = "key"
)

How a link was established. It travels to the reader because the two are different claims: one is read off the value, the other is inferred from a key.

View Source
const (
	// StereotypeBusinessObject is a thing the business tracks and that has an
	// identity of its own: an Order, a Customer, a Claim. The default.
	StereotypeBusinessObject = "businessObject"
	// StereotypeValueType is a structured value with no independent existence: an
	// Address, a Money amount. Two of them with equal contents *are* the same value,
	// which is why one cannot declare a business key and cannot own parts.
	StereotypeValueType = "valueType"
	// StereotypeEnumeration is a closed set of literals: an OrderStatus. It has
	// literals where the others have attributes, and it appears only as an
	// attribute's type — never at the end of an association.
	StereotypeEnumeration = "enumeration"
)

The stereotypes a class may carry. They are not decoration: almost every rule in the matrix below is about which of these three a class is.

View Source
const (
	KindAssociation    = "association"
	KindAggregation    = "aggregation"
	KindComposition    = "composition"
	KindGeneralization = "generalization"
)

The association kinds. Four, because these are the four that say something a reader can act on; UML's dependency, realization and usage say things about code rather than about a business record.

View Source
const (
	MultOptional = "0..1"
	MultOne      = "1"
	MultMany     = "0..*"
	MultAtLeast1 = "1..*"
)

The multiplicities an attribute or an association end may carry.

View Source
const (
	TypeString   = "string"
	TypeNumber   = "number"
	TypeBoolean  = "boolean"
	TypeDate     = "date"
	TypeDateTime = "dateTime"
	TypeTime     = "time"
	TypeDuration = "duration"
)

The primitive attribute types. Deliberately few and deliberately abstract: a process information model says a delivery date is a date, not that it is a DATETIME2(7) — the moment it says the latter it has become a storage schema.

View Source
const (
	RefusedOutOfSubset = "out-of-subset"
	RefusedByNotation  = "notation"
)

Why something was refused. The distinction is the point: RefusedOutOfSubset is a limit of this build and reads as "not yet"; RefusedByNotation is a fact about what these things are and reads as "no".

View Source
const (
	// ProcessUseDeclare is a <dataObject> whose itemSubjectRef names this class. It is
	// the use every other one hangs off, and it names no element: a data object is not
	// a flow node (ADR-0053).
	ProcessUseDeclare = "declare"
	// ProcessUseRead is a <dataInputAssociation>: an activity reads the object into a
	// process variable its FEEL can then see (ADR-0059).
	ProcessUseRead = "read"
	// ProcessUseWrite is a <dataOutputAssociation>: an activity writes the object, or
	// one member of it (ADR-0060), and may move its data state (ADR-0058).
	ProcessUseWrite = "write"
	// ProcessUseStore is a <dataStoreReference> naming a store that holds this class —
	// the cross-process channel, where instances outlive the process that made them.
	ProcessUseStore = "store"
)

The kinds of use a process makes of a class. They are stable machine names for the same reason the rule slugs are: a view groups and filters by them without parsing prose.

View Source
const (
	// ModelUseAttribute is another class carrying an attribute of this type. For an
	// «enumeration» it is the ordinary case and usually the only one.
	ModelUseAttribute = "attribute"
	// ModelUseAssociation is an association with this class at one end.
	ModelUseAssociation = "association"
	// ModelUseStates is a lifecycle whose states are this «enumeration»'s literals
	// (ADR-0306).
	ModelUseStates = "states"
	// ModelUseStore is a data store holding this class's instances.
	ModelUseStore = "store"
)

The kinds of use the information model makes of a class.

View Source
const (
	CodeMissingClassName         = "missing-class-name"
	CodeDuplicateClassName       = "duplicate-class-name"
	CodeDuplicateClassID         = "duplicate-class-id"
	CodeUnknownStereotype        = "unknown-stereotype"
	CodeMissingAttributeName     = "missing-attribute-name"
	CodeDuplicateAttribute       = "duplicate-attribute"
	CodeUnknownType              = "unknown-type"
	CodeUnknownMultiplicity      = "unknown-multiplicity"
	CodeAttributeTypedAsSelf     = "attribute-typed-as-self"
	CodeEnumerationHasAttributes = "enumeration-has-attributes"
	CodeEnumerationEmpty         = "enumeration-empty"
	CodeDuplicateLiteral         = "duplicate-literal"
	CodeIdentityNotAllowed       = "identity-not-allowed"
	CodeUnknownIdentityAttribute = "unknown-identity-attribute"
	CodeIdentityNotRequired      = "identity-not-required"
	CodeIdentityNotSingular      = "identity-not-singular"
	CodeUnknownClassRef          = "unknown-class-ref"
	CodeRelationshipRefused      = "relationship-refused"
	CodeSelfGeneralization       = "self-generalization"
	CodeGeneralizationCycle      = "generalization-cycle"
	CodeDuplicateAssociationID   = "duplicate-association-id"
	CodeStoreMissingName         = "store-missing-name"
	CodeDuplicateStoreName       = "duplicate-store-name"
	CodeDuplicateStoreID         = "duplicate-store-id"
	CodeStoreUnknownClass        = "store-unknown-class"
	CodeStoreClassNotStorable    = "store-class-not-storable"
	CodeStoreClassHasNoKey       = "store-class-has-no-key"
	CodeStoreUnknownMode         = "store-unknown-mode"

	// Lifecycles (ADR-0259). A lifecycle on disk has to be one a deploy can resolve
	// against: every state a transition names exists, one state is where an instance
	// starts, and no two states answer to the same name — a process writes a state by
	// that string and by nothing else.
	CodeLifecycleNotAllowed        = "lifecycle-not-allowed"
	CodeLifecycleEmpty             = "lifecycle-empty"
	CodeMissingStateName           = "missing-state-name"
	CodeDuplicateStateName         = "duplicate-state-name"
	CodeNoInitialState             = "no-initial-state"
	CodeManyInitialStates          = "many-initial-states"
	CodeUnknownTransitionState     = "unknown-transition-state"
	CodeDuplicateTransitionID      = "duplicate-transition-id"
	CodeTransitionLeavesFinalState = "transition-leaves-final-state"

	// A lifecycle sourced from an «enumeration»
	// (ADR-0306). All three say
	// the same kind of thing: the document claims its states come from a set, and then
	// contradicts itself about what that set is or what is in it.
	CodeLifecycleUnknownEnumeration = "lifecycle-unknown-enumeration"
	CodeLifecycleNotAnEnumeration   = "lifecycle-not-an-enumeration"
	CodeStateNotALiteral            = "state-not-a-literal"
)

The finding codes. They are a public contract — the canvas keys its messages and its marks off them — so a code is added rather than repurposed.

View Source
const (
	StoreModeRead = "read"
)

What a process may do with a store. Read-only today: writing through a store is a transaction against something outside the engine, which is a decision of its own and gets its own record rather than being smuggled in as a second enum value.

View Source
const SubsetVersion = 1

SubsetVersion is the version of the tables below. It is a public contract — a palette and a rule the browser enforces — so a change to what is permitted is a version bump rather than a quiet redefinition.

Variables

This section is empty.

Functions

func AllowedFrom

func AllowedFrom(fromStereotype, toStereotype string) []string

AllowedFrom lists the association kinds that may run from one stereotype to another — what the canvas offers in its connect menu, so it never offers a line the server would refuse.

func BusinessKey

func BusinessKey(vocab *Vocabulary, className string, value json.RawMessage) string

BusinessKey renders one data object's business key: the value of the attributes its class declares as its identity, joined. "" when the class is unknown, declares no key, or the value does not carry all of it.

This is the fact the cross-instance index is built on, and the reason a partial key is refused rather than joined from what is there: half a key identifies half as many things as a whole one only by accident, and an index that matched on it would quietly relate two orders that are not the same order.

func CheckApplication added in v0.7.0

func CheckApplication(cps []*compiler.CompiledProcess, vocab *Vocabulary) []compiler.Problem

CheckApplication reports what no single process can be asked: a state a class declares that nothing this application deploys ever writes.

`cps` is the application's processes as they actually run — the deployed ones plus whatever is being deployed or dry-run now, so the process that cancels an order clears the finding the moment it arrives rather than after the next deploy.

It is a warning like its two siblings and refuses nothing, for ADR-0230 slice 3's reason: a lifecycle is routinely drawn before the process that will write it.

func CheckDataFlow

func CheckDataFlow(cp *compiler.CompiledProcess, vocab *Vocabulary) []compiler.Problem

CheckDataFlow inspects a compiled process against an application's vocabulary and returns every finding, in a deterministic order.

It runs at deploy and on the Problems panel's dry run — never at runtime. That is invariant I5 as it applies here: whether a write can land is knowable from the model and the vocabulary, so it is decided once, at deploy, and the engine keeps reading integer indices.

func DetectImportFormat

func DetectImportFormat(document []byte) (string, bool)

DetectImportFormat reads the first meaningful byte, which is all it takes: a JSON document starts with an object and an XMI one with an element or a declaration. Sniffing rather than trusting a file extension is deliberate — a UML tool writes .uml, .xmi and .xml for the same document.

Types

type AccessResolver

type AccessResolver func(r *http.Request, applicationID string) (ApplicationAccess, error)

AccessResolver resolves application ownership on the API run loop. It is invoked only from a loop turn, so it may read the server's application store directly; it must not call Loop.Do recursively.

type ApplicationAccess

type ApplicationAccess struct {
	Exists    bool
	CanView   bool
	CanEdit   bool
	Protected bool
}

ApplicationAccess is the caller's resolved access to the process application that owns a model. Like Panorama, this area reuses the application scope (ADR-0071/ 0128) rather than inventing a second ACL.

type Association

type Association struct {
	ID   string `json:"id"`
	Name string `json:"name,omitempty"`
	Kind string `json:"kind"`
	// From is the source end. For a generalization it is the *specific* class ("an
	// ExpressOrder is an Order"); for a composition or aggregation it is the whole.
	From End `json:"from"`
	To   End `json:"to"`
}

Association relates two classes. Its Kind carries the meaning; the ends carry the reading — a role name and a multiplicity each, so the diagram states "a Customer places 0..* Orders" rather than only that a line exists.

type AssociationKind

type AssociationKind struct {
	Kind  string `json:"kind"`
	Label string `json:"label"`
	// Rule is the sentence this implements, in plain words, so a refusal can say
	// *why* rather than only that it was refused.
	Rule string `json:"rule"`
	// Directed reports whether the two ends mean different things, so the canvas
	// knows whether reversing the arrow changes the statement.
	Directed bool `json:"directed"`
}

AssociationKind is one drawable relationship.

func AssociationKinds

func AssociationKinds() []AssociationKind

AssociationKinds exposes the relationship table. Everything else about the subset reaches a caller through AuthoringSubset, which is the payload the browser is served; this one is separate because the matrix is built from it. It returns the package's own slice; callers must not mutate it.

type Attribute

type Attribute struct {
	Name string `json:"name"`
	// Type is a primitive (see PrimitiveTypes) or the name of another class in the
	// same model — including an «enumeration», which is how a closed set of values
	// is declared.
	Type          string `json:"type"`
	Multiplicity  string `json:"multiplicity"`
	Documentation string `json:"documentation,omitempty"`
}

Attribute is one typed member of a class.

type CatalogEntry added in v0.7.0

type CatalogEntry struct {
	ModelID       string `json:"modelId"`
	ModelName     string `json:"modelName"`
	ApplicationID string `json:"applicationId"`
	ClassID       string `json:"classId"`
	Name          string `json:"name"`
	Stereotype    string `json:"stereotype"`
	Documentation string `json:"documentation,omitempty"`
	// Members is the count of whichever members this kind of class has: attributes,
	// or an «enumeration»'s literals. One column, honestly filled, rather than two of
	// which one is always empty.
	Members   int          `json:"members"`
	Identity  []string     `json:"identity,omitempty"`
	States    int          `json:"states"`
	Usage     UsageSummary `json:"usage"`
	UpdatedAt int64        `json:"updatedAt"`
}

CatalogEntry is one class as the overview lists it: what it is, what it holds, and how much of the estate depends on it.

func Catalog added in v0.7.0

func Catalog(models []Model, processes func(applicationID string) []Process) []CatalogEntry

Catalog lists every class of every model handed in, each with the counts of where it is used.

processes is asked once per application, not once per class: an application's processes are walked a single time and bucketed by the class each use resolves to, so a catalogue of fifty classes costs one walk rather than fifty. It may be nil, which reads every class against no process at all — the model-side uses still stand, and for an enumeration they are the whole answer.

type Class

type Class struct {
	// ID is stable across renames, so an association survives its class being
	// renamed; Name is what a model and a diagram refer to, and what
	// `itemSubjectRef` matches.
	ID            string `json:"id"`
	Name          string `json:"name"`
	Documentation string `json:"documentation,omitempty"`
	// Stereotype says what kind of type this is, and it is what most of the
	// relationship rules in subset.go are actually about — see the constants there.
	Stereotype string      `json:"stereotype"`
	Attributes []Attribute `json:"attributes"`
	// Literals are the members of an «enumeration», which has literals where every
	// other stereotype has attributes. Empty otherwise.
	Literals []string `json:"literals,omitempty"`
	// Identity names the attributes that together form the business key: the fact
	// that makes Order#ORD-1 the same order in three processes and in a data store.
	// It is the part BPMN has no equivalent for, and every cross-process capability
	// rests on it. Empty for a class whose instances have no identity of their own.
	Identity []string `json:"identity,omitempty"`
	// Lifecycle is the states this class's instances move through, and what a BPMN
	// data state resolves against (ADR-0259). It sits here beside the business key
	// because both are facts about a business object that BPMN has no field for, and
	// because a lifecycle scoped to one process would reproduce the very problem the
	// information model exists to solve.
	//
	// nil is the normal case and means silence everywhere: a class without one is
	// exactly as it was before this existed.
	Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
	// X and Y place the class on the canvas. Layout is part of the document because
	// a diagram a person arranged is a diagram they can read again.
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

Class is one business object type — the thing a BPMN data object's `itemSubjectRef` names.

type Clock

type Clock func() time.Time

Clock supplies deterministic timestamps in tests.

type DataStore

type DataStore struct {
	ID            string `json:"id"`
	Name          string `json:"name"`
	Documentation string `json:"documentation,omitempty"`
	// Class is the class whose instances this store holds. It must be a business
	// object with a business key: a store you cannot address something in by its
	// identity is not a store a process can read from.
	Class string `json:"class"`
	// Worker is the configured Worker that backs it (ADR-0036/0203) — a clio event
	// store, a database, a SharePoint list. Empty while the store is modeled but not
	// yet wired to anything, which is a normal state and not an error: a store is
	// routinely drawn before somebody configures the worker behind it.
	Worker string `json:"worker,omitempty"`
	// Mode is what a process may do with it. Read-only today; writing through a store
	// is a decision of its own (transactionality against something outside the engine)
	// and gets its own record.
	Mode string  `json:"mode"`
	X    float64 `json:"x"`
	Y    float64 `json:"y"`
}

DataStore is a place instances of one class outlive the process that made them.

It is the one part of this model that is *not* storage-agnostic, and that is the point of separating it from the classes: an Order is an Order wherever it is kept, and the store is what says where. Everything about persistence lives here and nowhere else in the document.

type Derivation added in v0.7.0

type Derivation struct {
	Model Model `json:"model"`
	Gaps  []Gap `json:"gaps"`
}

Derivation is one application's information model as its processes imply it, with what could not be read stated beside it.

The Model is an ordinary Model so the class canvas draws it with no new code, and so it can be validated by the same rules an authored one is. It is never saved.

func Derive added in v0.7.0

func Derive(cps []*compiler.CompiledProcess) Derivation

Derive reads the classes, their members and their lifecycles off a set of compiled processes — the application's, as CheckApplication assembles it.

Deriving from nothing is an empty reading and not an empty claim: with no classes there is no drawing, and the sentences that qualify a drawing would qualify nothing.

type Diff added in v0.7.0

type Diff struct {
	Planned []DiffFinding `json:"planned"`
	Built   []DiffFinding `json:"built"`
	// Excluded is what the comparison did not look at, said where it lists rather than
	// in a footnote: a reader who does not know what was excluded cannot tell a short
	// list from a clean bill.
	Excluded []string `json:"excluded"`
	// Modeled reports whether anything is authored at all. An application that models
	// nothing produces no findings — nothing has been planned, so nothing is missing
	// from the plan.
	Modeled bool `json:"modeled"`
}

Diff is the whole reading.

func Difference added in v0.7.0

func Difference(cps []*compiler.CompiledProcess, vocab *Vocabulary) Diff

Difference reads one application's processes against the model authored for it.

It derives the built side itself rather than taking one, so a caller cannot hand in a derivation of one application and a vocabulary of another.

type DiffFinding added in v0.7.0

type DiffFinding struct {
	Side  string `json:"side"`
	Kind  string `json:"kind"`
	Class string `json:"class"`
	// Name is the member or the state; From and To are a transition's ends. A finding
	// about the class as a whole carries none of them.
	Name string `json:"name,omitempty"`
	From string `json:"from,omitempty"`
	To   string `json:"to,omitempty"`
	Note string `json:"note"`
}

DiffFinding is one difference, located precisely enough to act on.

type End

type End struct {
	ClassID      string `json:"classId"`
	Role         string `json:"role,omitempty"`
	Multiplicity string `json:"multiplicity,omitempty"`
}

End is one side of an association: which class, what it is called from the other side, and how many of it there are.

type Finding

type Finding struct {
	Code    string `json:"code"`
	Reason  string `json:"reason"`
	Message string `json:"message"`
	// Exactly one of ClassID / AssociationID / StoreID identifies where the finding
	// is; Attribute names the member within a class when the finding is about one.
	ClassID       string `json:"classId,omitempty"`
	AssociationID string `json:"associationId,omitempty"`
	StoreID       string `json:"storeId,omitempty"`
	Attribute     string `json:"attribute,omitempty"`
	// State and Transition locate a finding inside a class's lifecycle, the way
	// Attribute locates one inside its members — so the state canvas can mark the
	// state or the arrow that is wrong instead of colouring the whole class.
	State      string `json:"state,omitempty"`
	Transition string `json:"transition,omitempty"`
}

Finding is one thing wrong with a model, located precisely enough that the canvas can mark it rather than print a list somebody has to search.

type Gap added in v0.7.0

type Gap struct {
	Class string `json:"class,omitempty"`
	Kind  string `json:"kind"`
	Note  string `json:"note"`
}

Gap is one thing the reading could not see. A gap with no Class is a fact about derivation itself and is said once; a gap with one is about that class.

type Graph

type Graph struct {
	Nodes      []ObjectNode    `json:"nodes"`
	Links      []ObjectLink    `json:"links"`
	Unresolved []UnresolvedRef `json:"unresolved"`
	// Degraded says the application models nothing, so this is the instance's data
	// without the structure a model would give it.
	Degraded bool `json:"degraded"`
	// Truncated says a guard stopped the walk, so the picture is a part of one.
	Truncated bool `json:"truncated"`
}

Graph is the whole derived picture, with what it could not show.

func ObjectGraph

func ObjectGraph(objects []ObjectValue, vocab *Vocabulary) Graph

ObjectGraph derives one instance's object diagram from its data objects and the application's vocabulary.

type IDGenerator

type IDGenerator func() (string, error)

IDGenerator mints an opaque model or element id off the run loop.

type ImportNote

type ImportNote struct {
	Level string `json:"level"`
	// Element names the thing in the source document — a class, an attribute, an id
	// — so a reader can find it in the tool the document came from.
	Element string `json:"element,omitempty"`
	Message string `json:"message"`
}

ImportNote is one thing the import could not carry, or carried differently.

type ImportResponse

type ImportResponse struct {
	Format     string           `json:"format"`
	Notes      []ImportNote     `json:"notes"`
	Validation ValidationResult `json:"validation"`
	// Preview is the model the document would become, present on a dry run only.
	Preview *Model `json:"preview,omitempty"`
	// Model is the stored model, present when one was created.
	Model *Summary `json:"model,omitempty"`
}

ImportResponse is what an import answers with — and the notes are the substance of it, not a footnote: an import from a foreign notation is a lossy operation, and the list of what it lost is the part a modeler has to read.

type ImportResult

type ImportResult struct {
	Format string       `json:"format"`
	Model  Model        `json:"model"`
	Notes  []ImportNote `json:"notes"`
}

ImportResult is a parsed document: the model it became, and the honest account of the difference between the two.

func ParseImport

func ParseImport(format string, document []byte) (ImportResult, error)

ParseImport turns a document into a storable model and the account of what the import did to it. An empty format is detected from the document itself.

It never returns a model Validate would refuse: everything outside the subset is dropped or adjusted here, with a note, rather than being handed to the store and refused there — a refusal names a rule, and a person importing somebody else's diagram needs to know which of *their* elements it was about.

type Lifecycle added in v0.6.0

type Lifecycle struct {
	// StatesFrom names an «enumeration» in the same model whose literals are this
	// lifecycle's states, or is empty — which is the normal case and means the states
	// below are declared here and nowhere else
	// (ADR-0306).
	//
	// It is a *reference*, not a merge. The enumeration answers which values there
	// are; this answers in what order, which is the half no closed set of values can
	// hold. What it buys is that the strings are written once: the model that prompted
	// the record had five state names written twice, as literals a person maintained
	// and as states a deploy resolved, with nothing noticing when they drifted.
	//
	// A name rather than an id, for the reason Class.Identity names attributes and
	// DataStore.Class names a class: a state's identity is already its name, and a
	// literal's rename *is* a state's rename.
	StatesFrom  string                `json:"statesFrom,omitempty"`
	States      []LifecycleState      `json:"states"`
	Transitions []LifecycleTransition `json:"transitions"`
}

Lifecycle is the state machine a business object moves through, and the thing a BPMN data state resolves against (ADR-0259).

BPMN's `<dataObjectReference>` carries a data state — the `[received]`, `[approved]` in square brackets under the box — and says nothing whatever about what states exist or which may follow which. It is the second opaque slot beside `itemSubjectRef`, and this is what fills it: a class declares the states its instances can be in, and a deploy resolves every state a process writes against that declaration.

It is *not* an «enumeration» attribute. An enumeration is a closed set of values something is typed as; a lifecycle is a set of stages with an order between them, and the order is most of what it says. "An order may be cancelled, but not once it has shipped" is a lifecycle, and is not expressible as a type.

Nothing executes it. Atlas runs BPMN; a lifecycle constrains data that BPMN moves, and is resolved at deploy exactly as a class name is (ADR-0259 §5).

type LifecycleRules added in v0.6.0

type LifecycleRules struct {
	// Stereotypes that may carry one. Only a business object has an identity that
	// persists through states; a value type is equal to any other with the same
	// contents, and an enumeration is a set of values rather than a thing that moves
	// through them.
	Stereotypes []string `json:"stereotypes"`
	// Exactly one state is where an instance is created, and any number are where
	// instances end. Served rather than assumed, because "one start" is a rule the
	// canvas has to enforce while drawing and not only report afterwards.
	InitialStates int `json:"initialStates"`
	// SelfTransitions says a state may transition to itself: a record can be revised
	// without leaving the stage it is in.
	SelfTransitions bool `json:"selfTransitions"`
	// LeavesFinal says whether a transition may start at a final state. It may not —
	// "final" and "leaves" are the two saying opposite things.
	LeavesFinal bool `json:"leavesFinal"`
	// Guards says whether a transition may carry a condition. It may not, and the
	// reason travels with the answer: what causes a transition is the BPMN element
	// that writes the state, and a condition here would be a second place deciding
	// what happens.
	Guards  bool   `json:"guards"`
	Meaning string `json:"meaning"`
}

LifecycleRules is what the state canvas may draw, served rather than restated (ADR-0259 §2). The canvas refuses mid-drag exactly what the write path refuses, in the server's own words — which is the arrangement the class canvas already has for its relationship matrix, and the reason a refusal can say "UML allows this, this build does not" rather than pretending the standard forbids it.

func Lifecycles added in v0.6.0

func Lifecycles() LifecycleRules

Lifecycles reports the rules a state machine is held to.

type LifecycleState added in v0.6.0

type LifecycleState struct {
	Name          string `json:"name"`
	Documentation string `json:"documentation,omitempty"`
	// Initial marks where an instance starts. Exactly one state carries it: a machine
	// with no start says nothing about what an object is when it is created, and one
	// with two says two contradictory things.
	Initial bool `json:"initial,omitempty"`
	// Final marks a state nothing leaves. Any number of them, including none — an
	// order ends either shipped or cancelled, and a long-lived record may end nowhere.
	Final bool `json:"final,omitempty"`
	// X and Y place the state on the canvas, for the reason a class carries them: a
	// diagram somebody arranged is a diagram they can read again.
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

LifecycleState is one stage in a class's life.

Its Name is its identity, deliberately, where an association names its ends by a stable id. The name here *is* what a process writes: a `<dataState name="approved">` matches it by that string, so an id beside it would be a second identity for one thing, and renaming a state is renaming what every process has to write. The same reasoning makes Class.Identity name attributes and DataStore.Class name a class.

type LifecycleTransition added in v0.6.0

type LifecycleTransition struct {
	ID   string `json:"id"`
	Name string `json:"name,omitempty"`
	// From and To are state names within this lifecycle. A transition to the state it
	// starts from is a self-loop and is allowed: a record can be revised without
	// leaving the stage it is in.
	From string `json:"from"`
	To   string `json:"to"`
}

LifecycleTransition is one move a class's instances may make between its states.

Its Name is documentation and nothing more. What *causes* a transition is the BPMN element that writes the state, and that fact lives in the process — a condition here would be a second place where something decides what happens, and the first is the sequence flow (ADR-0259 §5).

type LossNote

type LossNote struct {
	Area   string `json:"area"`
	Reason string `json:"reason"`
}

LossNote is one thing the projection could not express, and why. The reason matters more than the fact: "JSON Schema has no keyword for this" is a property of the target notation, not a shortcoming of the model it came from.

type Model

type Model struct {
	ID            string `json:"id"`
	ApplicationID string `json:"applicationId"`
	Name          string `json:"name"`
	Documentation string `json:"documentation,omitempty"`
	// Revision guards a concurrent overwrite: a write states the revision it read,
	// and a write against a stale one is refused rather than silently winning.
	Revision     int64         `json:"revision"`
	Classes      []Class       `json:"classes"`
	Associations []Association `json:"associations"`
	// Stores are where instances of a class live beyond one process instance — the
	// cross-process channel BPMN gestures at with <dataStoreReference> and then says
	// nothing about. They live in this document because a store is per application,
	// declared once and named by every process that reaches it; and beside the
	// classes rather than inside one, because a class stays storage-agnostic. Only
	// the store says where its instances are kept.
	Stores    []DataStore `json:"stores"`
	CreatedAt int64       `json:"createdAt"`
	CreatedBy string      `json:"createdBy,omitempty"`
	UpdatedAt int64       `json:"updatedAt"`
	UpdatedBy string      `json:"updatedBy,omitempty"`
}

Model is one application-owned information model. Unlike a Panorama document — whose canonical form is the standard's own interchange XML — this one is stored in its native shape and *projected* to its notations: a UML class diagram to read, and a JSON Schema to validate against. A projection is derived, never authored, and states what it dropped.

func (*Model) ClassByID

func (m *Model) ClassByID(id string) (*Class, bool)

ClassByID finds a class by its stable id — how an association names its ends.

func (*Model) ClassByName

func (m *Model) ClassByName(name string) (*Class, bool)

ClassByName finds a class by the name a model refers to it with, which is what a BPMN `itemSubjectRef` carries. Names are unique within a model (validation.go enforces it), so the first match is the only one.

type ModelUse added in v0.7.0

type ModelUse struct {
	ModelID   string `json:"modelId"`
	ModelName string `json:"modelName"`
	Kind      string `json:"kind"`
	// Class is the class making the use: the one carrying the attribute or the
	// lifecycle, or the one at the other end of the association. Empty for a store,
	// which belongs to no class.
	Class string `json:"class,omitempty"`
	// Name is the attribute's name or the association's, where it has one.
	Name string `json:"name,omitempty"`
	// Detail is the reading a person wants beside it — a multiplicity, an
	// association's kind and its ends, a store's mode and Worker.
	Detail string `json:"detail,omitempty"`
}

ModelUse is one place in an information model where a class is used — the half of "where used" that is about the vocabulary rather than about a process.

type MultiplicityOption

type MultiplicityOption struct {
	Multiplicity string `json:"multiplicity"`
	Label        string `json:"label"`
	// Required and Collection are what the JSON Schema projection and the canvas
	// both need, derived here once rather than by parsing the string in two places.
	Required   bool `json:"required"`
	Collection bool `json:"collection"`
}

MultiplicityOption is one multiplicity, with what it means for a value.

func MultiplicityOf

func MultiplicityOf(m string) (MultiplicityOption, bool)

MultiplicityOf returns one multiplicity's meaning, and whether it is in the subset at all.

type ObjectAttribute

type ObjectAttribute struct {
	Name  string `json:"name"`
	Value string `json:"value"`
	Key   bool   `json:"key,omitempty"`
	// Absent distinguishes "this object does not carry that member" from "it carries
	// it, empty" — different facts about a datum, and the first is usually the one
	// worth noticing.
	Absent bool `json:"absent,omitempty"`
}

ObjectAttribute is one member of an object node, in the order its class declares them — so two objects of one class read alike rather than in whatever order their JSON happened to be written.

type ObjectLink struct {
	From  string `json:"from"`
	To    string `json:"to"`
	Kind  string `json:"kind"`
	Label string `json:"label,omitempty"`
	Via   string `json:"via"`
}

ObjectLink is one line.

type ObjectNode

type ObjectNode struct {
	ID    string `json:"id"`
	Label string `json:"label"`
	Name  string `json:"name"`
	Class string `json:"class,omitempty"`
	State string `json:"state,omitempty"`
	// Key is the object's business key, rendered — what makes this object *this*
	// one, and what another object's reference has to match to become a line.
	Key        string            `json:"key,omitempty"`
	Attributes []ObjectAttribute `json:"attributes"`
	// Value is what the object holds, rendered as an attribute's value is (a scalar
	// as itself, a structure as its JSON). Carried for an object whose class is
	// unknown or whose value is not a structure — there is no attribute list to show
	// instead, and an object with neither would be an empty box.
	Value string `json:"value,omitempty"`
	// Nested marks a node that came out of a whole's value rather than being a data
	// object of the instance in its own right.
	Nested bool `json:"nested,omitempty"`
	Unset  bool `json:"unset,omitempty"`
}

ObjectNode is one object on the diagram.

type ObjectValue

type ObjectValue struct {
	Name string
	// Class is the resolved declared type, "" when the object is untyped or its
	// itemSubjectRef resolves to nothing.
	Class string
	State string
	// Value is the object's value as canonical JSON, nil when it is unset.
	Value json.RawMessage
}

ObjectValue is one data object of an instance, as the graph reads it.

type PrimitiveType

type PrimitiveType struct {
	Type  string `json:"type"`
	Label string `json:"label"`
	// JSONType and JSONFormat are the projection: the draft 2020-12 type and the
	// format keyword this primitive becomes. A primitive with no format projects to
	// its bare type.
	JSONType   string `json:"jsonType"`
	JSONFormat string `json:"jsonFormat,omitempty"`
}

PrimitiveType is one built-in attribute type, with the JSON Schema it projects to (jsonschema.go reads this rather than keeping a second list).

func PrimitiveOf

func PrimitiveOf(t string) (PrimitiveType, bool)

PrimitiveOf returns one primitive's projection, and whether the name is one.

type Process added in v0.7.0

type Process struct {
	Compiled *compiler.CompiledProcess
	Name     string
	Version  int32
}

Process is one compiled process with the two labels a usage row shows that the compiled form does not carry. A CompiledProcess knows its key and its BPMN process id; what it is *called* and which version is deployed are facts of the deployment, so the caller states them rather than this package inventing a lookup for them.

type ProcessUse added in v0.7.0

type ProcessUse struct {
	ProcessDefinitionKey uint64 `json:"processDefinitionKey"`
	ProcessID            string `json:"processId"`
	ProcessName          string `json:"processName,omitempty"`
	Version              int32  `json:"version,omitempty"`
	Kind                 string `json:"kind"`
	// Object is the data object's name in that process — the local string BPMN scopes
	// to one definition, and the reason this whole area exists.
	Object string `json:"object,omitempty"`
	// ElementID is the BPMN id of the element making the use. Empty for a declaration,
	// which is a property of the process rather than of any element in it.
	ElementID string `json:"elementId,omitempty"`
	// Attribute is the member a write targets (ADR-0060); empty for a write of the
	// whole value.
	Attribute string `json:"attribute,omitempty"`
	// State is the state a write moves the object into, or — on a declaration — the
	// state instances of it are created in.
	State string `json:"state,omitempty"`
	// Variable is the process variable a read lands in (ADR-0059).
	Variable string `json:"variable,omitempty"`
	// WritesValue reports whether a write carries a value at all. A write without one
	// is a state-only transition, which is a different act and reads as one.
	WritesValue bool `json:"writesValue,omitempty"`
	// Collection marks a data object declared as a collection.
	Collection bool `json:"collection,omitempty"`
	// Store is the data store a ProcessUseStore names.
	Store string `json:"store,omitempty"`
}

ProcessUse is one place in one process where a class is used, located precisely enough to act on: which element, which member, which state.

type Projection

type Projection struct {
	Class  string         `json:"class"`
	Schema map[string]any `json:"schema"`
	Loss   []LossNote     `json:"loss"`
}

Projection is a derived schema together with what deriving it cost.

func SchemaFor

func SchemaFor(m Model, className string) (Projection, error)

SchemaFor projects one class of a model to a JSON Schema.

It validates the model first and refuses an invalid one. A schema derived from a model that does not yet make sense would be a confident, checkable, wrong statement about what a value must look like — worse than no schema.

type Refusal

type Refusal struct {
	Reason  string `json:"reason"`
	Message string `json:"message"`
}

Refusal explains a rejected connection in words a modeler can act on.

func AllowAssociation

func AllowAssociation(kind, fromStereotype, toStereotype string) (bool, *Refusal)

AllowAssociation is the matrix: may an association of this kind run from a class of one stereotype to a class of another? It is the single authority — the canvas is served it, the server applies it on write.

It rules on *stereotypes*, not on individual classes, because every rule here is about what kind of thing each end is. Whether the two ends are the same class, and whether a generalization closes a cycle, are properties of a particular model rather than of the notation, so validation.go answers those.

type Service

type Service struct {

	// Limits are the installation's resource budgets. New sets them to
	// [limits.Default]; the server overwrites them with its own once it has read the
	// environment, so every ceiling in this service is the one operators configured
	// (ADR-0291).
	Limits limits.Limits
	// contains filtered or unexported fields
}

Service serves the information-model area. Its only mutable dependency is the store, and every read and write of it goes through the loop — the design-time single-writer boundary (I3, ADR-0147).

func New

func New(loop *runloop.Loop, store *Store, access AccessResolver, newID IDGenerator, now Clock) *Service

New builds the service.

func (*Service) DeleteForApplicationOnLoop added in v0.18.0

func (s *Service) DeleteForApplicationOnLoop(applicationID string) error

DeleteForApplicationOnLoop removes every model one application owns — the purge of that application (ADR-0468). Like VocabularyOnLoop it runs inside an existing loop turn; idempotent.

func (*Service) HandleCreate

func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)

HandleCreate starts an empty information model for an application.

func (*Service) HandleDelete

func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)

HandleDelete removes a model.

func (*Service) HandleGet

func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)

HandleGet returns one whole model, with its validation.

func (*Service) HandleImport

func (s *Service) HandleImport(w http.ResponseWriter, r *http.Request)

HandleImport reads a UML class diagram into a new information model.

ADR-0230 left this out — "XMI is an export, not an interchange" — and ADR-0232 settles the other half: a model is routinely drawn in a UML tool before anybody opens Atlas, and retyping one by hand loses a business key quietly. What makes it safe is that the import is not trusted: it goes through the same subset the canvas writes through, everything outside that subset is dropped with a sentence naming the element, and a document that would produce a model the validator refuses is refused itself.

func (*Service) HandleList

func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)

HandleList lists the models the caller may view, newest first.

func (*Service) HandleSchema

func (s *Service) HandleSchema(w http.ResponseWriter, r *http.Request)

HandleSchema serves the JSON Schema projection of one class — the derived, read-only contract a value of that class is checked against, together with what the projection could not carry.

func (*Service) HandleSubset

func (s *Service) HandleSubset(w http.ResponseWriter, _ *http.Request)

HandleSubset serves the authoring subset: what may be created, what may be drawn between what, and the statement that it is a subset.

It is served rather than duplicated in the browser, and that is the whole reason this route exists — the canvas has to refuse a connection while it is being dragged, and the server has to refuse it on write. It reads no model and takes no id: the subset is a property of this build, not of anybody's document, so asking for it discloses nothing about what exists.

func (*Service) HandleUpdate

func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)

HandleUpdate replaces a model's content.

It takes the whole document rather than a patch, and refuses one that does not validate. A canvas edits a graph — moving a class, retyping an attribute, redrawing a line — and a patch language for that would be a second way to say everything the document already says. Refusing an invalid write is what keeps the store's guarantee simple: every model on disk is one the subset accepts, so a deploy resolving `itemSubjectRef` against it never meets a half-model.

func (*Service) HandleValidate added in v0.7.0

func (s *Service) HandleValidate(w http.ResponseWriter, r *http.Request)

HandleValidate judges a document the caller is holding and has not saved (ADR-0364).

It exists for the same reason HandleSubset does, one step further on. The subset is served so the canvas refuses mid-drag exactly what the server refuses on write; the *verdict* was not, so everything the subset cannot express — a store naming a class nothing declares, an attribute typed with something gone, a lifecycle whose states drifted from its enumeration — stayed invisible until Save, and was then reported about an edit the author had stopped thinking about.

Duplicating Validate into the browser would answer that and is exactly what ADR-0230 refused for the far smaller relationship matrix: two copies of a rule set are two rule sets, and the one the author sees would drift from the one the server enforces. So the verdict is served instead.

Three properties make it cheap enough to ask on every edit. It reads no stored model, so it needs no loop turn and no application scope: like the subset, asking discloses nothing about what exists. It writes nothing. And an invalid document is a 200 carrying findings rather than an error — a model mid-edit is *expected* to be invalid, and answering the normal case with a fault would teach the canvas to ignore it.

func (*Service) ModelsForApplicationOnLoop added in v0.18.0

func (s *Service) ModelsForApplicationOnLoop(applicationID string) ([]Model, error)

ModelsForApplicationOnLoop is the models one application owns, newest first, for a deletion preview. Like VocabularyOnLoop it runs inside an existing loop turn.

func (*Service) ModelsOnLoop added in v0.7.0

func (s *Service) ModelsOnLoop() ([]Model, error)

ModelsOnLoop is every stored model, newest first. Like VocabularyOnLoop it runs inside an existing loop turn, so callers must already hold one.

It answers no access question, deliberately: the catalogue reading that needs it also needs the application's *processes*, which only the server can reach, so it is one turn on the server's side that filters the models and gathers the processes together. A model this returns has not been authorized for anybody yet.

func (*Service) VocabularyOnLoop

func (s *Service) VocabularyOnLoop(applicationID string) (*Vocabulary, error)

VocabularyOnLoop is what an application's information models say, flattened for resolution — what a deploy and the Problems panel resolve `itemSubjectRef` against. It runs inside an existing loop turn, so callers must already hold one.

An application with no model yields an *unmodeled* vocabulary rather than an empty one, and the difference is deliberate: the checks that need a vocabulary say nothing at all against it, so an instance that has not started modeling does not get a warning on every data object it has.

type Step added in v0.7.0

type Step struct {
	From string `json:"from"`
	To   string `json:"to"`
	At   int64  `json:"at"`
	By   string `json:"by,omitempty"`
}

Step is a move the object actually made: it was in From and the next write put it in To.

type StereotypeKind

type StereotypeKind struct {
	Stereotype string `json:"stereotype"`
	Label      string `json:"label"`
	// Meaning is the sentence a modeler needs to choose correctly. It travels to the
	// browser because the difference between a business object and a value type is
	// the single most consequential choice in this metamodel, and a palette that only
	// lists three words makes it by accident.
	Meaning string `json:"meaning"`
	// HasIdentity reports whether this kind may declare a business key. Only a
	// business object may: identity is what makes Order#ORD-1 the same order in three
	// processes, and a value with no independent existence has none to declare.
	HasIdentity bool `json:"hasIdentity"`
	// HasAttributes is false for an enumeration, which carries literals instead.
	HasAttributes bool `json:"hasAttributes"`
}

StereotypeKind is one authorable class kind, as the palette offers it.

type Store

type Store struct {
	*sidecar.Store[Model]
}

Store persists information models as atomic, fsynced design-time sidecars. It is owned by the API run loop and does no locking of its own (I3).

func NewStore

func NewStore(dir string) (*Store, error)

NewStore opens the information-model directory. Models list newest first with a stable id tie-breaker, so a listing does not reshuffle between two reads that happened in the same second. IDs name their files directly and therefore must have the opaque hexadecimal shape token.New produces.

func (*Store) ForApplication

func (s *Store) ForApplication(applicationID string) ([]Model, error)

ForApplication returns one application's models, newest first.

type StoreMode

type StoreMode struct {
	Mode    string `json:"mode"`
	Label   string `json:"label"`
	Meaning string `json:"meaning"`
}

StoreMode is one authorable store mode, as the palette offers it.

func StoreModeOf

func StoreModeOf(m string) (StoreMode, bool)

StoreModeOf returns one mode, and whether it is in the subset at all.

type Subset

type Subset struct {
	Version          int                  `json:"version"`
	Notation         string               `json:"notation"`
	Stereotypes      []StereotypeKind     `json:"stereotypes"`
	AssociationKinds []AssociationKind    `json:"associationKinds"`
	Primitives       []PrimitiveType      `json:"primitives"`
	Multiplicities   []MultiplicityOption `json:"multiplicities"`
	StoreModes       []StoreMode          `json:"storeModes"`
	// Lifecycles is the state machine a class may carry (ADR-0259).
	Lifecycles LifecycleRules `json:"lifecycles"`
	// Matrix is AllowAssociation precomputed for every stereotype pair, so the canvas
	// can grey out a connection while it is being dragged without a round trip. Keyed
	// "from>to".
	Matrix map[string][]string `json:"matrix"`
	Limits []SubsetLimit       `json:"limits"`
}

Subset is the whole authoring contract, as the browser is served it.

func AuthoringSubset

func AuthoringSubset() Subset

AuthoringSubset builds the payload served to the browser.

type SubsetLimit

type SubsetLimit struct {
	Area   string `json:"area"`
	Reason string `json:"reason"`
}

SubsetLimit is one thing this build does not author, and why. Stating them is what keeps "we do not do this yet" apart from "this is not a thing".

type Summary

type Summary struct {
	ID            string `json:"id"`
	ApplicationID string `json:"applicationId"`
	Name          string `json:"name"`
	Documentation string `json:"documentation,omitempty"`
	Revision      int64  `json:"revision"`
	Classes       int    `json:"classes"`
	Associations  int    `json:"associations"`
	Stores        int    `json:"stores"`
	CreatedAt     int64  `json:"createdAt"`
	CreatedBy     string `json:"createdBy,omitempty"`
	UpdatedAt     int64  `json:"updatedAt"`
	UpdatedBy     string `json:"updatedBy,omitempty"`
}

Summary is the listing representation: everything a library row shows, without the classes and associations a canvas needs.

type Trace added in v0.7.0

type Trace struct {
	States      []TracedState      `json:"states"`
	Transitions []TracedTransition `json:"transitions"`
	// Current is the state the last write left the object in — the state it is in
	// now, which is not necessarily one the machine declares.
	Current string `json:"current,omitempty"`
	// Undeclared are the moves this instance made that no transition joins. It is the
	// run-time twin of the `data.illegal-transition` deploy check, and it catches what
	// that check cannot see: an instance started before the lifecycle was declared, and
	// a write by a process the check never ran against.
	Undeclared []Step `json:"undeclared,omitempty"`
	// Unknown names the states the object was written into that the machine does not
	// declare — `[aproved]` typed once, which is the failure this whole record exists
	// to make impossible. Each is named once however often it was written: it is one
	// fact about the model, not one per write.
	Unknown []string `json:"unknown,omitempty"`
}

Trace is one data object's declared lifecycle with its own life drawn on top.

func TraceLifecycle added in v0.7.0

func TraceLifecycle(lc *Lifecycle, trail []TrailEntry) Trace

TraceLifecycle reads a data object's trail against the lifecycle its class declares. The whole machine is always returned, not just the parts that were used: the overlay is the declared life with the lived one on it, and a picture of only what happened would answer a different question.

A class with no lifecycle traces to nothing at all — nil is the normal case and stays silent here exactly as it does everywhere else.

type TracedState added in v0.7.0

type TracedState struct {
	LifecycleState
	// Visited is whether the object was ever written into this state, Current whether
	// it is the state the last write left it in.
	Visited bool `json:"visited"`
	Current bool `json:"current"`
	// Entered counts the times the object arrived here — twice for a state it went
	// round to a second time. FirstAt and LastAt answer different questions and
	// neither substitutes for the other: when it first got here, and when it last did.
	Entered int   `json:"entered,omitempty"`
	FirstAt int64 `json:"firstAt,omitempty"`
	LastAt  int64 `json:"lastAt,omitempty"`
}

TracedState is one declared state with what this instance did in it. The declared half is embedded rather than copied so a state gains a field here the day the document gains one.

type TracedTransition added in v0.7.0

type TracedTransition struct {
	LifecycleTransition
	// Taken counts the moves this instance made that this transition accounts for.
	Taken int `json:"taken"`
	// LastAt and LastBy are the most recent of those moves — the element that made it
	// being the answer no class diagram can give, and the reason to draw this at all.
	LastAt int64  `json:"lastAt,omitempty"`
	LastBy string `json:"lastBy,omitempty"`
	// Ambiguous marks a transition that shares both its ends with another. The trail
	// records states, not transition ids, so where a model declares two ways from one
	// state to the same other, nothing in the record says which was taken. Both are
	// marked and both say so, rather than one being picked and quietly believed.
	Ambiguous bool `json:"ambiguous,omitempty"`
}

TracedTransition is one declared transition with what this instance did with it.

type TrailEntry added in v0.7.0

type TrailEntry struct {
	State string
	At    int64
	By    string
}

TrailEntry is one durable write to a data object, as the Data tab already reports it: the state the object held afterwards, when, and the BPMN element that made the write. By is empty where no element can be named — the seeding at instance creation, a write recorded before attribution existed, or an element whose definition is gone.

type UnresolvedRef

type UnresolvedRef struct {
	From  string `json:"from"`
	Role  string `json:"role"`
	Class string `json:"class"`
	Value string `json:"value"`
}

UnresolvedRef is a reference this instance cannot satisfy: a member holding a business key that names no object here.

type Usage added in v0.7.0

type Usage struct {
	Class         Class  `json:"class"`
	ModelID       string `json:"modelId"`
	ModelName     string `json:"modelName"`
	ApplicationID string `json:"applicationId"`

	Processes []ProcessUse `json:"processes"`
	Model     []ModelUse   `json:"model"`
	Summary   UsageSummary `json:"summary"`
}

Usage is everywhere one class is used, with the class itself, so a detail view is one call rather than two that can come to disagree.

func UsageOf added in v0.7.0

func UsageOf(models []Model, modelID, className string, procs []Process) (Usage, bool)

UsageOf reads everywhere one class is used. models must contain the model that holds the class and may contain any others — only its own application's are read, since that is the namespace its name resolves in — and procs are that application's processes. It reports false for a model or a class that is not there, rather than an empty usage, because "used nowhere" is a claim about something that exists.

type UsageSummary added in v0.7.0

type UsageSummary struct {
	Processes int `json:"processes"`
	Uses      int `json:"uses"`
	Reads     int `json:"reads"`
	Writes    int `json:"writes"`
	// Attributes and States are what processes *actually* touch, which is rarely all
	// of what the class declares — the gap between the two is the interesting part,
	// and it is the same gap the difference reading reports per application.
	Attributes []string `json:"attributes,omitempty"`
	States     []string `json:"states,omitempty"`
	Stores     []string `json:"stores,omitempty"`
	ModelUses  int      `json:"modelUses"`
}

UsageSummary is the shape of the answer before its rows: what a list column can show, and what the detail view says above its tables.

type ValidationResult

type ValidationResult struct {
	Valid    bool      `json:"valid"`
	Findings []Finding `json:"findings"`
}

ValidationResult is the whole verdict. Findings are ordered by where they are, so a re-validation of an unchanged model produces an identical list.

func Validate

func Validate(m Model) ValidationResult

Validate checks a whole model against the subset and against itself.

type Vocabulary

type Vocabulary struct {
	// contains filtered or unexported fields
}

Vocabulary is what an application's information models say, flattened for resolution: a class by name, and every attribute it has including the ones it inherits. Built once per check rather than walked per lookup, because a check resolves the same class once per association.

func NewVocabulary

func NewVocabulary(models []Model) *Vocabulary

NewVocabulary flattens the models of one application. A later model wins a name clash, matching the order the store lists them in.

func (*Vocabulary) Class

func (v *Vocabulary) Class(name string) (Class, bool)

Class resolves a declared type name.

func (*Vocabulary) Classes added in v0.7.0

func (v *Vocabulary) Classes() []Class

Classes lists every authored class by name, in a settled order. The lookup above answers "is this one modelled"; reading the difference between what is modelled and what is built has to walk the other way too.

func (*Vocabulary) Members added in v0.7.0

func (v *Vocabulary) Members(className string) []Attribute

Members is a class's attributes with inherited ones first — what the class actually has, rather than what its own definition lists.

func (*Vocabulary) Modeled

func (v *Vocabulary) Modeled() bool

Modeled reports whether the application has any information model.

func (*Vocabulary) Store

func (v *Vocabulary) Store(name string) (DataStore, bool)

Store resolves a data store by the name a process refers to it with.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL