omnix-sopiga/services/template-service/service/orders.go

84 lines
2.3 KiB
Go
Raw Permalink Normal View History

2026-08-07 12:21:42 +07:00
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
}