package app import ( "tercul/internal/app/auth" "tercul/internal/app/copyright" "tercul/internal/app/localization" "tercul/internal/app/analytics" "tercul/internal/app/monetization" app_search "tercul/internal/app/search" "tercul/internal/app/work" "tercul/internal/data/sql" "tercul/internal/platform/cache" "tercul/internal/platform/config" "tercul/internal/platform/db" "tercul/internal/platform/log" auth_platform "tercul/internal/platform/auth" platform_search "tercul/internal/platform/search" "tercul/internal/jobs/linguistics" "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 weaviateWrapper platform_search.WeaviateWrapper asynqClient *asynq.Client App *Application linguistics *linguistics.LinguisticsFactory } // 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", log.F("error", err)) return err } b.dbConn = dbConn log.LogInfo("Database initialized successfully") 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", log.F("error", err)) } else { b.redisCache = redisCache log.LogInfo("Redis cache initialized successfully") } return nil } // BuildWeaviate initializes the Weaviate client func (b *ApplicationBuilder) BuildWeaviate() error { log.LogInfo("Connecting to Weaviate", log.F("host", config.Cfg.WeaviateHost)) wClient, err := weaviate.NewClient(weaviate.Config{ Scheme: config.Cfg.WeaviateScheme, Host: config.Cfg.WeaviateHost, }) if err != nil { log.LogFatal("Failed to create Weaviate client", log.F("error", err)) return err } b.weaviateWrapper = platform_search.NewWeaviateWrapper(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") redisOpt := asynq.RedisClientOpt{ Addr: config.Cfg.RedisAddr, Password: config.Cfg.RedisPassword, DB: config.Cfg.RedisDB, } b.asynqClient = asynq.NewClient(redisOpt) log.LogInfo("Background job client initialized successfully") return nil } // BuildLinguistics initializes the linguistics components func (b *ApplicationBuilder) BuildLinguistics() error { log.LogInfo("Initializing linguistic analyzer") // Create sentiment provider var sentimentProvider linguistics.SentimentProvider sentimentProvider, err := linguistics.NewGoVADERSentimentProvider() if err != nil { log.LogWarn("Failed to initialize GoVADER sentiment provider, using rule-based fallback", log.F("error", err)) sentimentProvider = &linguistics.RuleBasedSentimentProvider{} } // Create linguistics factory and pass in the sentiment provider b.linguistics = linguistics.NewLinguisticsFactory(b.dbConn, b.redisCache, 4, true, sentimentProvider) log.LogInfo("Linguistics components initialized successfully") return nil } // BuildApplication initializes all application services func (b *ApplicationBuilder) BuildApplication() error { log.LogInfo("Initializing application layer") // Initialize repositories // Note: This is a simplified wiring. In a real app, you might have more complex dependencies. workRepo := sql.NewWorkRepository(b.dbConn) userRepo := sql.NewUserRepository(b.dbConn) // I need to add all the other repos here. For now, I'll just add the ones I need for the services. translationRepo := sql.NewTranslationRepository(b.dbConn) copyrightRepo := sql.NewCopyrightRepository(b.dbConn) authorRepo := sql.NewAuthorRepository(b.dbConn) tagRepo := sql.NewTagRepository(b.dbConn) categoryRepo := sql.NewCategoryRepository(b.dbConn) // Initialize application services workCommands := work.NewWorkCommands(workRepo, b.linguistics.GetAnalyzer()) workQueries := work.NewWorkQueries(workRepo) jwtManager := auth_platform.NewJWTManager() authCommands := auth.NewAuthCommands(userRepo, jwtManager) authQueries := auth.NewAuthQueries(userRepo, jwtManager) copyrightCommands := copyright.NewCopyrightCommands(copyrightRepo) bookRepo := sql.NewBookRepository(b.dbConn) publisherRepo := sql.NewPublisherRepository(b.dbConn) sourceRepo := sql.NewSourceRepository(b.dbConn) copyrightQueries := copyright.NewCopyrightQueries(copyrightRepo, workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo) localizationService := localization.NewService(translationRepo) searchService := app_search.NewIndexService(localizationService, b.weaviateWrapper) analyticsRepo := sql.NewAnalyticsRepository(b.dbConn) analysisRepo := linguistics.NewGORMAnalysisRepository(b.dbConn) analyticsService := analytics.NewService(analyticsRepo, analysisRepo, translationRepo, workRepo, b.linguistics.GetSentimentProvider()) b.App = &Application{ AnalyticsService: analyticsService, WorkCommands: workCommands, WorkQueries: workQueries, AuthCommands: authCommands, AuthQueries: authQueries, CopyrightCommands: copyrightCommands, CopyrightQueries: copyrightQueries, Localization: localizationService, Search: searchService, AuthorRepo: authorRepo, UserRepo: userRepo, TagRepo: tagRepo, CategoryRepo: categoryRepo, BookRepo: sql.NewBookRepository(b.dbConn), PublisherRepo: sql.NewPublisherRepository(b.dbConn), SourceRepo: sql.NewSourceRepository(b.dbConn), TranslationRepo: translationRepo, MonetizationQueries: monetization.NewMonetizationQueries(sql.NewMonetizationRepository(b.dbConn), workRepo, authorRepo, bookRepo, publisherRepo, sourceRepo), CopyrightRepo: copyrightRepo, MonetizationRepo: sql.NewMonetizationRepository(b.dbConn), CommentRepo: sql.NewCommentRepository(b.dbConn), LikeRepo: sql.NewLikeRepository(b.dbConn), BookmarkRepo: sql.NewBookmarkRepository(b.dbConn), CollectionRepo: sql.NewCollectionRepository(b.dbConn), } log.LogInfo("Application layer initialized successfully") return nil } // Build initializes all components in the correct order func (b *ApplicationBuilder) Build() error { 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.BuildLinguistics(); err != nil { return err } if err := b.BuildApplication(); err != nil { return err } log.LogInfo("Application builder completed successfully") return nil } // GetApplication returns the application container func (b *ApplicationBuilder) GetApplication() *Application { return b.App } // GetDB returns the database connection func (b *ApplicationBuilder) GetDB() *gorm.DB { return b.dbConn } // GetAsynq returns the Asynq client func (b *ApplicationBuilder) GetAsynq() *asynq.Client { return b.asynqClient } // GetLinguisticsFactory returns the linguistics factory func (b *ApplicationBuilder) GetLinguisticsFactory() *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 }