4.3 KiB
Backend Refactoring Summary
Problem
Entry.cs was a 550-line god class that contained command dispatching, business logic, HTML sanitization, logging infrastructure, and 13 private payload record types. The two Python sidecar services (PythonSidecarAiService and PythonSidecarSpeechService) duplicated ~80 lines of identical process/JSON plumbing. Payload DTOs were hidden as private records inside Entry.cs instead of being in the Dtos/ folder.
What Changed
1. Slimmed Entry.cs to a thin dispatcher (~330 lines)
Removed all business logic, HTML processing, logging implementation, and private record types. Entry now only parses the incoming JSON command, routes to the correct service, and returns the {ok, data} / {ok: false, error} envelope.
2. Extracted HtmlSanitizer (new file)
StripRichHtml and LooksLikeRichHtml moved from Entry.cs to Services/HtmlSanitizer.cs as a static utility class.
3. Extracted CommandLogger (new file)
LogStart, LogSuccess, LogFailure, EmitLog, ShouldLog, and LogLevelRank moved from Entry.cs to Services/CommandLogger.cs. Entry now receives this as a dependency.
4. Extracted IEntryFileService + EntryFileService (new files)
SaveEntry, LoadEntry, ListEntries, and ResolveTargetPath moved out of Entry.cs into a proper service with an interface. This follows the same pattern as IFragmentService / FragmentService.
5. Added IEntryFileRepository + DiskEntryFileRepository (new files)
EntryFileService now delegates all filesystem I/O (read, write, append, list, exists) to an IEntryFileRepository, keeping only business logic (HTML sanitization, parsing, merging) in the service. This mirrors the Fragment module's repository pattern (IFragmentRepository → FragmentService). An in-memory implementation can be swapped in for testing.
6. Extracted PythonSidecarClient (new file)
The duplicated SendAsync, LastJsonLine, and TryKill methods were extracted from both PythonSidecarAiService and PythonSidecarSpeechService into a shared Services/PythonSidecarClient.cs. Both services now delegate to it.
7. Moved payload records to Dtos/CommandDtos.cs (new file)
The 16 private payload/result records that were inside Entry.cs are now in Dtos/CommandDtos.cs. Records used through public interfaces (EntrySavePayload, EntryListItem, EntryLoadResult, EntrySaveResult) are public; the rest remain internal.
8. Moved database result records to Dtos/DatabaseDtos.cs (new file)
JournalDatabaseStatus and JournalDatabaseHydrationResult moved from IJournalDatabaseService.cs to Dtos/DatabaseDtos.cs for consistency with the other DTO files.
Files Created
Journal.Core/Services/HtmlSanitizer.csJournal.Core/Services/CommandLogger.csJournal.Core/Services/IEntryFileService.csJournal.Core/Services/EntryFileService.csJournal.Core/Services/PythonSidecarClient.csJournal.Core/Repositories/IEntryFileRepository.csJournal.Core/Repositories/DiskEntryFileRepository.csJournal.Core/Dtos/CommandDtos.csJournal.Core/Dtos/DatabaseDtos.cs
Files Modified
Journal.Core/Entry.cs— slimmed to thin dispatcherJournal.Core/Services/PythonSidecarAiService.cs— delegates to PythonSidecarClientJournal.Core/Services/PythonSidecarSpeechService.cs— delegates to PythonSidecarClientJournal.Core/Services/IJournalDatabaseService.cs— result records moved to DtosJournal.Core/Services/JournalDatabaseService.cs— added Dtos usingJournal.Core/ServiceCollectionExtensions.cs— registers new services and repositoryJournal.SmokeTests/Program.cs— updated NewEntry() with new dependencies
What Was NOT Changed
- Fragment module — already clean, untouched
- Config module — singleton reader, no changes needed
- Vault module — already well-separated (crypto/storage), untouched
- AI/Speech interfaces and disabled variants — untouched (only the sidecar implementations were refactored)
- Search module — stateless query service, no repository needed
- All test logic — no assertions or test behavior changed
Verification
- All 4 projects build successfully
- 70/70 smoke tests pass (5 Python sidecar tests fail only when Python is not installed on the machine, which is pre-existing)