DFE-Digital / DFE-Digital/check-performance-data

[Tech debt] JourneyController holds domain logic and flow-specific ids

Open
#400 0 comments 0 reactions 1 assignee View on GitHub

@davidgouge is already working on this.

Since Sep 8, 2026.

tech-debt
Dominant language
C#
Stars
0
Forks
1
Avg merge
19h 21m
Merged PRs (30d)
54

Description

Overview

JourneyController is 1,524 lines with 17 constructor dependencies. It is the largest hand-written file in the solution and it is a controller — against the CLAUDE.md rule that controllers must guard the request, delegate to a service, and translate the result.

It holds private static methods that are pure domain logic branching over Application types (RequestState, QuestionFlowConfig), which the rule names explicitly as belonging in Application. It also hard-codes question ids from specific flow config files, coupling a supposedly generic controller to the contents of three JSON files.

This matters beyond its line count: it is the file a new developer reads first, and the least representative example of how the team wants code written.

Evidence

Domain logic in the controller — src/DfE.CheckPerformanceData.Web/Controllers/Journey/JourneyController.cs:

Method Line
FirstIncompleteEnquiryPage 522
IncorrectGradeGap 531
ResultDoesNotBelongGap 545
MissingQualificationGap 556
IsCohortWide 1258
DetermineStatus 1339
IsEvidencePageValid 1347

Flow-specific constants at :43-48q-revised-grade, q-syllabus-code, q-missing-grade.

For scale: the next-largest hand-written file is 806 lines and the median controller is 87.

Proposed fix

  1. Extract an IEnquiryCompletenessService into Application/Journey/, taking the four gap methods plus IsCohortWide, DetermineStatus and IsEvidencePageValid. These become directly unit-testable without an MVC context.
  2. Move the flow-specific question ids out of the controller and into the flow config as typed metadata — e.g. a role field on the question ("role": "revisedGrade") — so the controller looks a question up by role rather than naming its id.
  3. Follow-up (can be a separate PR): split the results-enquiry actions (ResultSearchPage/Post, QualificationSearchPage/Post, ConfirmResultsEnquiryAsync, EnquiryConfirmation) into their own controller.

Steps 1 and 2 should remove roughly 200 lines and several of the 17 dependencies.

Acceptance criteria

  • No private method in JourneyController branches over RequestState or QuestionFlowConfig.
  • No flow-config question id appears as a constant in the Web layer.
  • The extracted service has unit tests covering each completeness gap.
  • QuestionFlowValidatorAlignmentTests extended to pin the new role metadata against the configs.
  • No behavioural change to the journey — existing E2E tests pass unchanged.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.