ADR-001: Product Assignment State Machine
| Date | Author | Status |
|---|---|---|
| 2026-03-18 | Fabian Beyerlein | Proposed |
Context
The entities of our internal shop system (OrderGroup and OrderRequest) are
used to drive the state of an ESL. This tightly couples our internal shop
system to the state of an ESL which makes it difficult to add third-party
integrations that are supposed to bypass our shop.
Decision
Create a dedicated entity ProductAssignmentState with this pseudo shape
type ProductAssignmentState struct {
ID uuid.UUID
AssignmentID uuid.UUID
State AssignmentState
UpdatedAt time.Time
// These audit fields don't really have any constraints
Reason string
UpdatedBy string // integration identifier (internal, erp-x-v1, ...)
}
type AssignmentState string
const (
AssignmentStateDefault AssignmentState = "DEFAULT"
AssignmentStateRequested AssignmentState = "REQUESTED"
)
A port will be provided by the topology module that allows other modules to
drive the state of a ProductAssignment. When gathering data for ESL change
handling we should fetch the ProductAssignmentState instead of checking for
open/unfulfilled OrderRequests. This shifts the responsibility of the state
and the logic to derive it to the appropriate party - the shop integration.
There will be no history of this state for now.
Creating a ProductAssignment now also has to create a ProductAssignmentState
with DEFAULT as the state.
State Transitions
State transitions are unrestricted for now.
Port
Note
The port uses ProductAssignmentState rather than AssignmentState
to avoid naming collisions in the shared ports package. Internally the
topology module maps this to its own domain.AssignmentState type.
// backend/apps/solaris/ports/topology.go
package ports
type ProductAssignmentStateManager interface {
Transition(
ctx context.Context, orgID, assignmentID uuid.UUID,
newState ProductAssignmentState, reason, updatedBy string,
) error
}
type ProductAssignmentState string
const (
ProductAssignmentStateDefault ProductAssignmentState = "DEFAULT"
ProductAssignmentStateRequested ProductAssignmentState = "REQUESTED"
)
Alternatives considered
- Keep inferring state from
OrderRequest- Means for third-party integrations our shop system is always involved, even if nobody uses it
- Since nobody is using our shop in such cases, we would need workarounds
for approving/rejecting
OrderRequests to trigger the state change
- Simple field on
ProductAssignment- Less extensible
- Adding audit fields for this means we pollute the
ProductAssignmententity with fields likeStateReason,StateUpdatedBy
Consequences
- Allows third-party integrations and other modules to drive the state of an ESL
- Removes the need to have an
OrderRequestto drive state - Internal shop system gets refactored to drive state in addition to managing it's own entities
- Big refactoring burden for the internal "shop system"
- Migration is going to be difficult, current state has to be inferred from the
state of any
OrderRequests associated with everyProductAssignment.