refactor(conference-scheduling): convert to Models Service SDK structure#1112
refactor(conference-scheduling): convert to Models Service SDK structure#1112larsbeck wants to merge 8 commits into
Conversation
adfc9c9 to
d43a295
Compare
There was a problem hiding this comment.
Pull request overview
This PR refactors the conference-scheduling quickstart to align with the Timefold Models Service SDK structure, introducing explicit DTO/input-output layers, model conversion/validation, updated demo-data flow, and updated build/QA conventions (coverage checks, arch rules, linting).
Changes:
- Introduces DTOs (input/output, metrics, config overrides, validation issue metadata) and a
ModelConvertor/ModelValidatorimplementation for solver-model translation and dataset validation. - Replaces the previous custom REST resources with the SDK
ModelRest-based REST surface; updates tests and the UI to the new/v1/...endpoints and demo-data semantics. - Updates the Maven build to platform conventions (parent POM, Error Prone/Spotless/PMD, JaCoCo thresholds, OpenAPI/Markdown linting, custom coverage helpers).
Reviewed changes
Copilot reviewed 65 out of 66 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/support/ShadowSourcesCoverageCheck.java | Adds a JaCoCo-based check to enforce full coverage for @ShadowSources methods. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/support/RecordWithMethodCoverageTest.java | Adds reflective execution of withXxx methods to keep record copy methods covered. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/support/CoverageReportSummaryTest.java | Tests the new JaCoCo CSV summary output. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/support/CoverageReportSummary.java | Summarizes JaCoCo coverage stats (instruction/branch + constraint provider line coverage). |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/SolverTestDataFactory.java | Introduces a DTO-based problem factory for solver tests. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/SolverManagerTest.java | Adds a Quarkus test using SolverManager + model conversion + metrics assertions. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceSchedulingConstraintProviderTest.java | Minor whitespace/formatting adjustment in constraint provider tests. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceScheduleResourceIT.java | Adds a native IT that exercises the new /v1/... REST endpoints. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/solver/ConferenceScheduleEnvironmentTest.java | Updates environment-mode test to use DTO-to-solver conversion instead of legacy demo REST. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/rest/ConferenceSchedulingResourceIT.java | Removes legacy integration test tied to old REST resources. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/rest/ConferenceScheduleResourceTest.java | Removes legacy unit test tied to old REST resources. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/ProjectStructureTest.java | Adds ArchUnit rules enforcing layer boundaries and record/DTO conventions. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/DtoWithMethodsUsageTest.java | Adds a test ensuring DTO withXxx methods are used and behave as expected. |
| use-cases/conference-scheduling/src/test/java/org/acme/conferencescheduling/demo/DemoDataBuilderTest.java | Tests the new DTO-based demo data builder. |
| use-cases/conference-scheduling/src/main/resources/META-INF/resources/index.html | Updates the UI (new endpoints, platform embedding hooks, asset loading changes). |
| use-cases/conference-scheduling/src/main/resources/application.properties | Updates model/platform metadata and termination/threading properties. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/justifications/ConferenceSchedulingJustification.java | Removes legacy solver justification record from its old package. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/ConstraintGroupTag.java | Introduces tags for constraint grouping metadata. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/solver/ConferenceScheduleConstraintGroup.java | Adds platform-facing constraint group metadata definitions. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleValidator.java | Adds dataset validation producing structured SDK issues. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleModelConvertor.java | Adds DTO↔solver model conversion, last-output application, and weight override application. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/service/ConferenceScheduleIssues.java | Defines issue classes mapping validation findings to SDK issue types/metadata. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ErrorInfo.java | Removes legacy error DTO (old REST layer). |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ConferenceScheduleSolverExceptionMapper.java | Removes legacy exception mapper (old REST layer). |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/exception/ConferenceScheduleSolverException.java | Removes legacy exception type (old REST layer). |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/DemoDataGenerator.java | Removes legacy REST-layer demo data generator. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceSchedulingResource.java | Removes legacy custom REST resource (replaced by SDK ModelRest). |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceSchedulingDemoResource.java | Removes legacy demo-data REST resource (replaced by SDK demo-data flow). |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/rest/ConferenceScheduleResource.java | Introduces ModelRest interface for schedules. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TimeslotIdDetail.java | Adds validation-issue metadata detail record for timeslot IDs. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TimeslotDTO.java | Adds DTO record for timeslots with withXxx methods and normalization. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkTypeDTO.java | Adds DTO record for talk types with normalization and withXxx. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkIdDetail.java | Adds validation-issue metadata detail record for talk IDs. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/TalkDTO.java | Adds DTO record for talks, including normalization and withXxx copy methods. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/SpeakerIdDetail.java | Adds validation-issue metadata detail record for speaker IDs. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/SpeakerDTO.java | Adds DTO record for speakers with normalization and withXxx. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/RoomIdDetail.java | Adds validation-issue metadata detail record for room IDs. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/RoomDTO.java | Adds DTO record for rooms with normalization and withXxx. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleValidationIssue.java | Defines validation issue types/codes/messages for the model. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleOutputMetrics.java | Adds output metrics record implementing SDK metrics interface. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleOutput.java | Adds model output DTO implementing SDK ModelOutput. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleInputMetrics.java | Adds input metrics record implementing SDK metrics interface. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleInput.java | Adds model input DTO implementing SDK ModelInput. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/dto/ConferenceScheduleConfigOverrides.java | Adds model config overrides record (weights) implementing SDK overrides interface. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Timeslot.java | Adjusts equality/overlap logic formatting and equality checks. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/TalkType.java | Uses LinkedHashSet for deterministic ordering and reformats equals. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Talk.java | Adds isScheduled() and refactors constructors/formatting; suppresses PMD for parameter list. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Speaker.java | Refactors constructors/formatting; suppresses PMD for parameter list. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/Room.java | Reformatting of equals method. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/UndesiredTagsJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/UnavailableTimeslotJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/RequiredTagsJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ProhibitedTagsJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/PreferredTagsJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/DiversityTalkJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ConflictTalkJustification.java | Moves justification record to domain/justification and enforces non-null description. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/justification/ConferenceSchedulingJustification.java | Adds shared justification record in the new domain/justification package. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/ConferenceSchedule.java | Migrates to HardMediumSoftScore, adds metrics + constraint weight overrides support, removes legacy solverStatus field. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/domain/ConferenceConstraintProperties.java | Removes redundant explicit no-arg constructor. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/demo/DemoDataGenerator.java | Implements SDK demo-data generator returning a DTO ModelRequest. |
| use-cases/conference-scheduling/src/main/java/org/acme/conferencescheduling/demo/DemoDataBuilder.java | Adds DTO-based deterministic demo data builder. |
| use-cases/conference-scheduling/README.md | Updates commands/paths and formatting for current conventions. |
| use-cases/conference-scheduling/pom.xml | Migrates to service-parent POM and adds plugins for quality gates, coverage, linting, and platform integration. |
| use-cases/conference-scheduling/lint-markdown.sh | Adds a helper script for running markdown lint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public ConferenceScheduleConfigOverrides() { | ||
| this(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L); | ||
| } |
There was a problem hiding this comment.
One of those ridiculous constructors. This code does not tell the reader anything.
What does the 15th null do?
There was a problem hiding this comment.
Agreed, it is ridiculous — unfortunately it currently can't be removed. The SDK's DefaultConfigProfileProcessor instantiates the ModelConfigOverrides type reflectively via getDeclaredConstructor().newInstance(), so a no-arg constructor is mandatory (the build fails without it). And for a record, a no-arg constructor is forced to spell out one null per component: every secondary constructor must delegate to the canonical one, which for 20 constraint weights means 20 nulls. It can't be hidden either — the canonical constructor of a public record must stay public.
What the nulls mean is at least well-defined now: "no override" per weight, so the profile/default weights apply (this also fixed a bug where the no-arg constructor set every weight to 1, silently overriding everything).
Filed TimefoldAI/timefold-solver#2493 to fix this properly in the SDK: if DefaultConfigProfileProcessor constructed records via their canonical constructor with per-type default args, every converted model could delete this constructor entirely.
triceo
left a comment
There was a problem hiding this comment.
IMO if were already doing such a major refactor, we should reconsider some of this code.
The DTO constructors are nasty, and overall IMO there needs to be less code if we want to use this as an actual example. Right now, the amount of code scares me away.
…w-feedback refactor(conference-scheduling): address PR TimefoldAI#1112 review feedback
- Move solver justifications to domain/justification with shared ConferenceSchedulingJustification interface - Add DTO layer (input/output/metrics/config overrides/validation) - Add DemoDataBuilder/DemoDataGenerator for demo data - Update pom.xml for platform model conventions
…edback - replace generated withXxx bloat and telescoping constructors with builders - fail fast on unknown references in model conversion - drop node/npm from the maven build and custom coverage harness - move to 999-SNAPSHOT parent, drop JFrog repositories Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Reverts the removal of the vacuum OpenAPI lint and markdownlint from the maven build and drops the DtoSchemaDescriptionTest that stood in for them. Also commits the previously missing .vacuum-ruleset.yaml and .markdownlint-cli2.jsonc (the root .gitignore's ".*" pattern had silently excluded them, so the lint steps could never run) and adds the missing @Schema description on ConferenceScheduleIssue that the restored vacuum lint caught. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Vacuum is a Go binary; the npm package was only a distribution channel. Download the pinned platform binary from GitHub releases instead of installing node/npm via frontend-maven-plugin. Markdownlint is removed entirely (spotless keeps formatting markdown). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Moves the vacuum lint out of the maven build into a dedicated GitHub workflow, following vacuum's documented CI/CD setup. The workflow lints the committed spec of every model shipping a .vacuum-ruleset.yaml, so future converted models are covered automatically. The generated src/build/openapi.json is now committed (force-added past the root .gitignore's "build" pattern) to keep the lint standalone and make spec drift visible in diffs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
Unknown prerequisite talk codes now fail fast like every other reference, the dataset info span is closed properly, a stray jQuery .fail() argument is removed and DemoDataBuilderTest uses assertNull. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
d1dac7e to
5e23bd8
Compare
Note on the failing
|
The UI loads all assets from CDNs, so the WebJars runtime dependencies and the web-dependency-locator extension were dead weight. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
| <includes> | ||
| <include>org.acme.conferencescheduling.*</include> | ||
| </includes> |
| <includes> | ||
| <include>org.acme.conferencescheduling.solver.*ConstraintProvider</include> | ||
| </includes> |
…leset The header still pointed at the removed exec-maven-plugin wiring; the lint now runs in the lint_openapi.yml workflow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qae2bExuXdkKjDJjFXynGi
|
| spec="$model_dir/src/build/openapi.json" | ||
| if [ ! -f "$spec" ]; then | ||
| echo "::error::$model_dir has a vacuum ruleset but no committed spec at $spec" | ||
| exit 1 | ||
| fi |
| # Run in CI: .github/workflows/lint_openapi.yml lints the committed spec of every | ||
| # model that ships a .vacuum-ruleset.yaml. | ||
| # Run locally: install vacuum (https://quobix.com/vacuum/installing/), then | ||
| # vacuum lint --ruleset .vacuum-ruleset.yaml --details --fail-severity error src/build/openapi.json |
I don't think it was ever net negative? We are introducing a lot of new things here and we are splitting the code into dtos and domain plus introduce tests, etc. So that is expected imho. |
Converts the conference-scheduling quickstart to the Models Service SDK structure:
domain/justificationwith a sharedConferenceSchedulingJustificationinterfaceDemoDataBuilder/DemoDataGeneratorfor demo datapom.xmlfor platform model conventionsReview feedback incorporated
Addresses the review comments from @triceo, @TomCools and Copilot:
Less code, readable construction (net ~-970 lines)
Talk/Speakerand onTalkDTO/SpeakerDTO; the telescoping 22-/11-arg constructors and theirPMD.ExcessiveParameterListsuppressions are gone, so the PMD parameter-list rule is enforced againwithXxxmethods deleted except the three actually used; the ArchUnit wither-per-component rule (which manufactured the bloat) is removed,with_methods_must_be_usedstays and now bans dead withersimmutableCopy/orEmpty/nonNegativehelpersTooManyStaticImportsraised to 30)ConfigOverrides: the 20 withers are gone and the no-arg constructor now yields all-null "no overrides" (previously it silently overrode every weight with 1). The constructor itself must stay because the SDK requires it reflectively — tracked in Service SDK: ModelConfigOverrides records are forced to declare an all-null no-arg constructor timefold-solver#2493Build & quality gates
CoverageReportSummary,ShadowSourcesCoverageCheck,RecordWithMethodCoverageTest) removed — plain JaCoCo gates unchanged (30% instruction / 20% branch bundle, 100% line on the ConstraintProvider).github/workflows/lint_openapi.ymllints the committed spec of every model shipping a.vacuum-ruleset.yaml(covers future models automatically).src/build/openapi.jsonand.vacuum-ruleset.yamlare now actually committed — the root.gitignore's.*andbuildpatterns had been silently excluding them, so the lint could never run beforequarkus-web-dependency-locatordropped (UI loads assets from CDN)999-SNAPSHOT(replaced on release; CI builds the solver from source), JFrog repositories removed — everything resolves from Maven CentralCorrectness
IllegalArgumentException("Unknown <kind> '<key>'.")on unknown talk-type/speaker/timeslot/room/prerequisite references instead of leaking nulls into the solver modeltalkTypeName, not just nullConferenceScheduleIssuegot its missing OpenAPI description (vacuum scores 100/100); small UI/test fixes (unclosed span, stray jQuery.failargument,assertNull), READMEcdpath correctedNote on CI: the failing
Maven Long-Runningjobs are pre-existing repo infrastructure breakage —actions/checkoutv7 refuses fork-PR checkouts underpull_request_target(see this comment); unrelated to this PR.🤖 Generated with Claude Code