Refactor database layer to eliminate type switching and simplify architecture

- Replace type switching with clean repository pattern using sqlc-generated code
- Move ContentRepository interface and domain models to db package
- Create separate SQLiteRepository and PostgreSQLRepository implementations
- Remove unnecessary RepositoryAdapter and ContentClient interface duplication
- Update all clients (HTTP, Mock) to implement db.ContentRepository directly
- Add context.Context parameters to all repository methods (Go best practice)
- Eliminate duplicate domain models and type conversions
- Remove type aliases - use db package types directly throughout codebase
- Update engine, content managers, and API handlers to use repositories directly

Benefits:
- Zero runtime type switching overhead
- Single source of truth for domain models
- Clean package boundaries and separation of concerns
- Standard Go interface patterns with context support
- Easier testing with mockable repository interface
- Maintainable: adding new database types requires no changes to existing code
This commit is contained in:
2025-10-08 19:34:21 +02:00
parent 38c2897ece
commit 01b921bfa3
16 changed files with 785 additions and 712 deletions

View File

@@ -1,21 +1,22 @@
package content
import (
"context"
"fmt"
"time"
"github.com/insertr/insertr/internal/engine"
"github.com/insertr/insertr/internal/db"
)
// MockClient implements ContentClient with mock data for development
type MockClient struct {
data map[string]engine.ContentItem
data map[string]db.ContentItem
}
// NewMockClient creates a new mock content client with sample data
func NewMockClient() *MockClient {
// Generate realistic mock content based on actual generated IDs
data := map[string]engine.ContentItem{
data := map[string]db.ContentItem{
// Navigation (index.html has collision suffix)
"navbar-logo-2b10ad": {
ID: "navbar-logo-2b10ad",
@@ -101,7 +102,7 @@ func NewMockClient() *MockClient {
}
// GetContent fetches a single content item by ID
func (m *MockClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) {
func (m *MockClient) GetContent(ctx context.Context, siteID, contentID string) (*db.ContentItem, error) {
if item, exists := m.data[contentID]; exists && item.SiteID == siteID {
return &item, nil
}
@@ -111,11 +112,11 @@ func (m *MockClient) GetContent(siteID, contentID string) (*engine.ContentItem,
}
// GetBulkContent fetches multiple content items by IDs
func (m *MockClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) {
result := make(map[string]engine.ContentItem)
func (m *MockClient) GetBulkContent(ctx context.Context, siteID string, contentIDs []string) (map[string]db.ContentItem, error) {
result := make(map[string]db.ContentItem)
for _, id := range contentIDs {
item, err := m.GetContent(siteID, id)
item, err := m.GetContent(ctx, siteID, id)
if err != nil {
return nil, err
}
@@ -128,8 +129,8 @@ func (m *MockClient) GetBulkContent(siteID string, contentIDs []string) (map[str
}
// GetAllContent fetches all content for a site
func (m *MockClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) {
result := make(map[string]engine.ContentItem)
func (m *MockClient) GetAllContent(ctx context.Context, siteID string) (map[string]db.ContentItem, error) {
result := make(map[string]db.ContentItem)
for _, item := range m.data {
if item.SiteID == siteID {
@@ -141,9 +142,9 @@ func (m *MockClient) GetAllContent(siteID string) (map[string]engine.ContentItem
}
// CreateContent creates a new mock content item
func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
func (m *MockClient) CreateContent(ctx context.Context, siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*db.ContentItem, error) {
// For mock client, just create and store the item
item := engine.ContentItem{
item := db.ContentItem{
ID: contentID,
SiteID: siteID,
HTMLContent: htmlContent,
@@ -159,30 +160,35 @@ func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTempl
}
// Collection method stubs - TODO: Implement these for mock testing
func (m *MockClient) GetCollection(siteID, collectionID string) (*engine.CollectionItem, error) {
func (m *MockClient) GetCollection(ctx context.Context, siteID, collectionID string) (*db.CollectionItem, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*engine.CollectionItem, error) {
func (m *MockClient) CreateCollection(ctx context.Context, siteID, collectionID, containerHTML, lastEditedBy string) (*db.CollectionItem, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) GetCollectionItems(siteID, collectionID string) ([]engine.CollectionItemWithTemplate, error) {
func (m *MockClient) GetCollectionItems(ctx context.Context, siteID, collectionID string) ([]db.CollectionItemWithTemplate, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*engine.CollectionTemplateItem, error) {
func (m *MockClient) CreateCollectionTemplate(ctx context.Context, siteID, collectionID, name, htmlTemplate string, isDefault bool) (*db.CollectionTemplateItem, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) GetCollectionTemplates(siteID, collectionID string) ([]engine.CollectionTemplateItem, error) {
func (m *MockClient) GetCollectionTemplates(ctx context.Context, siteID, collectionID string) ([]db.CollectionTemplateItem, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) CreateCollectionItem(siteID, collectionID, itemID string, templateID int, htmlContent string, position int, lastEditedBy string) (*engine.CollectionItemWithTemplate, error) {
func (m *MockClient) CreateCollectionItem(ctx context.Context, siteID, collectionID, itemID string, templateID int, htmlContent string, position int, lastEditedBy string) (*db.CollectionItemWithTemplate, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
func (m *MockClient) CreateCollectionItemAtomic(siteID, collectionID string, templateID int, lastEditedBy string) (*engine.CollectionItemWithTemplate, error) {
func (m *MockClient) CreateCollectionItemAtomic(ctx context.Context, siteID, collectionID string, templateID int, lastEditedBy string) (*db.CollectionItemWithTemplate, error) {
return nil, fmt.Errorf("collection operations not implemented in MockClient")
}
// WithTransaction executes a function within a transaction (not supported for mock client)
func (m *MockClient) WithTransaction(ctx context.Context, fn func(db.ContentRepository) error) error {
return fmt.Errorf("transactions not supported for mock client")
}