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
- func AllowedFrom(fromStereotype, toStereotype string) []string
- func BusinessKey(vocab *Vocabulary, className string, value json.RawMessage) string
- func CheckApplication(cps []*compiler.CompiledProcess, vocab *Vocabulary) []compiler.Problem
- func CheckDataFlow(cp *compiler.CompiledProcess, vocab *Vocabulary) []compiler.Problem
- func DetectImportFormat(document []byte) (string, bool)
- type AccessResolver
- type ApplicationAccess
- type Association
- type AssociationKind
- type Attribute
- type CatalogEntry
- type Class
- type Clock
- type DataStore
- type Derivation
- type Diff
- type DiffFinding
- type End
- type Finding
- type Gap
- type Graph
- type IDGenerator
- type ImportNote
- type ImportResponse
- type ImportResult
- type Lifecycle
- type LifecycleRules
- type LifecycleState
- type LifecycleTransition
- type LossNote
- type Model
- type ModelUse
- type MultiplicityOption
- type ObjectAttribute
- type ObjectLink
- type ObjectNode
- type ObjectValue
- type PrimitiveType
- type Process
- type ProcessUse
- type Projection
- type Refusal
- type Service
- func (s *Service) DeleteForApplicationOnLoop(applicationID string) error
- func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleImport(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSchema(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSubset(w http.ResponseWriter, _ *http.Request)
- func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleValidate(w http.ResponseWriter, r *http.Request)
- func (s *Service) ModelsForApplicationOnLoop(applicationID string) ([]Model, error)
- func (s *Service) ModelsOnLoop() ([]Model, error)
- func (s *Service) VocabularyOnLoop(applicationID string) (*Vocabulary, error)
- type Step
- type StereotypeKind
- type Store
- type StoreMode
- type Subset
- type SubsetLimit
- type Summary
- type Trace
- type TracedState
- type TracedTransition
- type TrailEntry
- type UnresolvedRef
- type Usage
- type UsageSummary
- type ValidationResult
- type Vocabulary
Constants ¶
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.
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.
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.
const ( KindDiffClass = "class" KindDiffMember = "member" KindDiffState = "state" KindDiffTransition = "transition" )
What kind of thing differs.
const ( ImportFormatJSON = "json" ImportFormatXMI = "xmi" )
The formats an import may arrive in.
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.
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.
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.
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.
const ( MultOptional = "0..1" MultOne = "1" MultMany = "0..*" MultAtLeast1 = "1..*" )
The multiplicities an attribute or an association end may carry.
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.
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".
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.
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.
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.
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.
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 ¶
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 ¶
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 ¶
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 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
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 ¶
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 ¶
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.
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 ¶
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 ¶
Refusal explains a rejected connection in words a modeler can act on.
func AllowAssociation ¶
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
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
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
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 ¶
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 ¶
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.
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 ¶
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 ¶
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
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
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 ¶
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.