#62 — Tech Debt: TodoListBroadcastDto Construction Is Manually Repeated at 8 Call Sites #62
Labels
No labels
priority/could
priority/must
priority/should
priority/wont
status/blocked
status/claimed
status/done-migrated
type/bug
type/feature
type/infra
type/tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/todo#62
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tech Debt:
TodoListBroadcastDtoConstruction Is Manually Repeated at 8 Call SitesReported by: Backend Engineer, spotted during
#23's code review, 2026-07-21Symptom
Every handler that mutates a
TodoListEntityand broadcasts the change constructsnew TodoListBroadcastDto(dto.Id, dto.Title, dto.Description, dto.IsArchived, dto.Color, dto.Icon)by hand from a freshly-fetched
TodoListDto— currently 8 call sites acrossCreateTodoListCommandHandler,RenameTodoListCommandHandler,ArchiveTodoListCommandHandler,UnarchiveTodoListCommandHandler,TransferTodoListOwnershipCommandHandler,AcceptListInvitationCommandHandler,RemoveTodoListMemberCommandHandler, andSetTodoListAppearanceCommandHandler.This has already caused two real omissions found only by code review, not the compiler (both
trailing constructor params have defaults, so a forgotten field compiles silently):
RemoveTodoListMemberCommandHandlershipped without forwardingIsArchivedat all (pre-#23),silently broadcasting
isArchived: falsefor archived lists whenever a member was removed fromone.
Color/Iconduring#23's first pass — caught and fixed inthat cycle's code review (see
docs/features/done/23_list_color_icon.md), but only because areviewer happened to check every call site by hand.
Expected behaviour
A single
dto.ToBroadcastDto()(or equivalent Mapperly-based) mapping fromTodoListDtotoTodoListBroadcastDto, called at all 8 sites, so adding a new field toTodoListDtoonly requiresupdating one mapping definition — a compile error (missing mapped member) rather than a silent
default if a field is forgotten.
Acceptance criteria
TodoListDtotoTodoListBroadcastDto(Mapperly[Mapper]staticmethod, matching the existing
TodoListMappingsprecedent, or a plain extension method) replacesall 8 hand-written positional/named
new TodoListBroadcastDto(...)callsTodoListDtowithout updating the mapping fails the build (Mapperly'sRequiredMappingStrategy.Source, or equivalent), not just silently defaults on the wireImplementation notes
Added
TodoListMappings.ToBroadcastDto(this TodoListDto dto)— a Mapperly[Mapper]extensionmethod with
RequiredMappingStrategy.Source(fails the build if a newTodoListDtofield isn'teither mapped or explicitly ignored) and
[MapperIgnoreSource(nameof(TodoListDto.CurrentUserRole))]for the one field that has no
TodoListBroadcastDtocounterpart. All 8 call sites(
CreateTodoListCommandHandler,RenameTodoListCommandHandler,ArchiveTodoListCommandHandler,UnarchiveTodoListCommandHandler,TransferTodoListOwnershipCommandHandler,AcceptListInvitationCommandHandler,RemoveTodoListMemberCommandHandler,SetTodoListAppearanceCommandHandler) now calldto.ToBroadcastDto().ArchiveTodoListCommandHandler/UnarchiveTodoListCommandHandler/CreateTodoListCommandHandlerpreviously passed a hardcoded
true/false/falseforIsArchivedinstead ofdto.IsArchived—confirmed behaviorally identical before replacing:
GetTodoListQueryHandleropens a freshDbContextand queries the database directly (not the EF change tracker), so by the timedtoisfetched after the preceding
ExecuteUpdateAsync/SaveChangesAsync,dto.IsArchivedalreadyreflects the just-written value.
No existing test references
TodoListBroadcastDtodirectly (confirmed via grep acrossCqsTodo.Tests), and neitherTodoListMappingsnorTodoMappingshave direct unit tests for theirgenerated
[Mapper]methods — correctness is enforced by the compiler viaRequiredMappingStrategy, matching the existing precedent.dotnet buildsucceeds with 0 errors.Notes for whoever picks this up
Out of scope for this story: the parallel observation (same review) that
TodoListColorandLabelColorduplicate the same 10-value palette across two C# enums and two TS unions. That's asmaller, lower-confidence finding (the two concepts — list accent color and label color — were
deliberately kept independent so they can diverge later, see
docs/features/done/23_list_color_icon_design.md) and not backed by a repeated-bug track recordthe way this mapping gap is — don't fold it into this story without separately re-justifying it.
Agents involved
Blockers
None.