refactor(dotnet): split collection read and write contracts
This commit is contained in:
@@ -5,9 +5,9 @@ namespace QuantEngine.Application.Services;
|
|||||||
|
|
||||||
public sealed class CollectionReadModelService : ICollectionReadModelService
|
public sealed class CollectionReadModelService : ICollectionReadModelService
|
||||||
{
|
{
|
||||||
private readonly ICollectionRepository _repository;
|
private readonly ICollectionReadRepository _repository;
|
||||||
|
|
||||||
public CollectionReadModelService(ICollectionRepository repository)
|
public CollectionReadModelService(ICollectionReadRepository repository)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace QuantEngine.Application.Services;
|
|||||||
public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
||||||
{
|
{
|
||||||
private readonly IKisApiClient _kisApiClient;
|
private readonly IKisApiClient _kisApiClient;
|
||||||
private readonly ICollectionRepository _repository;
|
private readonly ICollectionWriteRepository _writeRepository;
|
||||||
|
private readonly ICollectionReadRepository _readRepository;
|
||||||
private readonly PriceDataNormalizer _normalizer;
|
private readonly PriceDataNormalizer _normalizer;
|
||||||
private readonly SourcePriorityResolver _priorityResolver;
|
private readonly SourcePriorityResolver _priorityResolver;
|
||||||
private readonly ILogger<KisDataCollectionOrchestrator> _logger;
|
private readonly ILogger<KisDataCollectionOrchestrator> _logger;
|
||||||
@@ -21,14 +22,16 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
|
|
||||||
public KisDataCollectionOrchestrator(
|
public KisDataCollectionOrchestrator(
|
||||||
IKisApiClient kisApiClient,
|
IKisApiClient kisApiClient,
|
||||||
ICollectionRepository repository,
|
ICollectionWriteRepository repository,
|
||||||
|
ICollectionReadRepository readRepository,
|
||||||
PriceDataNormalizer normalizer,
|
PriceDataNormalizer normalizer,
|
||||||
SourcePriorityResolver priorityResolver,
|
SourcePriorityResolver priorityResolver,
|
||||||
ILogger<KisDataCollectionOrchestrator> logger,
|
ILogger<KisDataCollectionOrchestrator> logger,
|
||||||
IRuntimeAuditTrailService auditTrail)
|
IRuntimeAuditTrailService auditTrail)
|
||||||
{
|
{
|
||||||
_kisApiClient = kisApiClient;
|
_kisApiClient = kisApiClient;
|
||||||
_repository = repository;
|
_writeRepository = repository;
|
||||||
|
_readRepository = readRepository;
|
||||||
_normalizer = normalizer;
|
_normalizer = normalizer;
|
||||||
_priorityResolver = priorityResolver;
|
_priorityResolver = priorityResolver;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -66,7 +69,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
CollectionSnapshotRecord? cachedSnapshot = null;
|
CollectionSnapshotRecord? cachedSnapshot = null;
|
||||||
if (IsMarketClosed())
|
if (IsMarketClosed())
|
||||||
{
|
{
|
||||||
var latest = await _repository.GetLatestSnapshotsForTickerAsync(ticker, 1);
|
var latest = await _readRepository.GetLatestSnapshotsForTickerAsync(ticker, 1);
|
||||||
var todayPrefix = DateTime.UtcNow.AddHours(9).ToString("yyyy-MM-dd");
|
var todayPrefix = DateTime.UtcNow.AddHours(9).ToString("yyyy-MM-dd");
|
||||||
if (latest.Count > 0 && latest[0].CapturedAt.StartsWith(todayPrefix))
|
if (latest.Count > 0 && latest[0].CapturedAt.StartsWith(todayPrefix))
|
||||||
{
|
{
|
||||||
@@ -96,7 +99,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save to DB
|
// Save to DB
|
||||||
await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord(
|
await _writeRepository.SaveSnapshotAsync(new CollectionSnapshotRecord(
|
||||||
RunId: runId,
|
RunId: runId,
|
||||||
DatasetName: "data_feed",
|
DatasetName: "data_feed",
|
||||||
Ticker: ticker,
|
Ticker: ticker,
|
||||||
@@ -119,7 +122,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
_logger.LogWarning("Skipped invalid OHLCV bar for {Ticker}: constraints not satisfied", ticker);
|
_logger.LogWarning("Skipped invalid OHLCV bar for {Ticker}: constraints not satisfied", ticker);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await _repository.SavePriceHistoryDailyAsync(priceRecord);
|
await _writeRepository.SavePriceHistoryDailyAsync(priceRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +150,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
{ "error_kind", ex.GetType().Name }
|
{ "error_kind", ex.GetType().Name }
|
||||||
});
|
});
|
||||||
|
|
||||||
await _repository.SaveErrorAsync(new CollectionErrorRecord(
|
await _writeRepository.SaveErrorAsync(new CollectionErrorRecord(
|
||||||
RunId: runId,
|
RunId: runId,
|
||||||
SourceName: "kis_collector",
|
SourceName: "kis_collector",
|
||||||
ErrorKind: ex.GetType().Name,
|
ErrorKind: ex.GetType().Name,
|
||||||
@@ -166,7 +169,7 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
_auditTrail.Append("collection_audit", runId, new CollectionExecutionAudit(runId, result.Status, DateTimeOffset.Parse(startedAt), DateTimeOffset.Parse(finishedAt), result.SuccessCount, result.ErrorCount, "finished"));
|
_auditTrail.Append("collection_audit", runId, new CollectionExecutionAudit(runId, result.Status, DateTimeOffset.Parse(startedAt), DateTimeOffset.Parse(finishedAt), result.SuccessCount, result.ErrorCount, "finished"));
|
||||||
|
|
||||||
// Save run record
|
// Save run record
|
||||||
await _repository.SaveRunAsync(new CollectionRunRecord(
|
await _writeRepository.SaveRunAsync(new CollectionRunRecord(
|
||||||
RunId: runId,
|
RunId: runId,
|
||||||
Status: result.Status,
|
Status: result.Status,
|
||||||
StartedAt: startedAt,
|
StartedAt: startedAt,
|
||||||
@@ -364,4 +367,3 @@ public class KisDataCollectionOrchestrator : ICollectionOrchestrator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace QuantEngine.Core.Tests;
|
|||||||
public class KisDataCollectionOrchestratorTests
|
public class KisDataCollectionOrchestratorTests
|
||||||
{
|
{
|
||||||
private readonly Mock<IKisApiClient> _kisApiClientMock;
|
private readonly Mock<IKisApiClient> _kisApiClientMock;
|
||||||
private readonly Mock<ICollectionRepository> _repositoryMock;
|
private readonly Mock<ICollectionWriteRepository> _writeRepositoryMock;
|
||||||
|
private readonly Mock<ICollectionReadRepository> _readRepositoryMock;
|
||||||
private readonly Mock<ILogger<KisDataCollectionOrchestrator>> _loggerMock;
|
private readonly Mock<ILogger<KisDataCollectionOrchestrator>> _loggerMock;
|
||||||
private readonly Mock<IRuntimeAuditTrailService> _auditTrailMock;
|
private readonly Mock<IRuntimeAuditTrailService> _auditTrailMock;
|
||||||
private readonly PriceDataNormalizer _normalizer;
|
private readonly PriceDataNormalizer _normalizer;
|
||||||
@@ -23,15 +24,21 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
public KisDataCollectionOrchestratorTests()
|
public KisDataCollectionOrchestratorTests()
|
||||||
{
|
{
|
||||||
_kisApiClientMock = new Mock<IKisApiClient>();
|
_kisApiClientMock = new Mock<IKisApiClient>();
|
||||||
_repositoryMock = new Mock<ICollectionRepository>();
|
var repositoryMock = new Mock<ICollectionRepository>();
|
||||||
|
_writeRepositoryMock = repositoryMock.As<ICollectionWriteRepository>();
|
||||||
|
_readRepositoryMock = repositoryMock.As<ICollectionReadRepository>();
|
||||||
_loggerMock = new Mock<ILogger<KisDataCollectionOrchestrator>>();
|
_loggerMock = new Mock<ILogger<KisDataCollectionOrchestrator>>();
|
||||||
_auditTrailMock = new Mock<IRuntimeAuditTrailService>();
|
_auditTrailMock = new Mock<IRuntimeAuditTrailService>();
|
||||||
_priorityResolver = new SourcePriorityResolver();
|
_priorityResolver = new SourcePriorityResolver();
|
||||||
_normalizer = new PriceDataNormalizer(_priorityResolver);
|
_normalizer = new PriceDataNormalizer(_priorityResolver);
|
||||||
|
_kisApiClientMock
|
||||||
|
.Setup(k => k.GetDailyItemChartPriceAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), "D", It.IsAny<string>()))
|
||||||
|
.ReturnsAsync(new Dictionary<string, object>());
|
||||||
|
|
||||||
_orchestrator = new KisDataCollectionOrchestrator(
|
_orchestrator = new KisDataCollectionOrchestrator(
|
||||||
_kisApiClientMock.Object,
|
_kisApiClientMock.Object,
|
||||||
_repositoryMock.Object,
|
_writeRepositoryMock.Object,
|
||||||
|
_readRepositoryMock.Object,
|
||||||
_normalizer,
|
_normalizer,
|
||||||
_priorityResolver,
|
_priorityResolver,
|
||||||
_loggerMock.Object,
|
_loggerMock.Object,
|
||||||
@@ -56,19 +63,19 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
CapturedAt: $"{todayPrefix}T14:30:00"
|
CapturedAt: $"{todayPrefix}T14:30:00"
|
||||||
);
|
);
|
||||||
|
|
||||||
_repositoryMock
|
_readRepositoryMock
|
||||||
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
||||||
.ReturnsAsync(new List<CollectionSnapshotRecord> { cachedSnapshot });
|
.ReturnsAsync(new List<CollectionSnapshotRecord> { cachedSnapshot });
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
@@ -84,7 +91,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
"IsMarketClosed should return true and cached snapshot should be used, so KIS API should not be called"
|
"IsMarketClosed should return true and cached snapshot should be used, so KIS API should not be called"
|
||||||
);
|
);
|
||||||
|
|
||||||
_repositoryMock.Verify(
|
_writeRepositoryMock.Verify(
|
||||||
r => r.SaveSnapshotAsync(It.Is<CollectionSnapshotRecord>(s =>
|
r => r.SaveSnapshotAsync(It.Is<CollectionSnapshotRecord>(s =>
|
||||||
s.SourceName.Contains("(Cached)"))),
|
s.SourceName.Contains("(Cached)"))),
|
||||||
Times.Once,
|
Times.Once,
|
||||||
@@ -98,7 +105,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
var runId = "test-run-002";
|
var runId = "test-run-002";
|
||||||
var ticker = "005930";
|
var ticker = "005930";
|
||||||
var account = "mock";
|
var account = "mock";
|
||||||
_repositoryMock
|
_readRepositoryMock
|
||||||
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
||||||
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
||||||
|
|
||||||
@@ -115,15 +122,15 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
||||||
.ReturnsAsync(new Dictionary<string, object>());
|
.ReturnsAsync(new Dictionary<string, object>());
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
@@ -158,7 +165,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
CapturedAt: $"{priorDay}T14:30:00"
|
CapturedAt: $"{priorDay}T14:30:00"
|
||||||
);
|
);
|
||||||
|
|
||||||
_repositoryMock
|
_readRepositoryMock
|
||||||
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
||||||
.ReturnsAsync(new List<CollectionSnapshotRecord> { priorDaySnapshot });
|
.ReturnsAsync(new List<CollectionSnapshotRecord> { priorDaySnapshot });
|
||||||
|
|
||||||
@@ -174,15 +181,15 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
||||||
.ReturnsAsync(new Dictionary<string, object>());
|
.ReturnsAsync(new Dictionary<string, object>());
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
@@ -206,7 +213,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
var ticker = "005930";
|
var ticker = "005930";
|
||||||
var account = "mock";
|
var account = "mock";
|
||||||
|
|
||||||
_repositoryMock
|
_readRepositoryMock
|
||||||
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
.Setup(r => r.GetLatestSnapshotsForTickerAsync(ticker, It.IsAny<int>()))
|
||||||
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
||||||
|
|
||||||
@@ -222,15 +229,15 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
.Setup(k => k.GetDailyItemChartPriceAsync(ticker, It.IsAny<string>(), It.IsAny<string>(), "D", account))
|
||||||
.ReturnsAsync(new Dictionary<string, object>());
|
.ReturnsAsync(new Dictionary<string, object>());
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
@@ -261,7 +268,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
var account = "mock";
|
var account = "mock";
|
||||||
var tickers = new List<string> { "005930", "000660" };
|
var tickers = new List<string> { "005930", "000660" };
|
||||||
|
|
||||||
_repositoryMock
|
_readRepositoryMock
|
||||||
.Setup(r => r.GetLatestSnapshotsForTickerAsync(It.IsAny<string>(), It.IsAny<int>()))
|
.Setup(r => r.GetLatestSnapshotsForTickerAsync(It.IsAny<string>(), It.IsAny<int>()))
|
||||||
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
.ReturnsAsync(new List<CollectionSnapshotRecord>());
|
||||||
|
|
||||||
@@ -286,7 +293,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
.ReturnsAsync(new Dictionary<string, object>());
|
.ReturnsAsync(new Dictionary<string, object>());
|
||||||
|
|
||||||
var callCount = 0;
|
var callCount = 0;
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
.Setup(r => r.SaveSnapshotAsync(It.IsAny<CollectionSnapshotRecord>()))
|
||||||
.Returns((CollectionSnapshotRecord snapshot) =>
|
.Returns((CollectionSnapshotRecord snapshot) =>
|
||||||
{
|
{
|
||||||
@@ -296,15 +303,15 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
.Setup(r => r.SavePriceHistoryDailyAsync(It.IsAny<PriceHistoryDailyRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveErrorAsync(It.IsAny<CollectionErrorRecord>()))
|
.Setup(r => r.SaveErrorAsync(It.IsAny<CollectionErrorRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
_repositoryMock
|
_writeRepositoryMock
|
||||||
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
.Setup(r => r.SaveRunAsync(It.IsAny<CollectionRunRecord>()))
|
||||||
.Returns(Task.CompletedTask);
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
@@ -315,7 +322,7 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
Assert.Equal(1, result.SuccessCount);
|
Assert.Equal(1, result.SuccessCount);
|
||||||
Assert.Equal(1, result.ErrorCount);
|
Assert.Equal(1, result.ErrorCount);
|
||||||
|
|
||||||
_repositoryMock.Verify(
|
_writeRepositoryMock.Verify(
|
||||||
r => r.SaveErrorAsync(It.Is<CollectionErrorRecord>(e =>
|
r => r.SaveErrorAsync(It.Is<CollectionErrorRecord>(e =>
|
||||||
e.Ticker == "000660" && e.ErrorMessage == "Storage Error")),
|
e.Ticker == "000660" && e.ErrorMessage == "Storage Error")),
|
||||||
Times.Once
|
Times.Once
|
||||||
@@ -411,3 +418,5 @@ public class KisDataCollectionOrchestratorTests
|
|||||||
throw new InvalidOperationException("Repository root not found.");
|
throw new InvalidOperationException("Repository root not found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,25 @@ public interface IDataCollectionStore
|
|||||||
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
|
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ICollectionWriteRepository
|
||||||
|
{
|
||||||
|
Task SaveRunAsync(CollectionRunRecord run);
|
||||||
|
Task UpdateRunStatusAsync(string runId, string status, string? finishedAt = null, int? totalSnapshots = null, int? totalErrors = null);
|
||||||
|
Task SaveSnapshotAsync(CollectionSnapshotRecord snapshot);
|
||||||
|
Task SaveErrorAsync(CollectionErrorRecord error);
|
||||||
|
Task SavePriceHistoryDailyAsync(PriceHistoryDailyRecord record);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICollectionReadRepository
|
||||||
|
{
|
||||||
|
Task<List<CollectionRunRecord>> GetRecentRunsAsync(int limit = 20);
|
||||||
|
Task<List<CollectionSnapshotRecord>> GetRunSnapshotsAsync(string runId);
|
||||||
|
Task<List<CollectionErrorRecord>> GetRunErrorsAsync(string runId, int limit = 50);
|
||||||
|
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
|
||||||
|
Task<List<CollectionSnapshotRecord>> GetLatestSnapshotsForTickerAsync(string ticker, int limit = 10);
|
||||||
|
Task<List<PriceHistorySummaryRecord>> GetPriceHistorySummaryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Collection run record (maps Python CollectionRun).
|
/// Collection run record (maps Python CollectionRun).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using QuantEngine.Infrastructure.Data;
|
|||||||
|
|
||||||
namespace QuantEngine.Infrastructure.Repositories
|
namespace QuantEngine.Infrastructure.Repositories
|
||||||
{
|
{
|
||||||
public class CollectionRepository : ICollectionRepository
|
public class CollectionRepository : ICollectionRepository, ICollectionReadRepository, ICollectionWriteRepository
|
||||||
{
|
{
|
||||||
private readonly IDbConnectionFactory _connectionFactory;
|
private readonly IDbConnectionFactory _connectionFactory;
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,10 @@ try
|
|||||||
builder.Services.AddScoped<JsonSeedIngestionService>();
|
builder.Services.AddScoped<JsonSeedIngestionService>();
|
||||||
builder.Services.AddScoped<IPostgresqlHistorySnapshotReader, PostgresqlHistorySnapshotReader>();
|
builder.Services.AddScoped<IPostgresqlHistorySnapshotReader, PostgresqlHistorySnapshotReader>();
|
||||||
builder.Services.AddScoped<HistoryIngestionService>();
|
builder.Services.AddScoped<HistoryIngestionService>();
|
||||||
builder.Services.AddScoped<ICollectionRepository, CollectionRepository>();
|
builder.Services.AddScoped<CollectionRepository>();
|
||||||
|
builder.Services.AddScoped<ICollectionRepository>(sp => sp.GetRequiredService<CollectionRepository>());
|
||||||
|
builder.Services.AddScoped<ICollectionReadRepository>(sp => sp.GetRequiredService<CollectionRepository>());
|
||||||
|
builder.Services.AddScoped<ICollectionWriteRepository>(sp => sp.GetRequiredService<CollectionRepository>());
|
||||||
builder.Services.AddScoped<ICollectionReadModelService, CollectionReadModelService>();
|
builder.Services.AddScoped<ICollectionReadModelService, CollectionReadModelService>();
|
||||||
builder.Services.AddSingleton<IRuntimeAuditTrailService, RuntimeAuditTrailService>();
|
builder.Services.AddSingleton<IRuntimeAuditTrailService, RuntimeAuditTrailService>();
|
||||||
builder.Services.AddScoped<ITokenCache, PostgresTokenCache>();
|
builder.Services.AddScoped<ITokenCache, PostgresTokenCache>();
|
||||||
|
|||||||
Reference in New Issue
Block a user