# Journal Backend (.NET) A .NET 10 backend for the Project Journal app. Provides core journal functionality as a class library with a sidecar console app for Tauri integration and an optional HTTP API. ## Project Structure ``` backend/ ├── Journal.Core/ Class library — all business logic │ ├── Models/ │ │ ├── Fragment.cs Domain model (validated, owns Guid ID) │ │ └── Command.cs Stdin command shape for sidecar protocol │ ├── Dtos/ │ │ └── FragmentDtos.cs Immutable records for API boundary │ │ ├── FragmentDto Read (what goes out) │ │ ├── CreateFragmentDto Create (what comes in) │ │ └── UpdateFragmentDto Update (partial, all fields optional) │ ├── Repositories/ │ │ ├── IFragmentRepository.cs Interface (data access contract) │ │ └── InMemoryFragmentRepository.cs In-memory implementation │ ├── Services/ │ │ ├── IFragmentService.cs Interface (business logic contract) │ │ └── FragmentService.cs Validates, calls repo, maps to DTOs │ ├── Entry.cs Command dispatcher (stdin/stdout) │ ├── ServiceCollectionExtensions.cs DI registration helper │ └── Journal.Core.csproj │ ├── Journal.Sidecar/ Console app — Tauri sidecar bridge │ ├── App.cs Boots DI container, runs Entry.RunAsync() │ └── Journal.Sidecar.csproj References Journal.Core │ ├── Journal.Api/ Web API — HTTP endpoint wrapper (optional) │ ├── Program.cs │ └── Journal.Api.csproj │ └── README.md ``` ## Architecture Each layer only knows about the one below it: ``` Sidecar (stdin/stdout) ──┐ ├──► Services (business logic) ──► Repositories (data access) API (HTTP/JSON) ─────────┘ ``` - **Models** — Domain objects with validation. The source of truth. - **DTOs** — Immutable records that cross the API boundary. Internal logic never leaks out. - **Repositories** — Where data lives. Swap `InMemoryFragmentRepository` for SQLite/EF Core later without touching anything above. - **Services** — Business rules, validation, orchestration. Doesn't know about HTTP or stdin. - **Entry** — Transport adapter. Translates stdin/stdout JSON into service calls. ## Dependencies - **Journal.Core** — `Microsoft.Extensions.DependencyInjection.Abstractions` (interface-only, lightweight) - **Journal.Sidecar** — `Microsoft.Extensions.DependencyInjection` (full container implementation) + references `Journal.Core` ## Building ```powershell # Build everything (building Sidecar also rebuilds Core if changed) dotnet build backend\Journal.Sidecar\Journal.Sidecar.csproj # Build just the library dotnet build backend\Journal.Core\Journal.Core.csproj # Format code dotnet format backend\Journal.Core\Journal.Core.csproj ``` ## Publishing Publish as a single-file self-contained executable (no .NET runtime install needed): ```powershell dotnet publish backend\Journal.Sidecar\Journal.Sidecar.csproj -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true ``` Output: `backend\Journal.Sidecar\bin\Release\net10.0\win-x64\publish\Journal.Sidecar.exe` (~70MB, everything bundled) To exclude debug symbols: add `-p:DebugType=none` For a smaller build that requires .NET 10 on the target machine: ```powershell dotnet publish backend\Journal.Sidecar\Journal.Sidecar.csproj -c Release -r win-x64 -p:PublishSingleFile=true ``` ## Sidecar Protocol The sidecar communicates over stdin/stdout using JSON lines. One JSON line in, one JSON line out. ### Command Format ```json { "action": "fragments.create", "id": null, "type": null, "tag": null, "payload": { "type": "!TRIGGER", "description": "stomach drop" } } ``` **Fields:** - `action` — The operation to perform (e.g. `fragments.list`, `fragments.create`) - `id` — Target entity ID (for get/update/delete) - `type` / `tag` — Filter parameters (for search) - `payload` — Request body, deserialized into the appropriate DTO per action ### Available Actions | Action | Description | Requires | |--------|-------------|----------| | `fragments.list` | List all fragments | — | | `fragments.get` | Get fragment by ID | `id` | | `fragments.create` | Create a new fragment | `payload` (CreateFragmentDto) | | `fragments.update` | Update a fragment | `id`, `payload` (UpdateFragmentDto) | | `fragments.delete` | Delete a fragment | `id` | | `fragments.search` | Search by type/tag | `type` and/or `tag` | ### Response Format Success: ```json { "ok": true, "data": { "id": "abc-123", "type": "!TRIGGER", "description": "...", "time": "...", "tags": [] } } ``` Error: ```json { "ok": false, "error": "Description is required" } ``` ## Extending with New Modules The `Command` class is generic — new modules use the same dot-notation pattern: ``` vault.unlock → IVaultService (future) vault.lock entries.list → IEntryService (future) entries.create ai.analyze → IAiService (future) ai.chat search.query → ISearchService (future) ``` To add a module: 1. Create model, DTO, repository, and service in `Journal.Core/` 2. Register the new service in `ServiceCollectionExtensions.cs` 3. Inject the service into `Entry.cs` and add cases to the action switch 4. No changes needed to `Command.cs` or `App.cs` ## Dependency Injection `ServiceCollectionExtensions.cs` wires everything up. Any host (sidecar, API, tests) calls: ```csharp services.AddFragmentServices(); ``` This registers: - `IFragmentRepository` → `InMemoryFragmentRepository` (singleton — one shared store) - `IFragmentService` → `FragmentService` (transient — fresh instance per request)