refactor(db): implement ADR002 DbContext composition pattern - #10
Conversation
- define composition approach using IEntityTypeConfiguration<T> - each service owns its DbSet declarations, shared configs in Shared/ - ApiGateway is migration owner for common tables (documents, processed_messages) - Worker relies on existing schema, does not create migrations for common tables
- add Microsoft.EntityFrameworkCore 8.0.x and Relational to Shared.csproj - create IEntityTypeConfiguration<T> for all 5 entities in Shared/Configurations/ - split IDocumentRepository into base interface + IOutboxRepository - create DocumentRepositoryBase<TContext> with common SQL operations
- create GatewayDbContext in ApiGateway with Documents, ProcessedMessages, OutboxMessages - create WorkerDbContext in Worker with Documents, ProcessedMessages, WorkflowCheckpoints, AgentDefinitions - each context applies only its required IEntityTypeConfiguration<T> - remove old shared AppDbContext
- create ApiGateway DocumentRepository with Outbox support (implements IOutboxRepository) - create Worker DocumentRepository with WorkflowCheckpoint queries - both inherit from DocumentRepositoryBase<TContext>
…ucture - update OutboxPublisher to use IOutboxRepository instead of IDocumentRepository - update DocumentService to accept IOutboxRepository for CreateDocumentAsync - update Program.cs to use GatewayDbContext with EnsureCreated - register both IDocumentRepository and IOutboxRepository in DI
- update Program.cs to use WorkerDbContext with EnsureCreated - update PostgreSqlCheckpointStore to use WorkerDbContext - register Worker DocumentRepository in DI
- update DocumentServiceTests to use IOutboxRepository mock - update OutboxPublisherTests to use IOutboxRepository mock - fix CreateDocumentAsync test to verify outbox repository call
- delete ApiGateway/Data/AppDbContext.cs (replaced by GatewayDbContext) - delete ApiGateway/Data/DocumentRepository.cs (replaced by Repositories/DocumentRepository) - delete Worker/Data/AppDbContext.cs (replaced by WorkerDbContext) - delete Worker/Data/DocumentRepository.cs (replaced by Repositories/DocumentRepository)
There was a problem hiding this comment.
Code Review
This pull request implements ADR-002, transitioning the system from DbContext inheritance to a composition-based approach. It introduces shared entity configurations and a base repository class in the Shared project, while creating service-specific contexts (GatewayDbContext and WorkerDbContext). The ApiGateway repository was split to separate outbox concerns, and various service registrations and tests were updated accordingly. Feedback highlights several critical issues in the shared repository base regarding the incorrect usage of ExecuteSqlRawAsync, where the CancellationToken is being treated as a SQL parameter, and the use of application-side timestamps instead of database functions. Additionally, the ApiGateway startup logic incorrectly uses EnsureCreated() instead of MigrateAsync(), contradicting the ADR. Finally, it is recommended to pin NuGet package versions in the Shared project to ensure build determinism.
…MigrateAsync, pin EF Core versions - replace ExecuteSqlRawAsync with ExecuteSqlInterpolatedAsync in DocumentRepositoryBase - use NOW() database function instead of DateTime.UtcNow application timestamps - fix CancellationToken not being passed as SQL parameter (ExecuteSqlInterpolatedAsync handles it correctly) - replace EnsureCreated() with MigrateAsync() in ApiGateway Program.cs (per ADR-002) - add IsRelational() guard to support in-memory database in tests - fix config key format in integration tests (colon instead of double underscore) - pin Microsoft.EntityFrameworkCore and Relational to 8.0.27 for deterministic builds
Summary
This PR refactors the database architecture from a shared
AppDbContextinheritance model to a composition pattern usingIEntityTypeConfiguration<T>. Each service now owns its ownDbContextwith only the tables it needs, eliminating code duplication while maintaining clean separation of concerns.What's Changed
Shared
Microsoft.EntityFrameworkCore8.0.x andMicrosoft.EntityFrameworkCore.RelationalpackagesShared/Configurations/:DocumentConfiguration,ProcessedMessageConfiguration,OutboxConfiguration,WorkflowCheckpointConfiguration,AgentDefinitionConfigurationIDocumentRepositoryinto base interface +IOutboxRepositoryfor Outbox-specific operationsDocumentRepositoryBase<TContext>inShared/Repositories/with common SQL operations (GetById, GetAll, TryUpdateStatus, UpdateText, MarkMessageProcessed, IsMessageProcessed)ApiGateway
GatewayDbContextwith DbSets for Documents, ProcessedMessages, OutboxMessagesDocumentRepositoryimplementing bothIDocumentRepositoryandIOutboxRepositoryOutboxPublisherto useIOutboxRepositoryinstead ofIDocumentRepositoryDocumentServiceto acceptIOutboxRepositoryforCreateDocumentAsyncProgram.csto useGatewayDbContextwithEnsureCreated()Worker
WorkerDbContextwith DbSets for Documents, ProcessedMessages, WorkflowCheckpoints, AgentDefinitionsDocumentRepositoryimplementingIDocumentRepositorywith WorkflowCheckpoint query methodsProgram.csto useWorkerDbContextwithEnsureCreated()PostgreSqlCheckpointStoreto useWorkerDbContextTests
DocumentServiceTeststo mockIOutboxRepositoryOutboxPublisherTeststo mockIOutboxRepositoryCleanup
AppDbContext.csandDocumentRepository.csfrom both ApiGateway and WorkerTesting
dotnet build— success, no warningsRelated
docs/adr/ADR002_DbContext_Composition.md