refactor(dotnet): separate collection read model service
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 16s
Validators (Pushes and Pull Requests) / validate-core (push) Successful in 2m3s

This commit is contained in:
2026-07-13 00:44:43 +09:00
parent d610ecb57c
commit bccefed35e
6 changed files with 70 additions and 33 deletions
@@ -0,0 +1,13 @@
using QuantEngine.Core.Interfaces;
namespace QuantEngine.Application.Interfaces;
public interface ICollectionReadModelService
{
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
Task<List<CollectionRunRecord>> GetRecentRunsAsync(int limit = 20);
Task<List<CollectionSnapshotRecord>> GetRunSnapshotsAsync(string runId);
Task<List<CollectionErrorRecord>> GetRunErrorsAsync(string runId, int limit = 50);
Task<List<CollectionSnapshotRecord>> GetLatestSnapshotsForTickerAsync(string ticker, int limit = 10);
Task<List<PriceHistorySummaryRecord>> GetPriceHistorySummaryAsync();
}
@@ -0,0 +1,21 @@
using QuantEngine.Application.Interfaces;
using QuantEngine.Core.Interfaces;
namespace QuantEngine.Application.Services;
public sealed class CollectionReadModelService : ICollectionReadModelService
{
private readonly ICollectionRepository _repository;
public CollectionReadModelService(ICollectionRepository repository)
{
_repository = repository;
}
public Task<CollectionDashboardStateRecord> GetDashboardStateAsync() => _repository.GetDashboardStateAsync();
public Task<List<CollectionRunRecord>> GetRecentRunsAsync(int limit = 20) => _repository.GetRecentRunsAsync(limit);
public Task<List<CollectionSnapshotRecord>> GetRunSnapshotsAsync(string runId) => _repository.GetRunSnapshotsAsync(runId);
public Task<List<CollectionErrorRecord>> GetRunErrorsAsync(string runId, int limit = 50) => _repository.GetRunErrorsAsync(runId, limit);
public Task<List<CollectionSnapshotRecord>> GetLatestSnapshotsForTickerAsync(string ticker, int limit = 10) => _repository.GetLatestSnapshotsForTickerAsync(ticker, limit);
public Task<List<PriceHistorySummaryRecord>> GetPriceHistorySummaryAsync() => _repository.GetPriceHistorySummaryAsync();
}
@@ -7,11 +7,11 @@ namespace QuantEngine.Web.Endpoints;
public class GetCollectionStateEndpoint : EndpointWithoutRequest<CollectionDashboardStateRecord>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
public GetCollectionStateEndpoint(ICollectionRepository repo)
public GetCollectionStateEndpoint(ICollectionReadModelService readModelService)
{
_repo = repo;
_readModelService = readModelService;
}
public override void Configure()
@@ -27,7 +27,7 @@ public class GetCollectionStateEndpoint : EndpointWithoutRequest<CollectionDashb
{
try
{
var state = await _repo.GetDashboardStateAsync();
var state = await _readModelService.GetDashboardStateAsync();
await SendOkAsync(state, ct);
}
catch
@@ -50,11 +50,11 @@ public class GetRecentRunsResponse
public class GetRecentRunsEndpoint : Endpoint<GetRecentRunsRequest, GetRecentRunsResponse>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
public GetRecentRunsEndpoint(ICollectionRepository repo)
public GetRecentRunsEndpoint(ICollectionReadModelService readModelService)
{
_repo = repo;
_readModelService = readModelService;
}
public override void Configure()
@@ -70,7 +70,7 @@ public class GetRecentRunsEndpoint : Endpoint<GetRecentRunsRequest, GetRecentRun
{
try
{
var runs = await _repo.GetRecentRunsAsync(req.Limit);
var runs = await _readModelService.GetRecentRunsAsync(req.Limit);
await SendOkAsync(new GetRecentRunsResponse { Runs = runs, Count = runs.Count }, ct);
}
catch
@@ -94,11 +94,11 @@ public class GetRunSnapshotsResponse
public class GetRunSnapshotsEndpoint : Endpoint<GetRunSnapshotsRequest, GetRunSnapshotsResponse>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
public GetRunSnapshotsEndpoint(ICollectionRepository repo)
public GetRunSnapshotsEndpoint(ICollectionReadModelService readModelService)
{
_repo = repo;
_readModelService = readModelService;
}
public override void Configure()
@@ -115,7 +115,7 @@ public class GetRunSnapshotsEndpoint : Endpoint<GetRunSnapshotsRequest, GetRunSn
{
try
{
var snapshots = await _repo.GetRunSnapshotsAsync(req.RunId);
var snapshots = await _readModelService.GetRunSnapshotsAsync(req.RunId);
await SendOkAsync(new GetRunSnapshotsResponse { RunId = req.RunId, Snapshots = snapshots, Count = snapshots.Count }, ct);
}
catch
@@ -140,11 +140,11 @@ public class GetRunErrorsResponse
public class GetRunErrorsEndpoint : Endpoint<GetRunErrorsRequest, GetRunErrorsResponse>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
public GetRunErrorsEndpoint(ICollectionRepository repo)
public GetRunErrorsEndpoint(ICollectionReadModelService readModelService)
{
_repo = repo;
_readModelService = readModelService;
}
public override void Configure()
@@ -161,7 +161,7 @@ public class GetRunErrorsEndpoint : Endpoint<GetRunErrorsRequest, GetRunErrorsRe
{
try
{
var errors = await _repo.GetRunErrorsAsync(req.RunId, req.Limit);
var errors = await _readModelService.GetRunErrorsAsync(req.RunId, req.Limit);
await SendOkAsync(new GetRunErrorsResponse { RunId = req.RunId, Errors = errors, Count = errors.Count }, ct);
}
catch
@@ -186,11 +186,11 @@ public class GetLatestSnapshotsResponse
public class GetLatestSnapshotsEndpoint : Endpoint<GetLatestSnapshotsRequest, GetLatestSnapshotsResponse>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
public GetLatestSnapshotsEndpoint(ICollectionRepository repo)
public GetLatestSnapshotsEndpoint(ICollectionReadModelService readModelService)
{
_repo = repo;
_readModelService = readModelService;
}
public override void Configure()
@@ -206,7 +206,7 @@ public class GetLatestSnapshotsEndpoint : Endpoint<GetLatestSnapshotsRequest, Ge
{
try
{
var snapshots = await _repo.GetLatestSnapshotsForTickerAsync(req.Ticker, req.Limit);
var snapshots = await _readModelService.GetLatestSnapshotsForTickerAsync(req.Ticker, req.Limit);
await SendOkAsync(new GetLatestSnapshotsResponse { Ticker = req.Ticker, Snapshots = snapshots, Count = snapshots.Count }, ct);
}
catch
@@ -223,12 +223,12 @@ public class GetPriceHistorySummaryResponse
public class GetPriceHistorySummaryEndpoint : EndpointWithoutRequest<GetPriceHistorySummaryResponse>
{
private readonly ICollectionRepository _repo;
private readonly ICollectionReadModelService _readModelService;
private readonly ILogger<GetPriceHistorySummaryEndpoint> _logger;
public GetPriceHistorySummaryEndpoint(ICollectionRepository repo, ILogger<GetPriceHistorySummaryEndpoint> logger)
public GetPriceHistorySummaryEndpoint(ICollectionReadModelService readModelService, ILogger<GetPriceHistorySummaryEndpoint> logger)
{
_repo = repo;
_readModelService = readModelService;
_logger = logger;
}
@@ -245,7 +245,7 @@ public class GetPriceHistorySummaryEndpoint : EndpointWithoutRequest<GetPriceHis
{
try
{
var summary = await _repo.GetPriceHistorySummaryAsync();
var summary = await _readModelService.GetPriceHistorySummaryAsync();
await SendOkAsync(new GetPriceHistorySummaryResponse { Tickers = summary }, ct);
}
catch (Exception ex)
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using QuantEngine.Application.Interfaces;
using QuantEngine.Core.Interfaces;
using QuantEngine.Web.Services;
@@ -8,16 +9,16 @@ namespace QuantEngine.Web.Pages.Admin.Collection;
[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
public class IndexModel : PageModel
{
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionReadModelService _collectionReadModelService;
private readonly ILogger<IndexModel> _logger;
public List<CollectionRunRecord>? Runs { get; set; }
public List<PriceHistorySummaryRecord>? HistorySummary { get; set; }
public string? Message { get; set; }
public IndexModel(ICollectionRepository collectionRepository, ILogger<IndexModel> logger)
public IndexModel(ICollectionReadModelService collectionReadModelService, ILogger<IndexModel> logger)
{
_collectionRepository = collectionRepository;
_collectionReadModelService = collectionReadModelService;
_logger = logger;
}
@@ -25,8 +26,8 @@ public class IndexModel : PageModel
{
try
{
Runs = await _collectionRepository.GetRecentRunsAsync(limit: 20);
HistorySummary = await _collectionRepository.GetPriceHistorySummaryAsync();
Runs = await _collectionReadModelService.GetRecentRunsAsync(limit: 20);
HistorySummary = await _collectionReadModelService.GetPriceHistorySummaryAsync();
}
catch (Exception ex)
{
@@ -2,6 +2,7 @@ using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using QuantEngine.Application.Interfaces;
using QuantEngine.Core.Interfaces;
using QuantEngine.Web.Services;
@@ -11,7 +12,7 @@ namespace QuantEngine.Web.Pages.Admin.Dashboard;
public class IndexModel : PageModel
{
private readonly IWorkspaceRepository _workspaceRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionReadModelService _collectionReadModelService;
private readonly IWebHostEnvironment _environment;
private readonly ILogger<IndexModel> _logger;
@@ -95,12 +96,12 @@ public class IndexModel : PageModel
public IndexModel(
IWorkspaceRepository workspaceRepository,
ICollectionRepository collectionRepository,
ICollectionReadModelService collectionReadModelService,
IWebHostEnvironment environment,
ILogger<IndexModel> logger)
{
_workspaceRepository = workspaceRepository;
_collectionRepository = collectionRepository;
_collectionReadModelService = collectionReadModelService;
_environment = environment;
_logger = logger;
}
@@ -112,7 +113,7 @@ public class IndexModel : PageModel
var accounts = await _workspaceRepository.GetAccountsAsync();
ActiveUsersCount = accounts.Count(a => string.Equals(a.IsActive, "true", StringComparison.OrdinalIgnoreCase));
var dashboard = await _collectionRepository.GetDashboardStateAsync();
var dashboard = await _collectionReadModelService.GetDashboardStateAsync();
RecentRunsCount = string.IsNullOrEmpty(dashboard?.LastRunId) ? 0 : 1;
// These two queries only complete if the DB round-trip actually
+1
View File
@@ -108,6 +108,7 @@ try
builder.Services.AddScoped<IPostgresqlHistorySnapshotReader, PostgresqlHistorySnapshotReader>();
builder.Services.AddScoped<HistoryIngestionService>();
builder.Services.AddScoped<ICollectionRepository, CollectionRepository>();
builder.Services.AddScoped<ICollectionReadModelService, CollectionReadModelService>();
builder.Services.AddScoped<ITokenCache, PostgresTokenCache>();
builder.Services.AddHttpClient<IKisApiClient, KisApiClient>();