138 lines
3.7 KiB
Go
138 lines
3.7 KiB
Go
// Package service provides business logic / use cases for the application.
|
|
// Services orchestrate domain operations using port interfaces.
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"git.threesix.ai/jordan/sp2-verify-1770320675/pkg/logging"
|
|
"git.threesix.ai/jordan/sp2-verify-1770320675/services/api/internal/domain"
|
|
"git.threesix.ai/jordan/sp2-verify-1770320675/services/api/internal/port"
|
|
)
|
|
|
|
// ExampleService handles example-related business logic.
|
|
type ExampleService struct {
|
|
repo port.ExampleRepository
|
|
logger *logging.Logger
|
|
}
|
|
|
|
// NewExampleService creates a new example service.
|
|
func NewExampleService(repo port.ExampleRepository, logger *logging.Logger) *ExampleService {
|
|
return &ExampleService{
|
|
repo: repo,
|
|
logger: logger.WithService("ExampleService"),
|
|
}
|
|
}
|
|
|
|
// List returns all examples.
|
|
func (s *ExampleService) List(ctx context.Context) ([]domain.Example, error) {
|
|
return s.repo.List(ctx)
|
|
}
|
|
|
|
// Get returns an example by ID.
|
|
// Returns domain.ErrExampleNotFound if not found.
|
|
func (s *ExampleService) Get(ctx context.Context, id domain.ExampleID) (*domain.Example, error) {
|
|
return s.repo.Get(ctx, id)
|
|
}
|
|
|
|
// CreateInput contains the data needed to create an example.
|
|
type CreateInput struct {
|
|
Name string
|
|
Description string
|
|
}
|
|
|
|
// Create creates a new example with duplicate detection.
|
|
// Returns domain.ErrDuplicateExample if name already exists.
|
|
// Returns domain.ErrInvalidExampleName if name is invalid.
|
|
func (s *ExampleService) Create(ctx context.Context, input CreateInput) (*domain.Example, error) {
|
|
// Check for duplicates
|
|
exists, err := s.repo.ExistsByName(ctx, input.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if exists {
|
|
return nil, domain.ErrDuplicateExample
|
|
}
|
|
|
|
// Generate new ID
|
|
id := domain.ExampleID(uuid.New().String())
|
|
|
|
// Create domain entity (validates name)
|
|
example, err := domain.NewExample(id, input.Name, input.Description)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Persist
|
|
if err := s.repo.Create(ctx, example); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("example created", "id", id, "name", input.Name)
|
|
return example, nil
|
|
}
|
|
|
|
// UpdateInput contains the data needed to update an example.
|
|
type UpdateInput struct {
|
|
Name string
|
|
Description string
|
|
}
|
|
|
|
// Update modifies an existing example.
|
|
// Returns domain.ErrExampleNotFound if not found.
|
|
// Returns domain.ErrDuplicateExample if new name conflicts with another example.
|
|
// Returns domain.ErrInvalidExampleName if name is invalid.
|
|
func (s *ExampleService) Update(ctx context.Context, id domain.ExampleID, input UpdateInput) (*domain.Example, error) {
|
|
// Fetch existing
|
|
example, err := s.repo.Get(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check for name conflicts (only if name changed)
|
|
if example.Name != input.Name {
|
|
exists, err := s.repo.ExistsByName(ctx, input.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if exists {
|
|
return nil, domain.ErrDuplicateExample
|
|
}
|
|
}
|
|
|
|
// Update domain entity (validates name)
|
|
if err := example.Update(input.Name, input.Description); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Persist
|
|
if err := s.repo.Update(ctx, example); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.logger.Info("example updated", "id", id, "name", input.Name)
|
|
return example, nil
|
|
}
|
|
|
|
// Delete removes an example by ID.
|
|
// Returns domain.ErrExampleNotFound if not found.
|
|
func (s *ExampleService) Delete(ctx context.Context, id domain.ExampleID) error {
|
|
// Verify exists before delete
|
|
if _, err := s.repo.Get(ctx, id); err != nil {
|
|
if errors.Is(err, domain.ErrExampleNotFound) {
|
|
return domain.ErrExampleNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
if err := s.repo.Delete(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.logger.Info("example deleted", "id", id)
|
|
return nil
|
|
}
|