Skip to content

Application and Storage Boundary

Hubuum has one application-facing storage boundary. A selectable storage backend implements that boundary in full; it is not a collection of optional features.

PostgreSQL and the experimental process-local memory adapter are selectable complete backends. The separate focused in-memory resource model remains a test tool.

Choose a Reading Path

The Boundary in One Page

HTTP handlers / workers / administration
                    |
                    v
        application services and policy
                    |
                    v
            opaque StorageContext
                    |
                    v
             StorageHandle
      common dispatch + observation
                    |
                    v
          complete StorageBackend
       | operation traits         |
       | audited StorageTransaction
                    |
                    v
           PostgreSQL adapter
                    |
                    v
     Diesel / SQL / pools / transactions

Dependencies point down the diagram. Calls and errors return upward through the same layers.

Values crossing the boundary are backend-neutral DTOs. An application consumer must never recover a pool, receive a Diesel row, build SQL, or handle a driver error.

The boundary has five responsibilities:

  1. The application owns use cases, public API models, authorization-policy selection, persistence-independent validation, and conversion from StorageError to ApiError.
  2. The storage contract owns operation-shaped traits, composable transaction-scoped resource APIs, backend-neutral requests and results, and the bounded storage error taxonomy.
  3. The opaque handle owns exhaustive backend dispatch plus common tracing and metrics. Callers do not select an adapter for each operation.
  4. Each adapter owns persistence rows, queries, native transactions, locking, driver errors, notifications, and explicit conversion to contract DTOs and StorageError.
  5. Certification combines reusable semantic conformance, complete-backend compatibility, native failure tests, and a sealed application registry. Implementing the Rust trait shapes alone does not make an adapter selectable.

Complete Means Complete

StorageBackend is the aggregate trait in crates/hubuum-storage-core/src/backend.rs. Its supertraits are the complete compile-time contract, including the mandatory TransactionStorage unit-of-work capability.

An adapter opts in structurally only after it implements every required operation trait and all six family bounds. Rust rejects the implementation if any method or bound is missing. The application then admits it through the sealed CertifiedStorageBackend registry only after its shared and native behavioral evidence passes.

The documentation organizes those traits into 20 semantic capability groups:

  • lifecycle and identity foundations;
  • permission-aware reads and computed data;
  • relations, history, inventory, and search;
  • tasks and long-running workflows;
  • event administration and event workers; and
  • operational state, retention, and execution context.

These groups are a documentation map over one indivisible contract. They are neither a second runtime contract nor feature flags. The semantic capability group map names every group, maps it to its required traits, and explains its relationships.

Adapters are statically linked Rust crates. Trait checking and crate versions are therefore the compatibility mechanism; Hubuum has no duplicate runtime contract version or dynamic capability negotiation.

The server and administrator CLI select one registered adapter through the typed HUBUUM_STORAGE_BACKEND setting. The only current value is postgresql; an empty value selects that default, while every other unknown value fails configuration parsing. Startup logs, metrics, and the administrator configuration endpoint report the selected backend and the same non-sensitive effective settings.

Services Depend on Exact Operation Traits

Collection, class, object, class-relation, and object-relation services use the specific traits that own their operations:

CollectionService -------> CollectionStorage
ClassService ------------> ClassStorage
ObjectService -----------> ObjectStorage
ClassRelationService ----> ClassRelationStorage
ObjectRelationService ---> ObjectRelationStorage
                                ^
                                |
                     PostgreSQL or focused model

ResourceStorage is the method-free resource family bound, but focused services depend on the exact operation traits above rather than on that aggregate. There is no default "unsupported" behavior. A focused model implements only the operation traits it can perform. Tests may inject it through those traits, but it cannot satisfy StorageBackend and cannot be selected for the application.

Production composition projects exact observed trait objects from a complete StorageHandle.

Application workflows that must compose several resource mutations use TransactionStorage. The callback receives an opaque StorageTransaction whose collections(), classes(), class_relations(), objects(), and object_relations() accessors return discoverable operation types. Every transactional mutation inherits one required EventContext.

Boundary Rules

The following rules are architectural invariants:

  • Application code accepts StorageContext, AuthorizationContext, an application service, or a backend-neutral capability trait.
  • Only an authorization-aware context may select the configured permission backend. A storage handle cannot silently substitute local authorization for an external policy backend.
  • Adapter inputs and results are crate-owned or storage-owned DTOs with private representation where practical.
  • A use case may compose safe resource primitives through TransactionStorage. The adapter owns the native transaction and exposes neither a connection nor a query language.
  • Invariant-heavy state machines remain one operation-shaped trait method. Task completion, restore application, retention, permission mutation, and similar workflows must not be reconstructed from lower-level calls.
  • Transaction-scoped mutations always inherit the transaction's EventContext. State and durable audit events commit or roll back together.
  • Ordinary audited mutations require EventContext and return StorageMutationOutcome: committed changes carry a durable StorageAuditReceipt, while genuine no-ops carry no receipt and append no event.
  • Imports and restores are restricted to the explicit ImportStorage and RestoreStorage capabilities. Those typed surfaces preserve or reconstruct history and are not unaudited shortcuts for ordinary writes.
  • Native mechanisms such as SQL cursors, statement timeouts, advisory locks, task-local database settings, and notification listeners remain private to the adapter.
  • Backend errors move upward exactly once:
adapter error -> StorageError -> ApiError
  • Every storage call uses bounded, static capability and operation labels. Entity IDs, names, queries, URLs, credentials, and payloads never become log or metric labels.
  • A backend is selectable only after it passes the shared compatibility suite and six-part conformance verifier plus its own native consistency, concurrency, recovery, and failure tests.

Architecture tests enforce these rules for the current source tree. The maintainer guide locates those checks; the testing guide explains what they do and do not prove.

The transactions and side-effects guide defines the decision rule, event guarantee, cancellation semantics, and adapter obligations in detail.

Current Workspace Shape

The application, reusable contracts, and native adapter are distinct:

hubuum application
|-- hubuum-domain
|-- hubuum-query
|-- hubuum-events-core
|-- hubuum-task-core
|-- hubuum-storage-core
|-- hubuum-storage-conformance
`-- hubuum-storage-postgres
  • hubuum-domain owns backend-independent validated identifiers, revisions, patches, and policy values extracted from the root application.
  • hubuum-query owns bounded query options, filters, sorts, cursors, scalar inference, and parsing. It describes query intent and does not expose SQL or database type names.
  • hubuum-events-core owns typed event identity, envelopes, mutation provenance, and event integration traits. Its public identifiers reuse hubuum-domain newtypes instead of raw database integers.
  • hubuum-task-core owns task values shared by storage and worker code.
  • hubuum-storage-core owns the complete storage contract, private-field DTOs, and semantic errors. It has no Actix, Diesel, global configuration, or ApiError dependency. Resource lifecycle, revision, metadata, and principal boundaries use domain IDs and revisions rather than persistence-shaped strings and integers.
  • hubuum-storage-conformance owns the reusable six-part behavioral verifier for receipts, no-ops, rollback, fan-out to a recording sink, telemetry, and exact revision-conflict propagation. It also owns the retention retry verifier for durable claim identity and idempotent completion, deterministic delivery, restore-coordination, and lease-loss protocol expectations, and the common application, service, readiness, and authenticated HTTP expectations. It is an experimental public development dependency for adapter authors.
  • hubuum-storage-postgres owns the native pool, TLS, endpoint diagnostics, generated schema, migrations, JSONB validation, query instrumentation, and all production PostgreSQL operations.
  • The root crate owns application services and static composition. It constructs the PostgreSQL adapter with telemetry and dedicated operational pools, then places it behind the opaque handle.
  • The root crate has no PostgreSQL module tree. Adapter-specific integration fixtures are typed, feature-gated APIs owned by hubuum-storage-postgres.

The backend-neutral contracts needed by an out-of-tree adapter form the experimental public seven-crate storage adapter SDK. External-crate integration tests nevertheless implement all 44 complete-backend traits and their 250 methods, compile every transaction port, exercise representative typed query APIs, and name public construction paths for all current adapter-returned values without crate-private access. Backend registration remains explicit, exhaustive, and application-owned. Hubuum does not load storage plugins dynamically.

The SDK is a supported pre-1.0 Rust source contract for statically linked adapters. It is not a promise that the PostgreSQL adapter, server composition, or a dynamic plugin ABI is public. Remaining follow-up work validates the boundary with an independent adapter and standardizes neutral audit-document construction before a stable 1.0 designation.

Moving a file does not by itself improve the boundary. Dependencies must continue to point from the application to contracts and from adapters to contracts, never from a contract or adapter back into the application.

Current Confidence

The PostgreSQL path is exercised against a real migrated database by shared backend contracts, PostgreSQL-specific tests, service tests, HTTP integration tests, destructive restore tests, query-budget tests, platform and feature builds, production-container tests, and benchmarks. CI also migrates representative data from the adjacent stable release, starts the new application, and restarts the previous application against the migrated schema.

The contract's methods, effects, selected input variants, and collection-shaped method semantics are inventoried mechanically. Every registered backend runs the reusable six-part audit verifier plus compact service, readiness, and authenticated HTTP point/list scenarios. Adapter-private deterministic failpoints prove rollback at representative compound-write and task-state-machine seams.

This is strong practical coverage, not a formal proof of portability. PostgreSQL is the only complete production adapter. The portable six-part, retention-retry, delivery-fault, restore-coordination, and lease-loss expectations are extracted, while backend provisioning and the broader application compatibility fixtures remain application-owned.

The testing guide gives the detailed assessment and the highest-value remaining improvements.