Architecture Decision Record

BSFG ADR-0017

Status: Accepted · Date: 2026-03-06

Status: Accepted

Date: 2026-03-06

Context

BSFG facts must remain durable, replayable, hashable, and domain-agnostic. The object portion of a fact therefore needs a representation that is:

  • stable under hashing and idempotency checks
  • independent of any one domain’s closed type hierarchy
  • easy for heterogeneous producers and consumers to emit and interpret
  • compatible with optional schema governance without forcing transport-time validation

The design must decide whether object payloads are carried as strongly typed transport variants or as a canonicalized neutral data form.

Options Considered

Option Description Benefits Drawbacks
Typed protobuf oneof payloads Encode each supported object shape as a protobuf oneof variant. strong compile-time typing
clear generated APIs
closed variant set
transport becomes domain-coupled
schema churn propagates into boundary protocol
Arbitrary JSON text Store object payload as non-canonical JSON string bytes. flexible
easy for producers to emit
not stable for hashing
equivalent objects may serialize differently
weak idempotency foundation
Binary blobs only Treat object payload as opaque bytes and leave interpretation entirely external. maximal neutrality
no JSON handling required
poor inspectability
harder debugging
weak semantic interoperability
Canonical JSON bytes with optional schema reference (Selected) Store object payload as canonical JSON bytes and allow an optional schema URI/version in the envelope. stable hashing and idempotency
domain-agnostic transport
human-inspectable semantics
schema governance possible without transport lock-in
runtime validation is externalized
producers must canonicalize correctly
strong typing moves downstream

Decision

BSFG fact objects use canonical JSON bytes as the transport representation.

fact = (subject, predicate, object_json)

The object_json field is the canonical serialized form of the object payload. Producers and consumers may deserialize it into richer local types, but the boundary substrate treats it as canonical JSON bytes.

Schema governance is supported through an optional schema reference carried in the envelope, for example:

object_schema = "urn:bsfg:schema:batch.step_completed:v1"

BSFG does not require transport-time schema validation. Validation, compatibility checks, and stricter typing belong to producers, consumers, and domain services.

Consequences

Benefits:

  • idempotency can rely on stable payload representation
  • transport remains neutral across bounded contexts
  • facts are inspectable without a closed generated-type universe
  • schema governance can mature incrementally rather than being forced into the transport substrate

Tradeoffs:

  • canonicalization errors become a producer responsibility
  • runtime validation is not automatic at the boundary
  • consumers that want strong typing must impose it themselves