6.3 KiB
Service Simplification Plan
Status: draft Last updated: 2026-04-05
Purpose
This document tracks the next pass of service simplification work. The goal is to reduce orchestration density, make the booking flow easier to follow for a human reader, and keep responsibilities aligned with the actual boundaries in the code.
The emphasis is not on deleting services for its own sake. The emphasis is on:
- keeping one clear owner for each meaningful boundary
- removing thin wrappers and pass-through helpers
- avoiding services that mostly shuffle data between layers
- keeping presentation concerns out of calculation and orchestration code
Current Read
The codebase is already in a better place than it was at the start of the refactor, but a few services still carry more than one responsibility:
BookingServiceno longer owns session lifecycle, baseline snapshot handling, return URL management, or room grouping. That work now lives inBookingSessionServiceandBookingRoomSelectionService, which keeps the booking orchestration boundary narrower.BookingServicestill covers hydration, booking bootstrap, service preselection, and booking status rules.BookingParticipantCountServicenow handles only participant count shaping.ParticipantPrepopulationServicenow owns applicant prefill plus the create-mode dummy-data shortcut.BookingPriceCalculatorServiceis focused on pricing, but it still sits close to display-oriented behavior in adjacent code paths.TravelDataServiceremains broad and is likely the next larger boundary after booking orchestration is reduced.
One registry stands out as a real orchestration boundary and should be left alone for now:
ParticipantFieldHandlerRegistry
It is not just a lookup table. It owns execution order, edit-mode mutability gating, and synchronization of submitted form data back into the DTO state.
Next Pass
1. Reduce BookingService
Primary goal: make the booking create/edit flow easier to read by splitting unrelated concerns.
Concrete next steps:
- keep booking session lifecycle in one place
- extract baseline room snapshot handling into a narrower helper or dedicated service
- separate return URL handling if it stays conceptually unrelated
- keep
startFreshBooking()focused on booking bootstrap rather than general session utilities - keep hydration behavior obvious and local to the booking session path
Decision rule:
- if a method only forwards to DTO/session behavior, prefer removing the wrapper
- if a method is a genuine workflow owner, keep it and narrow the surrounding API instead of splitting it into generic helpers
2. Keep pricing calculation focused
Primary goal: keep pricing code about pricing, not rendering.
Concrete next steps:
- keep
BookingPriceCalculatorServiceas the pricing boundary - continue removing display formatting from pricing code paths
- keep any remaining view-specific formatting in the presentation layer or a dedicated UI helper
- avoid introducing another service that only formats values already known to the view
Decision rule:
- if a value is only needed for display, prefer exposing the raw numeric/domain value and formatting it as close to the UI as possible
3. Leave the field-handler registry in place
Primary goal: avoid unnecessary churn in a class that is already a meaningful orchestration layer.
Concrete next steps:
- do not refactor
ParticipantFieldHandlerRegistryin this pass - revisit only if a later change can split ordering, mutability, and synchronization into clear collaborators without making the flow harder to trace
Decision rule:
- if a registry owns actual workflow behavior, treat it as a boundary rather than a smell
Follow-Up Queue
After the booking service pass, the next likely candidates are:
TravelDataService
This is the largest broad service still in the codebase. It likely needs a later pass if the application should become easier to follow end to end.
Likely directions:
- separate runtime travel loading from cache/snapshot maintenance if the public API still feels too wide
- keep the read path explicit and avoid hiding maintenance work behind one large method surface
BookingPriceCalculatorService
This service should stay focused on pricing logic, but it may still have room for further internal cleanup if more display or transport aggregation concerns surface.
Likely directions:
- keep calculation responsibilities together
- avoid dragging presentation behavior back into the calculator
- split only if a sub-boundary becomes obvious and reusable
ParticipantFieldHandlerRegistry
This stays on the list only as a future optional refactor, not as an immediate target.
Likely directions, only if justified later:
- isolate ordering/sorting logic if it becomes independently meaningful
- split synchronization code if a clearer DTO/form boundary emerges
- otherwise leave it as the central orchestration point for participant field processing
Progress Tracker
| Item | Status | Notes |
|---|---|---|
| Participant card DTO cleanup | Done | Card data now uses typed DTOs instead of nested array payloads |
| Room label formatting cleanup | Done | Pricing labels now have a dedicated presentation helper |
| Booking service split | In progress | Session lifecycle, baseline snapshot, return URL handling, room grouping, and participant count shaping moved out of BookingService; dummy prefill moved into ParticipantPrepopulationService |
| Pricing service review | Pending | Keep focused on calculation, not rendering |
| Travel data service review | Pending | Broad boundary, likely later pass |
| Participant field registry review | Deferred | Real orchestration boundary, intentionally left alone for now |
Acceptance Criteria
The next booking-service pass is only worth keeping if it:
- reduces the number of unrelated responsibilities in
BookingService - makes the booking flow easier to trace from controller to session/DTO state
- preserves existing booking behavior and test coverage
- does not replace one large service with several generic “manager” classes
Working Agreement
- Update this document as decisions are made.
- Record rejected simplification ideas here with a short reason.
- Keep the plan aligned with actual code, not with an abstract architecture ideal.
- If a future simplification does not clearly reduce cognitive load, do not add it.