Application lifecycle
This document defines the lifecycle of an Application in Balyze.
It describes the possible states of a candidature, the allowed transitions between them, and the principles governing state changes.
Purpose of the lifecycle
The application lifecycle exists to:
- represent the real progression of a candidature
- enforce clear state transitions
- prevent inconsistent or ambiguous application states
- support future extensions without breaking existing rules
The lifecycle is explicit, finite, and intentionally constrained.
Application states
At the current stage, an Application can be in one of the following states:
Draft
The initial state of an application.
Characteristics:
- the application has not been submitted
- structural information may still be adjusted indirectly
- no submission timestamp is set
- the application is not considered active
This state is temporary and internal to the user.
Submitted
The application has been formally submitted.
Characteristics:
- the submission timestamp (
applied_at) is set - the application becomes historically relevant
- structural data is frozen
- contextual data remains editable
Once an application is submitted, it represents a point-in-time intent.
In progress
The application is actively being processed.
This state may represent:
- ongoing exchanges
- interviews
- follow-ups
- waiting periods
The application remains editable only within predefined contextual boundaries.
Rejected
The application process has ended unsuccessfully.
Characteristics:
- no further progression is expected
- the application is considered closed
- historical data must remain intact
This is a terminal state.
Accepted
The application process has ended successfully.
Characteristics:
- the application is considered completed
- no further lifecycle transitions are expected
- the application remains accessible for historical reference
This is a terminal state.
Allowed transitions
The lifecycle follows a strictly controlled transition graph:
draft→submittedsubmitted→in_progressin_progress→rejectedin_progress→accepted
Transitions are intentionally unidirectional.
Forbidden transitions
The following transitions are explicitly not allowed:
- returning to
draftafter submission - skipping directly from
draftto terminal states - modifying structural data after submission
- reopening a rejected or accepted application
These restrictions preserve historical consistency and simplify domain rules.
Editability and lifecycle interaction
Lifecycle transitions directly impact what data may be modified.
Key principles:
- structural data is frozen after submission
- contextual data remains editable throughout the lifecycle
- lifecycle state changes are always explicit actions
Detailed editability rules are documented separately.
Enforcement strategy
Lifecycle rules are enforced at the domain service layer.
- Controllers do not implement lifecycle logic
- Models do not orchestrate transitions
- Policies handle authorization, not lifecycle validity
This ensures consistent enforcement across all entry points.
Out of scope considerations
The following aspects are intentionally excluded at this stage:
- automatic transitions
- time-based state changes
- external system synchronization
- analytics-driven lifecycle extensions
These concerns may be revisited in later versions.
Rationale
This lifecycle favors clarity and predictability over flexibility.
By limiting transitions and freezing structural data early, the system:
- avoids ambiguous application states
- preserves historical accuracy
- reduces long-term complexity
Future extensions can build upon this foundation without breaking existing assumptions.