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:
Tenancy Tenants, projects, environments, and related policy scope.
Inventory Resource graph, topology, capacity, and allocation relationships.
Task Long-running task state, orchestration metadata, and cross-service workflow tracking.
Audit Operator-visible event history and policy-relevant change records.
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.
Related Decisions
- This ADR refines the Sentral role described in platform-plan.md.
- Public boundary concerns must align with 008-public-api-style.md.
- Inventory semantics must align with 009-resource-inventory-and-tenancy-model.md.
- Task handling must align with 019-workflow-retry-dead-letter-and-reconciliation.md.
More Information
- task resource schema
- audit record model
- quota and usage-accounting model
- API facade composition rules
Audit
- 2026-03-14: ADR proposed.