From ed5d6e35e03465ca2588b2ecc2b82fdcb44c7492 Mon Sep 17 00:00:00 2001 From: bergm Date: Wed, 1 Jul 2026 16:29:51 +0200 Subject: [PATCH] Phase 3b: Repository-Schicht im Core (Interfaces + Mongo-Impl + DI) - Interfaces: IAccountRepository, IMarketRepository, IPositionRepository (IPositionRepository kapselt das Collection-per-Account-Muster). - Mongo-Implementierungen bilden die bisherige Shim-Semantik 1:1 mit direkten MongoDB.Driver-Aufrufen nach (kein App-Shim-Bezug im Core). - AddCorePersistence()-Erweiterung + Registrierung in Program.cs. - Noch keine Call-Sites umgestellt; Build 0 Fehler. Co-Authored-By: Claude Opus 4.8 --- Program.cs | 2 + .../ServiceCollectionExtensions.cs | 21 +++++++++ .../Persistence/IAccountRepository.cs | 15 ++++++ .../Persistence/IMarketRepository.cs | 27 +++++++++++ .../Persistence/IPositionRepository.cs | 27 +++++++++++ .../Mongo/MongoAccountRepository.cs | 24 ++++++++++ .../Mongo/MongoMarketRepository.cs | 41 +++++++++++++++++ .../Mongo/MongoPositionRepository.cs | 46 +++++++++++++++++++ src/PolyTrader.Core/PolyTrader.Core.csproj | 1 + 9 files changed, 204 insertions(+) create mode 100644 src/PolyTrader.Core/DependencyInjection/ServiceCollectionExtensions.cs create mode 100644 src/PolyTrader.Core/Persistence/IAccountRepository.cs create mode 100644 src/PolyTrader.Core/Persistence/IMarketRepository.cs create mode 100644 src/PolyTrader.Core/Persistence/IPositionRepository.cs create mode 100644 src/PolyTrader.Core/Persistence/Mongo/MongoAccountRepository.cs create mode 100644 src/PolyTrader.Core/Persistence/Mongo/MongoMarketRepository.cs create mode 100644 src/PolyTrader.Core/Persistence/Mongo/MongoPositionRepository.cs diff --git a/Program.cs b/Program.cs index e61a02e..a7a886d 100644 --- a/Program.cs +++ b/Program.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using PolyTrader.Core.Configuration; +using PolyTrader.Core.DependencyInjection; using PolyTraderSharp.Models; using PolyTraderSharp.Services; @@ -33,6 +34,7 @@ internal static class Program var client = new MongoClient(dbOptions.ConnectionString); return client.GetDatabase(dbOptions.DatabaseName); }); + services.AddCorePersistence(); services.AddSingleton((IServiceProvider sp) => ServerSettings.Load("server_settings.xml")); services.AddSingleton(); services.AddSingleton(copySignalChannel.Writer); diff --git a/src/PolyTrader.Core/DependencyInjection/ServiceCollectionExtensions.cs b/src/PolyTrader.Core/DependencyInjection/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..5357ddf --- /dev/null +++ b/src/PolyTrader.Core/DependencyInjection/ServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using PolyTrader.Core.Persistence; +using PolyTrader.Core.Persistence.Mongo; + +namespace PolyTrader.Core.DependencyInjection +{ + public static class ServiceCollectionExtensions + { + /// + /// Registriert die Core-Persistenzschicht (Repository-Interfaces + Mongo-Implementierungen). + /// Setzt eine registrierte IMongoDatabase voraus. + /// + public static IServiceCollection AddCorePersistence(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + return services; + } + } +} diff --git a/src/PolyTrader.Core/Persistence/IAccountRepository.cs b/src/PolyTrader.Core/Persistence/IAccountRepository.cs new file mode 100644 index 0000000..194e32b --- /dev/null +++ b/src/PolyTrader.Core/Persistence/IAccountRepository.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence +{ + /// + /// Zugriff auf die eigenen Trading-Accounts (Collection "accounts"). + /// + public interface IAccountRepository + { + List GetAll(); + void Upsert(AccountState account); + void Delete(int accountId); + } +} diff --git a/src/PolyTrader.Core/Persistence/IMarketRepository.cs b/src/PolyTrader.Core/Persistence/IMarketRepository.cs new file mode 100644 index 0000000..45a89ea --- /dev/null +++ b/src/PolyTrader.Core/Persistence/IMarketRepository.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence +{ + /// + /// Zugriff auf die Markt-Metadaten (Collection "markets"). + /// + public interface IMarketRepository + { + MarketData? GetById(string id); + + /// Findet den Markt, dessen ClobTokenIds die angegebene TokenId enthalten. + MarketData? FindByTokenId(string tokenId); + + /// Alle nicht geschlossenen Märkte. + List GetActive(); + + void Upsert(MarketData market); + void Insert(MarketData market); + + /// Aktualisiert einen vorhandenen Markt ohne Upsert. + void Update(MarketData market); + + void EnsureIndexes(); + } +} diff --git a/src/PolyTrader.Core/Persistence/IPositionRepository.cs b/src/PolyTrader.Core/Persistence/IPositionRepository.cs new file mode 100644 index 0000000..10ce1a7 --- /dev/null +++ b/src/PolyTrader.Core/Persistence/IPositionRepository.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence +{ + /// + /// Zugriff auf die offenen Positionen je Account. Kapselt das aktuelle + /// Collection-per-Account-Muster ("open_positions_{id}" / "demo_positions_{id}"). + /// In der späteren MySQL-Migration wird daraus eine Tabelle mit account_id/is_demo. + /// + public interface IPositionRepository + { + List GetLive(int accountId); + List GetDemo(int accountId); + + Position? FindLive(int accountId, string tokenId); + Position? FindDemo(int accountId, string tokenId); + + void UpsertLive(int accountId, Position position); + void UpsertDemo(int accountId, Position position); + + void DeleteLive(int accountId, string tokenId); + void DeleteDemo(int accountId, string tokenId); + + void DropDemo(int accountId); + } +} diff --git a/src/PolyTrader.Core/Persistence/Mongo/MongoAccountRepository.cs b/src/PolyTrader.Core/Persistence/Mongo/MongoAccountRepository.cs new file mode 100644 index 0000000..5a66026 --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Mongo/MongoAccountRepository.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence.Mongo +{ + public class MongoAccountRepository : IAccountRepository + { + private readonly IMongoCollection _col; + + public MongoAccountRepository(IMongoDatabase db) + { + _col = db.GetCollection("accounts"); + } + + public List GetAll() => _col.Find(_ => true).ToList(); + + public void Upsert(AccountState account) => + _col.ReplaceOne(x => x.AccountId == account.AccountId, account, new ReplaceOptions { IsUpsert = true }); + + public void Delete(int accountId) => _col.DeleteMany(x => x.AccountId == accountId); + } +} diff --git a/src/PolyTrader.Core/Persistence/Mongo/MongoMarketRepository.cs b/src/PolyTrader.Core/Persistence/Mongo/MongoMarketRepository.cs new file mode 100644 index 0000000..5fa67e1 --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Mongo/MongoMarketRepository.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence.Mongo +{ + public class MongoMarketRepository : IMarketRepository + { + private readonly IMongoCollection _col; + + public MongoMarketRepository(IMongoDatabase db) + { + _col = db.GetCollection("markets"); + } + + public MarketData? GetById(string id) => _col.Find(x => x.Id == id).FirstOrDefault(); + + public MarketData? FindByTokenId(string tokenId) => + _col.Find(x => x.ClobTokenIds != null && x.ClobTokenIds.Contains(tokenId)).FirstOrDefault(); + + public List GetActive() => _col.Find(x => !x.Closed).ToList(); + + public void Upsert(MarketData market) => + _col.ReplaceOne(x => x.Id == market.Id, market, new ReplaceOptions { IsUpsert = true }); + + public void Insert(MarketData market) => _col.InsertOne(market); + + public void Update(MarketData market) => _col.ReplaceOne(x => x.Id == market.Id, market); + + public void EnsureIndexes() + { + try + { + var keys = Builders.IndexKeys.Ascending(x => x.Id); + _col.Indexes.CreateOne(new CreateIndexModel(keys)); + } + catch { } + } + } +} diff --git a/src/PolyTrader.Core/Persistence/Mongo/MongoPositionRepository.cs b/src/PolyTrader.Core/Persistence/Mongo/MongoPositionRepository.cs new file mode 100644 index 0000000..95ae92c --- /dev/null +++ b/src/PolyTrader.Core/Persistence/Mongo/MongoPositionRepository.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using MongoDB.Driver; +using PolyTraderSharp.Models; + +namespace PolyTrader.Core.Persistence.Mongo +{ + public class MongoPositionRepository : IPositionRepository + { + private readonly IMongoDatabase _db; + + public MongoPositionRepository(IMongoDatabase db) + { + _db = db; + } + + private IMongoCollection Live(int accountId) => + _db.GetCollection($"open_positions_{accountId}"); + + private IMongoCollection Demo(int accountId) => + _db.GetCollection($"demo_positions_{accountId}"); + + public List GetLive(int accountId) => Live(accountId).Find(_ => true).ToList(); + public List GetDemo(int accountId) => Demo(accountId).Find(_ => true).ToList(); + + public Position? FindLive(int accountId, string tokenId) => + Live(accountId).Find(x => x.TokenId == tokenId).FirstOrDefault(); + + public Position? FindDemo(int accountId, string tokenId) => + Demo(accountId).Find(x => x.TokenId == tokenId).FirstOrDefault(); + + public void UpsertLive(int accountId, Position position) => + Live(accountId).ReplaceOne(x => x.TokenId == position.TokenId, position, new ReplaceOptions { IsUpsert = true }); + + public void UpsertDemo(int accountId, Position position) => + Demo(accountId).ReplaceOne(x => x.TokenId == position.TokenId, position, new ReplaceOptions { IsUpsert = true }); + + public void DeleteLive(int accountId, string tokenId) => + Live(accountId).DeleteOne(x => x.TokenId == tokenId); + + public void DeleteDemo(int accountId, string tokenId) => + Demo(accountId).DeleteOne(x => x.TokenId == tokenId); + + public void DropDemo(int accountId) => _db.DropCollection($"demo_positions_{accountId}"); + } +} diff --git a/src/PolyTrader.Core/PolyTrader.Core.csproj b/src/PolyTrader.Core/PolyTrader.Core.csproj index 30d0206..e20313e 100644 --- a/src/PolyTrader.Core/PolyTrader.Core.csproj +++ b/src/PolyTrader.Core/PolyTrader.Core.csproj @@ -8,6 +8,7 @@ +