tercul-backend/internal/app/application_builder.go
Damir Mukimov fa336cacf3
wip
2025-09-01 00:43:59 +02:00

312 lines
8.7 KiB
Go

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
}