package app import ( "tercul/internal/platform/cache" "tercul/internal/platform/config" "tercul/internal/platform/db" "tercul/internal/platform/log" repositories2 "tercul/internal/repositories" "tercul/linguistics" "tercul/services" "time" "github.com/hibiken/asynq" "github.com/weaviate/weaviate-go-client/v5/weaviate" "gorm.io/gorm" ) // ApplicationBuilder handles the initialization of all application components type ApplicationBuilder struct { dbConn *gorm.DB redisCache cache.Cache weaviateClient *weaviate.Client asynqClient *asynq.Client repositories *RepositoryContainer services *ServiceContainer linguistics *linguistics.LinguisticsFactory } // RepositoryContainer holds all repository instances type RepositoryContainer struct { WorkRepository repositories2.WorkRepository UserRepository repositories2.UserRepository AuthorRepository repositories2.AuthorRepository TranslationRepository repositories2.TranslationRepository CommentRepository repositories2.CommentRepository LikeRepository repositories2.LikeRepository BookmarkRepository repositories2.BookmarkRepository CollectionRepository repositories2.CollectionRepository TagRepository repositories2.TagRepository CategoryRepository repositories2.CategoryRepository CopyrightRepository repositories2.CopyrightRepository } // ServiceContainer holds all service instances type ServiceContainer struct { WorkService services.WorkService CopyrightService services.CopyrightService LocalizationService services.LocalizationService AuthService services.AuthService } // NewApplicationBuilder creates a new ApplicationBuilder func NewApplicationBuilder() *ApplicationBuilder { return &ApplicationBuilder{} } // BuildDatabase initializes the database connection func (b *ApplicationBuilder) BuildDatabase() error { log.LogInfo("Initializing database connection") dbConn, err := db.InitDB() if err != nil { log.LogFatal("Failed to initialize database - application cannot start without database connection", log.F("error", err), log.F("host", config.Cfg.DBHost), log.F("database", config.Cfg.DBName)) return err } b.dbConn = dbConn log.LogInfo("Database initialized successfully", log.F("host", config.Cfg.DBHost), log.F("database", config.Cfg.DBName)) return nil } // BuildCache initializes the Redis cache func (b *ApplicationBuilder) BuildCache() error { log.LogInfo("Initializing Redis cache") redisCache, err := cache.NewDefaultRedisCache() if err != nil { log.LogWarn("Failed to initialize Redis cache, continuing without caching - performance may be degraded", log.F("error", err), log.F("redisAddr", config.Cfg.RedisAddr)) } else { b.redisCache = redisCache log.LogInfo("Redis cache initialized successfully", log.F("redisAddr", config.Cfg.RedisAddr)) } return nil } // BuildWeaviate initializes the Weaviate client func (b *ApplicationBuilder) BuildWeaviate() error { log.LogInfo("Connecting to Weaviate", log.F("host", config.Cfg.WeaviateHost), log.F("scheme", config.Cfg.WeaviateScheme)) wClient, err := weaviate.NewClient(weaviate.Config{ Scheme: config.Cfg.WeaviateScheme, Host: config.Cfg.WeaviateHost, }) if err != nil { log.LogFatal("Failed to create Weaviate client - vector search capabilities will not be available", log.F("error", err), log.F("host", config.Cfg.WeaviateHost), log.F("scheme", config.Cfg.WeaviateScheme)) return err } b.weaviateClient = wClient log.LogInfo("Weaviate client initialized successfully") return nil } // BuildBackgroundJobs initializes Asynq for background job processing func (b *ApplicationBuilder) BuildBackgroundJobs() error { log.LogInfo("Setting up background job processing", log.F("redisAddr", config.Cfg.RedisAddr)) redisOpt := asynq.RedisClientOpt{ Addr: config.Cfg.RedisAddr, Password: config.Cfg.RedisPassword, DB: config.Cfg.RedisDB, } asynqClient := asynq.NewClient(redisOpt) b.asynqClient = asynqClient log.LogInfo("Background job client initialized successfully") return nil } // BuildRepositories initializes all repositories func (b *ApplicationBuilder) BuildRepositories() error { log.LogInfo("Initializing repositories") // Initialize base repositories baseWorkRepo := repositories2.NewWorkRepository(b.dbConn) userRepo := repositories2.NewUserRepository(b.dbConn) authorRepo := repositories2.NewAuthorRepository(b.dbConn) translationRepo := repositories2.NewTranslationRepository(b.dbConn) commentRepo := repositories2.NewCommentRepository(b.dbConn) likeRepo := repositories2.NewLikeRepository(b.dbConn) bookmarkRepo := repositories2.NewBookmarkRepository(b.dbConn) collectionRepo := repositories2.NewCollectionRepository(b.dbConn) tagRepo := repositories2.NewTagRepository(b.dbConn) categoryRepo := repositories2.NewCategoryRepository(b.dbConn) copyrightRepo := repositories2.NewCopyrightRepository(b.dbConn) // Wrap work repository with cache if available var workRepo repositories2.WorkRepository if b.redisCache != nil { workRepo = repositories2.NewCachedWorkRepository( baseWorkRepo, b.redisCache, nil, 30*time.Minute, // Cache work data for 30 minutes ) log.LogInfo("Using cached work repository") } else { workRepo = baseWorkRepo log.LogInfo("Using non-cached work repository") } b.repositories = &RepositoryContainer{ WorkRepository: workRepo, UserRepository: userRepo, AuthorRepository: authorRepo, TranslationRepository: translationRepo, CommentRepository: commentRepo, LikeRepository: likeRepo, BookmarkRepository: bookmarkRepo, CollectionRepository: collectionRepo, TagRepository: tagRepo, CategoryRepository: categoryRepo, CopyrightRepository: copyrightRepo, } log.LogInfo("Repositories initialized successfully") return nil } // BuildLinguistics initializes the linguistics components func (b *ApplicationBuilder) BuildLinguistics() error { log.LogInfo("Initializing linguistic analyzer") b.linguistics = linguistics.NewLinguisticsFactory( b.dbConn, b.redisCache, 4, // Default concurrency true, // Cache enabled ) log.LogInfo("Linguistics components initialized successfully") return nil } // BuildServices initializes all services func (b *ApplicationBuilder) BuildServices() error { log.LogInfo("Initializing service layer") workService := services.NewWorkService(b.repositories.WorkRepository, b.linguistics.GetAnalyzer()) copyrightService := services.NewCopyrightService(b.repositories.CopyrightRepository) localizationService := services.NewLocalizationService(b.repositories.TranslationRepository) authService := services.NewAuthService(b.repositories.UserRepository) b.services = &ServiceContainer{ WorkService: workService, CopyrightService: copyrightService, LocalizationService: localizationService, AuthService: authService, } log.LogInfo("Services initialized successfully") return nil } // Build initializes all components in the correct order func (b *ApplicationBuilder) Build() error { // Build components in dependency order if err := b.BuildDatabase(); err != nil { return err } if err := b.BuildCache(); err != nil { return err } if err := b.BuildWeaviate(); err != nil { return err } if err := b.BuildBackgroundJobs(); err != nil { return err } if err := b.BuildRepositories(); err != nil { return err } if err := b.BuildLinguistics(); err != nil { return err } if err := b.BuildServices(); err != nil { return err } log.LogInfo("Application builder completed successfully") return nil } // GetDatabase returns the database connection func (b *ApplicationBuilder) GetDatabase() *gorm.DB { return b.dbConn } // GetCache returns the cache instance func (b *ApplicationBuilder) GetCache() cache.Cache { return b.redisCache } // GetWeaviateClient returns the Weaviate client func (b *ApplicationBuilder) GetWeaviateClient() *weaviate.Client { return b.weaviateClient } // GetAsynqClient returns the Asynq client func (b *ApplicationBuilder) GetAsynqClient() *asynq.Client { return b.asynqClient } // GetRepositories returns the repository container func (b *ApplicationBuilder) GetRepositories() *RepositoryContainer { return b.repositories } // GetServices returns the service container func (b *ApplicationBuilder) GetServices() *ServiceContainer { return b.services } // GetLinguistics returns the linguistics factory func (b *ApplicationBuilder) GetLinguistics() *linguistics.LinguisticsFactory { return b.linguistics } // Close closes all resources func (b *ApplicationBuilder) Close() error { if b.asynqClient != nil { b.asynqClient.Close() } if b.dbConn != nil { sqlDB, err := b.dbConn.DB() if err == nil { sqlDB.Close() } } return nil }