Sentral internal decomposition SENTRAL 020

Proposed Architecture Sentral Decomposition Bounded contexts Inventory

Defines Sentral as a control-plane center composed of bounded contexts rather than a single undifferentiated domain.

Author
Lars Solem
Updated

Status

Proposed on 2026-03-14 by Lars Solem.

Context

Sentral is currently the most responsibility-heavy service in the architecture. It acts as:

  • public API boundary
  • system of record for core platform state
  • task and orchestration owner
  • inventory and resource graph owner
  • tenancy owner
  • audit owner

That is a legitimate control-plane center, but without explicit internal decomposition it risks becoming a monolith that is hard to evolve, reason about, or scale operationally.

Decision

Dataverket keeps Sentral as the control-plane center, but defines it internally as a set of bounded contexts rather than one undifferentiated service blob.

The initial bounded contexts are:

  1. Tenancy Tenants, projects, environments, and related policy scope.

  2. Inventory Resource graph, topology, capacity, and allocation relationships.

  3. Task Long-running task state, orchestration metadata, and cross-service workflow tracking.

  4. Audit Operator-visible event history and policy-relevant change records.

  5. API facade Public resource API and cross-domain composition layer.

Sentral must also have an explicit concurrency and consistency model for those contexts. NATS-based workflows and PostgreSQL-backed state do not by themselves define what happens when concurrent writes or competing allocations occur.

Why this model

This keeps the architectural advantages of a central control-plane facade while reducing the risk that “Sentral” becomes a synonym for “everything that had nowhere else to go”.

The decomposition is intended to:

  • reduce conceptual sprawl
  • reduce schema entanglement
  • make ownership boundaries reviewable
  • allow later extraction if one domain needs independent scaling or delivery

Deployment posture

The initial implementation may still ship as:

  • one repository
  • one process
  • one deployment unit

This ADR does not require premature microservice fragmentation.

What it does require is:

  • explicit internal boundaries
  • schema separation with mandatory isolation for the inventory context from day 1
  • no casual cross-domain table coupling
  • a path to later extraction without redesigning the domain model

Data ownership model

Each bounded context should own its own relational schema or equivalent persistent boundary.

The inventory context is not optional in this respect. It should have its own schema boundary from day 1 because it combines high-volume graph reads, allocation-sensitive writes, and operationally critical topology state.

Cross-context interactions should prefer:

  • explicit APIs
  • task handoff
  • event publication

They should avoid:

  • arbitrary direct reads into another context’s mutable tables
  • hidden coupling through shared write access

Inventory isolation requirement

Inventory should be treated as the strongest internal boundary inside Sentral.

That means:

  • the inventory context should own its own PostgreSQL schema from day 1
  • the API facade should read inventory through explicit context interfaces or read models rather than by casual table access
  • tenancy, task, and audit contexts should not share write access into inventory tables
  • extraction of inventory into its own deployment unit later should not require a schema redesign first

This does not force inventory into a separate process immediately, but it does force the storage and interface boundary that makes later operational tuning realistic.

Concurrency and consistency posture

Sentral should use a pragmatic hybrid consistency model rather than one universal locking rule.

The baseline posture is:

  • optimistic concurrency for ordinary mutable resource updates where concurrent edits are expected but contention is usually low
  • transactional reservation or pessimistic locking for scarce inventory and allocation workflows where double-allocation would be unacceptable
  • task-driven saga-style orchestration across service boundaries rather than distributed transactions between services

This keeps normal API updates simple while still protecting the parts of the system where races would corrupt inventory or capacity state.

Optimistic concurrency for normal edits

For resources such as project metadata, policy objects, and many inventory attributes, Sentral should prefer optimistic concurrency with explicit version checking.

That means:

  • resource records should carry a version or equivalent concurrency token
  • conflicting writes should fail explicitly rather than silently overwriting newer state
  • APIs should expose conflict responses that tell the caller to re-read and retry intentionally

This is the default for human and API-driven edits where merge-or-retry behavior is safer than broad locking.

Pessimistic or transactional reservation for scarce resources

For workflows involving scarce or uniquely allocatable resources, Sentral should prefer transactional reservation semantics backed by PostgreSQL constraints and locking where needed.

Examples include:

  • allocating a specific server
  • consuming capacity from a finite pool
  • assigning a VLAN, IP block, or other uniqueness-constrained resource
  • recording a lease or reservation that must not be duplicated

In these cases the system should:

  • serialize the critical allocation step
  • rely on database-enforced uniqueness where possible
  • fail one contender clearly rather than allowing parallel success followed by later reconciliation

Reconciliation is useful after interruption, but it is not a substitute for correct exclusivity on scarce resources.

Saga-style orchestration across domains

Once a workflow crosses from Sentral into other services such as Nett, Maskin, or Plattform, the platform should not assume one ACID transaction across the whole workflow.

The expected model is:

  • Sentral commits local intent and reservation state first
  • a task is created with explicit workflow state
  • domain services perform their steps asynchronously
  • compensating or cleanup actions are used where later steps fail
  • final convergence is achieved through task state plus reconciliation, not distributed two-phase commit

This is the consistency model that fits NATS-driven orchestration without pretending away partial failure.

Conflict examples

The architecture should behave explicitly in cases such as:

  • two operators editing the same inventory object: optimistic conflict, one write succeeds and the other must re-read before retrying
  • two workflows attempting to allocate the same scarce resource: one reservation succeeds, the other receives an allocation conflict or dependency-blocked outcome
  • one workflow updating local Sentral state while a downstream service is delayed: local transaction commits, downstream progress is tracked as task state, and reconciliation finishes or compensates later

API facade role

The public API facade may compose data from several contexts, but it does not own all underlying data itself.

Its job is to:

  • present a coherent public resource model
  • enforce authentication and authorization at the public boundary
  • coordinate task creation for asynchronous operations
  • aggregate context-specific data for operator and tenant use

Inventory and task centrality

Inventory and task contexts are especially central because many domain services depend on them.

That means:

  • they need strong interface discipline
  • they must not become arbitrary dumping grounds
  • changes to them should be reviewed with platform-wide impact in mind

Inventory deserves extra caution because it combines some of the heaviest read patterns with some of the most correctness-sensitive writes. Keeping it isolated from the API facade at the schema and interface level is part of how Sentral avoids becoming impossible to tune.

Relationship to domain services

Sentral does not absorb the responsibilities of domain services.

It must not own:

  • network device-specific logic that belongs in Nett
  • VM lifecycle logic that belongs in Maskin
  • cluster lifecycle logic that belongs in Plattform
  • object storage internals that belong in Objekt

Sentral coordinates and records. Domain services execute their domain-specific logic.

Consequences

  • Sentral remains the architectural center without being allowed to become a design junk drawer
  • the team can start simple operationally while preserving future modularity
  • schema and ownership discipline become explicit engineering requirements
  • inventory gets a mandatory schema boundary from day 1 instead of being deferred as a later cleanup
  • commercial billing is explicitly deferred instead of being assumed as an early bounded context

Decision Outcome

Proposed. This ADR records the current preferred direction and still needs acceptance before it becomes binding.

More Information

  • task resource schema
  • audit record model
  • quota and usage-accounting model
  • API facade composition rules

Audit

  • 2026-03-14: ADR proposed.