2026-02-23 21:17:00 -06:00

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 (IFragmentRepositoryFragmentService). 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.cs
  • Journal.Core/Services/CommandLogger.cs
  • Journal.Core/Services/IEntryFileService.cs
  • Journal.Core/Services/EntryFileService.cs
  • Journal.Core/Services/PythonSidecarClient.cs
  • Journal.Core/Repositories/IEntryFileRepository.cs
  • Journal.Core/Repositories/DiskEntryFileRepository.cs
  • Journal.Core/Dtos/CommandDtos.cs
  • Journal.Core/Dtos/DatabaseDtos.cs

Files Modified

  • Journal.Core/Entry.cs — slimmed to thin dispatcher
  • Journal.Core/Services/PythonSidecarAiService.cs — delegates to PythonSidecarClient
  • Journal.Core/Services/PythonSidecarSpeechService.cs — delegates to PythonSidecarClient
  • Journal.Core/Services/IJournalDatabaseService.cs — result records moved to Dtos
  • Journal.Core/Services/JournalDatabaseService.cs — added Dtos using
  • Journal.Core/ServiceCollectionExtensions.cs — registers new services and repository
  • Journal.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)