chore: update .gitignore and add developer documentation
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
## Build outputs
|
||||
**/bin/
|
||||
**/obj/
|
||||
**/publish/
|
||||
TestResults/
|
||||
|
||||
## Rider / JetBrains / VS Code / Visual Studio
|
||||
.idea/
|
||||
@@ -22,6 +24,8 @@ assets/
|
||||
|
||||
## Secrets & local environment files
|
||||
.env
|
||||
.env.*
|
||||
.env.bak*
|
||||
*.env.local
|
||||
appsettings.Development.json
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
Finlytic is a personal financial analysis/trading platform: a fleet of .NET 10 microservices communicating
|
||||
over MQTT, one HTTP/WebSocket gateway (`FinlyticBackend`), and a Flutter app (`FinlyticApp`) as the only
|
||||
client. There is no CI/multi-developer setup — this is a single-developer repo backed by one shared
|
||||
PostgreSQL instance ("OmniDB") and one MQTT broker, both external to this repo.
|
||||
|
||||
## Commands
|
||||
|
||||
### Backend (.NET)
|
||||
|
||||
```
|
||||
dotnet build Finlytic.sln # build everything
|
||||
dotnet build FinlyticEngine # build a single project
|
||||
dotnet test FinlyticEngine.Tests # run one test project
|
||||
dotnet test FinlyticEngine.Tests --filter "FullyQualifiedName~TradeLifecycleServiceTests"
|
||||
```
|
||||
|
||||
- Solution-wide `dotnet test` will also try to run `FinlyticEngine.Tests/_Verify/PostgresVerificationTests.cs`,
|
||||
which connects to a real, throwaway local Postgres container on `localhost:55987` and has no `[Skip]`/trait
|
||||
to exclude it. It fails outside that one-off setup — run test projects individually, or filter it out with
|
||||
`--filter "FullyQualifiedName!~_Verify"`, rather than running the whole solution's tests.
|
||||
- `FinlyticBot.Tests` currently has no test files (scaffolding only).
|
||||
- EF Core migrations are created via the CLI only (never hand-written) from inside the owning service project,
|
||||
e.g.: `dotnet ef migrations add <Name> --project FinlyticAssets`.
|
||||
|
||||
### Frontend (Flutter, `FinlyticApp/`)
|
||||
|
||||
```
|
||||
flutter pub get
|
||||
flutter run # or -d chrome for the web dashboard
|
||||
flutter test
|
||||
flutter analyze
|
||||
```
|
||||
|
||||
### Docker Compose (`compose.yaml`)
|
||||
|
||||
The compose file only defines this repo's own services — Postgres and the MQTT broker are external
|
||||
infrastructure (see the comment block at the top of `compose.yaml` for required env vars: `DB_HOST`,
|
||||
`DB_PORT`, `DB_PASSWORD`, `MQTT_HOST`, `MQTT_PORT`, plus per-service secrets like `JWT_SECRET_KEY`,
|
||||
`ADMIN_DEFAULT_PASSWORD`, `ALPACA_KEY_ID`/`ALPACA_SECRET_KEY`). Set these in a local `.env` file.
|
||||
|
||||
`FinlyticNews` and `FinlyticFundamentals` build from a shared Playwright base image that must exist first:
|
||||
|
||||
```
|
||||
docker compose --profile build-base build finlytic-playwright-base
|
||||
docker compose build
|
||||
```
|
||||
|
||||
Keep the `PLAYWRIGHT_VERSION` build arg in `compose.yaml` in sync with the `Microsoft.Playwright` NuGet
|
||||
package version in `FinlyticCore/FinlyticCore.csproj`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Service topology
|
||||
|
||||
Nine .NET projects share one solution (`Finlytic.sln`): `FinlyticCore` (shared library, no entry point) plus
|
||||
eight deployable services — `FinlyticAssets`, `FinlyticNews`, `FinlyticFundamentals`, `FinlyticSentiment`,
|
||||
`FinlyticTechnicals`, `FinlyticEngine`, `FinlyticSimulation`, `FinlyticBot`, and `FinlyticBackend`. Each
|
||||
non-Core service is its own Docker image, has its own PostgreSQL database (`finlytic_assets`,
|
||||
`finlytic_news`, ... one per service — never shared tables across services), and runs its own EF Core
|
||||
migrations independently at startup via `DbContext.MigrateWithBootstrapAsync` (`FinlyticCore/Database/DatabaseBootstrapper.cs`).
|
||||
|
||||
- **`FinlyticBackend`** is the *only* service allowed to host Kestrel/HTTP/WebSocket endpoints. It is a thin
|
||||
aggregation gateway for `FinlyticApp`: its controllers and SignalR hubs (`Hubs/`) largely proxy to the
|
||||
backing microservices over MQTT RPC and rebroadcast their MQTT events to connected clients via SignalR
|
||||
(`BackendMqttBridge`). All other services are `Microsoft.NET.Sdk.Worker` background services with no HTTP
|
||||
surface at all — this is an enforced rule (`Rules.md` §5), not a stylistic default. See the
|
||||
`compose.yaml` comment block explaining why only `finlyticbackend` has a Docker `HEALTHCHECK`.
|
||||
- Every other service follows the same internal shape: `Database/` (DbContext + migrations),
|
||||
`Entities/` (EF entities), `Services/` (business logic + the service's MQTT client), `Util/`, and a
|
||||
`Program.cs` that wires DI, registers the service's MQTT client as a hosted service, and migrates the DB
|
||||
at startup.
|
||||
|
||||
### Inter-service communication (MQTT-only)
|
||||
|
||||
All cross-service calls go over MQTT — never direct HTTP between backend services. `FinlyticCore/Util/MqttTopics.cs`
|
||||
is the single source of truth for every topic and RPC channel name; add new channels there rather than
|
||||
inlining topic strings at call sites. Two patterns are used:
|
||||
|
||||
1. **RPC** (`services/request/{channel}/{correlationId}` → `services/response/{channel}/{correlationId}`),
|
||||
implemented by `FinlyticCore/Util/ManagedMqttClient.cs` (`SendRpcRequestAsync` / `SubscribeRpcAsync`).
|
||||
Used for synchronous-style query/command calls, e.g. `FinlyticBackend` asking `FinlyticAssets` to resolve
|
||||
an ISIN, or `FinlyticEngine` asking `FinlyticSentiment` for a sentiment summary.
|
||||
2. **Pub/sub event streams** (e.g. `finlytic/news/stream/{isin}`, `finlytic/engine/proposals/created`,
|
||||
`finlytic/bot/trades/stream`, `finlytic/logs/{service}`) for fire-and-forget notifications. `FinlyticBackend`'s
|
||||
`BackendMqttBridge` subscribes to the wildcard form of most of these (`*Wildcard` constants) purely to
|
||||
relay them onward to SignalR clients.
|
||||
|
||||
Every service also implements a shared `health_Ping` RPC channel and exposes runtime-configurable settings
|
||||
via a `{service}_settings_GetAll` / `{service}_settings_Update` channel pair (backed by `ISettingsService`
|
||||
and `IOptionsMonitor`-style dynamic config — see `FinlyticCore/Services/Settings`) — changing a setting does
|
||||
not require a service restart, and channel-level logging enable/disable follows the same mechanism (see
|
||||
`IFinlyticLogger<T>` / `FinlyticLogBroadcaster` in `FinlyticCore/Services/FinlyticLogger`).
|
||||
|
||||
### Domain flow (roughly upstream → downstream)
|
||||
|
||||
`FinlyticAssets` (asset/ISIN resolution, Trade Republic price feed) and `FinlyticNews`/`FinlyticFundamentals`
|
||||
(scraped/ingested data, using Playwright) feed `FinlyticSentiment` (FinBERT analysis) and `FinlyticTechnicals`
|
||||
(indicators/strategy setups), which feed `FinlyticEngine` (trade proposal generation, AI-assisted validation
|
||||
via an n8n webhook, trade lifecycle management) and `FinlyticSimulation` (backtesting/strategy reliability
|
||||
scoring). `FinlyticBot` executes accepted proposals as paper trades (via Alpaca or a synthetic ledger).
|
||||
`FinlyticBackend` sits above all of them as the gateway `FinlyticApp` talks to.
|
||||
|
||||
### Flutter app (`FinlyticApp/`)
|
||||
|
||||
Feature-folder structure under `lib/features/*` (auth, dashboard, discovery, trades, bot, simulation, news,
|
||||
calendar, favorites, search, asset_detail, proposals, admin), with `lib/core/` for cross-cutting network
|
||||
(Dio + interceptors), theme, and services, and `lib/shared/` for shared widgets. State management is
|
||||
`flutter_bloc`. Every outgoing request must carry the JWT via a central Dio interceptor, and a 401/403 must
|
||||
trigger an immediate client-side logout — this is an enforced rule (`Rules.md` §8), not optional handling.
|
||||
|
||||
## Project-wide rules (`Rules.md`)
|
||||
|
||||
`Rules.md` is a binding rules document for this repo, not a style guide — treat every rule as an
|
||||
architectural constraint to actively check against, not background reading. Full text is in `Rules.md`;
|
||||
key points, since they shape most non-trivial changes:
|
||||
|
||||
- Every service interface + implementation lives in one file, named after the implementation class.
|
||||
- Every method (any visibility) needs XML doc comments in English; implementations of interface/base
|
||||
methods use `/// <inheritdoc />` (custom helper methods still need their own explicit docs).
|
||||
- Strongly-typed data classes only for state passing, API payloads, and MQTT messages — no
|
||||
`Dictionary<string, object>`, `dynamic`, or raw `JObject`/`JsonDocument` in internal logic. Shared
|
||||
DTOs/entities/enums used by more than one service belong in `FinlyticCore`; service-specific models
|
||||
(e.g. raw third-party API shapes) stay in that service.
|
||||
- No mock/demo/fallback data anywhere, backend or frontend. Empty results are either a real empty set or an
|
||||
explicit exception — the Flutter UI must show an explicit empty state, never placeholder content.
|
||||
- Async all the way (no `.Result`/`.Wait()`/`.GetAwaiter().GetResult()`); async DB/MQTT/network methods take
|
||||
a trailing `CancellationToken` and pass it through.
|
||||
- Every `FinlyticBackend` HTTP/WebSocket endpoint requires `[Authorize]` unless explicitly `[AllowAnonymous]`
|
||||
(only `/api/v1/auth/login`-style endpoints and the `/health` check are exempt).
|
||||
- No hardcoded secrets; bind config via `IOptions<T>`/`IOptionsMonitor<T>`, and `FinlyticBackend` fails fast
|
||||
at startup if `JWT_SECRET_KEY`/`ADMIN_DEFAULT_PASSWORD` are missing or weak (see `Program.cs`).
|
||||
Reference in New Issue
Block a user