Reference
Engineer-facing reference for growing a SolverForge domain model after scaffolding.
Extend the Domain
Treat the generated scaffold as a starter shell, not the center of gravity. The real domain model belongs in your application code: the types, invariants, and derived helpers that describe the planning problem you actually need to solve.
What this page is for
- deciding what belongs in facts, entities, solutions, and app-level helpers
- growing beyond the stock scaffold without turning it into a dumping ground
- choosing between scalar planning variables and planning lists
- keeping SolverForge-specific annotations narrow while the domain stays readable
Keep the boundary crisp
| Put it in the domain model | Put it somewhere else |
|---|---|
| entities the solver changes | HTTP handlers, database code, and CLI orchestration |
| immutable input facts | page components and frontend formatting |
| planning variables and score fields | ad-hoc migration logic |
| derived helpers that explain the model | solver search policy that belongs in solver.toml |
| fixtures and validation helpers for this problem | generic runtime internals that should stay in SolverForge crates |
Choose the right modeling primitive
| Use | When it fits | Typical example |
|---|---|---|
| problem facts | immutable reference data | employees, skills, depots, rooms, vehicles |
| planning entities | records whose assignment or ordering can change | shifts, visits, tasks, jobs |
| standard planning variables | one entity chooses one value from a range | a shift picks an employee |
| list planning variables | order matters and insertion/removal is the core move | route stops, job sequences, task chains |
Use mixed models when the problem really has both assignment and sequencing.
The current runtime builds one ModelContext per planning model, and the stock
construction heuristics already understand mixed scalar-plus-list problems.
A practical growth path
- Start from the scaffolded shell and keep its generated files thin.
- Add the real facts, entities, and variables that match production data.
- Move sample data, validation, and fixtures next to the app modules that own the domain.
- Split large models into app modules once the domain stops fitting cleanly in one file.
- Keep the solver-specific annotations close to the fields they describe, but keep problem-specific behavior in ordinary Rust methods and modules.
Common extensions
| You need to add… | Typical change |
|---|---|
| another planning dimension | add a new fact collection and reference it from entity variables |
| optional assignment | use allows_unassigned = true on the standard variable |
| ordering behavior | introduce a planning list and list-aware constraints |
| richer scoring context | add immutable facts or derived helper methods instead of shoving logic into constraints |
| validation or import normalization | add app-side builders or conversion layers before solving |
Suggested module layout
src/
domain/
facts.rs
entities.rs
solution.rs
fixtures.rs
validation.rs
constraints/
solver/
That is not a framework requirement. It is a good default when the scaffold is no longer tiny.
Rules of thumb
- Keep problem facts immutable.
- Keep entities focused on solver-owned state plus the fields needed to reason about that state.
- Prefer derived helper methods over leaking normalization code into constraints.
- Treat the scaffold as disposable starter code once the app has a real shape.
- Reach for list variables when order is the point, not when a single scalar assignment would do.