package service import ( "context" "errors" "fmt" "time" fwingestion "github.com/yourorg/go-dw-platform/framework/ingestion" fwmetrics "github.com/yourorg/go-dw-platform/framework/metrics" "github.com/yourorg/go-dw-platform/services/template-service/domain" "github.com/yourorg/go-dw-platform/services/template-service/dto" "github.com/yourorg/go-dw-platform/services/template-service/entity" "github.com/yourorg/go-dw-platform/services/template-service/repository" "github.com/yourorg/go-dw-platform/services/template-service/transformer" ) var ErrDuplicate = errors.New("duplicate order") type Service struct { repository *repository.Repository transformer *transformer.Transformer batcher *fwingestion.Batcher[*domain.Order] retrier *fwingestion.Retrier } func New(repo *repository.Repository, tf *transformer.Transformer, batchSize int, batchTimeout time.Duration, maxRetries int, retryDelay time.Duration) *Service { return &Service{ repository: repo, transformer: tf, batcher: fwingestion.NewBatcher[*domain.Order](batchSize, batchTimeout), retrier: fwingestion.NewRetrier(maxRetries, retryDelay), } } func (s *Service) Process(ctx context.Context, req *dto.IngestRequest) (*dto.ProcessResult, error) { order, err := s.transformer.RequestToDomain(req) if err != nil { return nil, fmt.Errorf("process: %w", err) } exists, err := s.repository.ExistsByKey(ctx, order.NaturalKey()) if err != nil { return nil, fmt.Errorf("check duplicate: %w", err) } if exists { return nil, ErrDuplicate } s.batcher.Add(order) if s.batcher.IsFull() || s.batcher.IsExpired() { return s.FlushBatch(ctx) } return &dto.ProcessResult{Status: "queued"}, nil } func (s *Service) FlushBatch(ctx context.Context) (*dto.ProcessResult, error) { start := time.Now() batch := s.batcher.Get() entities := make([]*entity.Order, len(batch)) for i, d := range batch { entities[i] = s.transformer.DomainToEntity(d) } rows, err := s.retrier.Do(ctx, func() (int64, error) { return s.repository.InsertBatch(ctx, entities) }) if err != nil { return nil, fmt.Errorf("flush batch: %w", err) } fwmetrics.RecordBatchInsert(len(batch), time.Since(start)) s.batcher.Clear() return &dto.ProcessResult{ Status: "success", RowsInserted: rows, }, nil }