Skip to content

Include preparation duration in schedule responses to remove calendar N+1 fetches #330

Description

@jjoonleo

Problem

OnTime-front currently needs only each Schedule's total Preparation Duration for the calendar selected-day UI, but it has to fetch the full preparation list per schedule to calculate that duration.

That creates an N+1 pattern:

  1. Frontend loads schedules through GET /schedules?startDate=...&endDate=....
  2. Calendar extracts visible-day schedule ids.
  3. For each missing schedule id, frontend calls GET /schedules/{scheduleId}/preparations.
  4. Frontend sums preparation step durations through PreparationEntity.totalDuration.

PR DevKor-github/OnTime-front#558 reduced the UI impact with bounded frontend concurrency, but that is still an API workaround. The server already owns schedule preparation source resolution, so the better contract is for schedule responses to include the computed preparation duration directly.

Frontend evidence

Current frontend code paths:

  • lib/presentation/calendar/bloc/monthly_schedules_bloc.dart
    • MonthlySchedulesPreparationsPrefetchRequested receives schedule ids for the visible date.
    • It calls LoadPreparationByScheduleIdUseCase per missing schedule id.
    • It then calls GetPreparationByScheduleIdUseCase and stores preparation.totalDuration in preparationDurationByScheduleId.
  • lib/data/data_sources/preparation_remote_data_source.dart
    • getPreparationByScheduleId(scheduleId) performs GET /schedules/{scheduleId}/preparations.
  • lib/data/models/get_schedule_response_model.dart
    • Schedule responses currently include moveTime, scheduleSpareTime, preparationMode, preparationTemplateId, preparationTemplateName, preparationTemplateDeleted, and preparationFrozen, but no preparation duration field.

Related frontend mitigation:

Proposed API contract

Add a server-computed preparation-duration field to schedule response objects returned by schedule list/detail endpoints.

Preferred field shape:

{
  "scheduleId": "...",
  "moveTime": 20,
  "scheduleSpareTime": 10,
  "preparationDuration": 35
}

Field semantics:

  • preparationDuration is an integer minute count.
  • It is the sum of preparation step durations only.
  • It excludes moveTime.
  • It excludes scheduleSpareTime.
  • It should be available anywhere the frontend receives a schedule response for calendar/list/detail use.

Source resolution rules

The backend should compute the value from the same preparation source the schedule actually uses:

  • DEFAULT: sum the user's current default preparation steps for unstarted schedules.
  • TEMPLATE: sum the linked preparation template's steps for unstarted schedules, including resolvable deleted templates already linked to existing schedules.
  • CUSTOM: sum the schedule-specific preparation steps.
  • Started/frozen schedules: sum the frozen schedule-specific preparation snapshot, not the mutable default/template source.

These rules should stay aligned with the existing preparation template and schedule-start docs.

Frontend follow-up after backend ships

Once the field is available, OnTime-front should:

  • Parse the new field in GetScheduleResponseModel.
  • Carry it on ScheduleEntity or a schedule list view model as Duration(minutes: preparationDuration).
  • Use it in MonthlySchedulesBloc instead of calling GET /schedules/{scheduleId}/preparations just to populate calendar duration badges.
  • Keep GET /schedules/{scheduleId}/preparations for screens that need actual preparation steps, such as edit/detail/preparation flow.

Acceptance criteria

  • Schedule list responses include preparationDuration for each schedule.
  • Schedule detail response includes the same field or otherwise exposes the same schedule response shape.
  • The value is in minutes and excludes move time and schedule spare time.
  • Tests cover DEFAULT, TEMPLATE, CUSTOM, and started/frozen schedule cases.
  • Tests cover deleted-but-linked template resolution if schedule responses can still reference deleted templates.
  • API docs are updated with the new response field and source-resolution rules.
  • OnTime-front can remove the calendar selected-day preparation N+1 fetch path after adopting this field.

Non-goals

  • Do not include full preparation steps in schedule list responses.
  • Do not change moveTime or scheduleSpareTime semantics.
  • Do not remove GET /schedules/{scheduleId}/preparations; detailed preparation screens still need it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dataDatabase schema, migrations, and data integrityenhancementNew feature or requestpriority:P2Medium: important hardening or operational maturity work

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions