Architecture Decision Record

BSFG ADR-0021

Status: Accepted · Date: 2026-03-06

Status: Accepted

Date: 2026-03-06

Context

In BSFG, a fact is represented as:

fact = (subject, predicate, object_json)

The subject identifies what the fact is about. The object carries structured payload. The predicate therefore carries the relationship that the fact asserts.

Predicate design affects:

  • readability of facts in logs and diagnostics
  • stability of cross-system contracts
  • replay and projection logic
  • how much accidental business detail leaks into identifiers

The architecture needs a predicate style that is stable, compact, and expressive without turning predicates into sentence fragments or domain-specific mini-protocols.

Options Considered

Option Description Benefits Drawbacks
Free-form text predicates Allow arbitrary natural-language predicate phrases. maximal local flexibility
easy to invent ad hoc
poor consistency
hard to govern
weak replay/projection discipline
Noun-only property names Use property-style names such as status, owner, or attachment. compact
familiar in some data models
ambiguous for process facts
weaker relation semantics
less clear in eventful manufacturing contexts
Predicates embedded into subject Encode relation meaning inside the subject namespace rather than as a separate predicate field. fewer top-level fields
can feel convenient in the short term
subject identity becomes unstable
routing and semantics get mixed
harder partitioning and governance
Stable lower_snake_case relation names (Selected) Use compact relation names as predicates, organized by meaning rather than by transport mechanics. clear fact readability
stable contracts across producers and consumers
good fit for replay and projection logic
keeps subject identity separate from relation meaning
requires vocabulary governance
teams must resist inventing overlapping synonyms

Decision

BSFG predicates use stable lower_snake_case relation names.

The predicate expresses the relation asserted about the subject, while detailed parameters remain inside object_json.

subject   = batch:PlantA/B1042
predicate = step_completed
object    = { "step": "sterile_filter", "result": "ok" }

Predicates should be concise relation names, not sentence fragments and not encoded summaries of the whole payload.

Predicate families include:

  • domain predicates: step_started, step_completed, alarm_raised, sample_taken
  • state predicates: state_is, status_is, mode_is, value_is
  • protocol predicates: received_by, confirmed_by, stored_at, attached_as

BSFG does not enforce a closed global predicate list, but predicate names must remain stable within a bounded context and should be curated to avoid synonym drift.

Consequences

Benefits:

  • facts remain readable in logs, dashboards, and replay tools
  • subject identity stays clean and stable
  • object payload can evolve without renaming the relation
  • projection code can organize logic around stable relation names

Tradeoffs:

  • predicate naming requires shared discipline
  • governance is needed to avoid overlapping forms such as completed vs step_completed
  • some teams may initially prefer more free-form event naming and will need to adapt