185 lines
9.0 KiB
Markdown
185 lines
9.0 KiB
Markdown
# Service Simplification Plan
|
|
|
|
Status: draft
|
|
Last updated: 2026-04-13
|
|
|
|
## 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:
|
|
|
|
- `BookingService` no longer owns session lifecycle, baseline snapshot handling, return URL management, or room grouping. That work now lives in `BookingSessionService` and `BookingRoomSelectionService`, which keeps the booking orchestration boundary narrower.
|
|
- `BookingService` still covers hydration, booking bootstrap, service preselection, and booking status rules.
|
|
- `BookingParticipantCountService` now handles only participant count shaping.
|
|
- `ParticipantPrepopulationService` now owns applicant prefill plus the create-mode dummy-data shortcut.
|
|
- `BookingEditParticipantContextFactory` now prepares the edit participant page context directly, replacing the older pass-through participant form service.
|
|
- `BookingPriceCalculatorService` is focused on pricing, but it still sits close to display-oriented behavior in adjacent code paths.
|
|
- `TravelDataService` remains 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 `BookingPriceCalculatorService` as 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 `ParticipantFieldHandlerRegistry` in 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
|
|
|
|
## Implementation Backlog
|
|
|
|
### 1. Keep the summary count contract explicit
|
|
|
|
Goal: make the booking summary read clearly without duplicating equivalent count fields.
|
|
|
|
Tasks:
|
|
- use a single summary-facing count field for the sidebar and step summary views
|
|
- keep the room-capacity-derived meaning explicit in the field name and docblock
|
|
- keep the participant-shaping count logic separate if the code still needs it internally
|
|
- remove template branching that compares two equivalent summary counts
|
|
|
|
Acceptance criteria:
|
|
- the summary template reads one count field, not two equivalent ones
|
|
- the field name makes the room-capacity meaning obvious to a new developer
|
|
- participant-shaping logic can still use its own internal count without leaking that distinction into the view layer
|
|
|
|
### 2. 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
|
|
|
|
### 3. Keep pricing calculation focused
|
|
|
|
Primary goal: keep pricing code about pricing, not rendering.
|
|
|
|
Concrete next steps:
|
|
- keep `BookingPriceCalculatorService` as 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
|
|
|
|
### 4. 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 `ParticipantFieldHandlerRegistry` in 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.
|