Multiple implementations of the same back-end application. The aim is to provide quick, side-by-side comparisons of different technologies (languages, frameworks, libraries) while preserving consistent business logic across all implementations.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

97 lines
3.2 KiB

package commands
import (
"context"
"errors"
"fmt"
"time"
"autostore/internal/application/interfaces"
"autostore/internal/domain/entities"
"autostore/internal/domain/specifications"
"autostore/internal/domain/value_objects"
)
var (
ErrItemCreationFailed = errors.New("failed to create item")
ErrInvalidUserID = errors.New("invalid user ID")
)
type AddItemCommand struct {
itemRepo interfaces.IItemRepository
orderService interfaces.IOrderService
timeProvider interfaces.ITimeProvider
expirationSpec *specifications.ItemExpirationSpec
logger interfaces.ILogger
}
func NewAddItemCommand(
itemRepo interfaces.IItemRepository,
orderService interfaces.IOrderService,
timeProvider interfaces.ITimeProvider,
expirationSpec *specifications.ItemExpirationSpec,
logger interfaces.ILogger,
) *AddItemCommand {
return &AddItemCommand{
itemRepo: itemRepo,
orderService: orderService,
timeProvider: timeProvider,
expirationSpec: expirationSpec,
logger: logger,
}
}
func (c *AddItemCommand) Execute(ctx context.Context, name string, expirationDate time.Time, orderURL string, userID string) (string, error) {
c.logger.Info(ctx, "Executing AddItemCommand", "name", name, "userID", userID)
// Convert string IDs to value objects
itemID, err := value_objects.NewRandomItemID()
if err != nil {
c.logger.Error(ctx, "Failed to generate item ID", "error", err)
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err)
}
userIDObj, err := value_objects.NewUserID(userID)
if err != nil {
c.logger.Error(ctx, "Invalid user ID", "userID", userID, "error", err)
return "", fmt.Errorf("%w: %v", ErrInvalidUserID, err)
}
// Create expiration date value object
expirationDateObj, err := value_objects.NewExpirationDate(expirationDate)
if err != nil {
c.logger.Error(ctx, "Invalid expiration date", "expirationDate", expirationDate, "error", err)
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err)
}
// Create item entity
item, err := entities.NewItem(itemID, name, expirationDateObj, orderURL, userIDObj)
if err != nil {
c.logger.Error(ctx, "Failed to create item entity", "error", err)
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err)
}
// Get current time and check if item is expired
currentTime := c.timeProvider.Now()
isExpired := c.expirationSpec.IsExpired(item, currentTime)
// Save the item first (even if expired, as per business rule #4)
if err := c.itemRepo.Save(ctx, item); err != nil {
c.logger.Error(ctx, "Failed to save item", "itemID", itemID.String(), "error", err)
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err)
}
if isExpired {
c.logger.Info(ctx, "Item is expired, triggering order", "itemID", itemID.String())
// Business rule: When an item expires, a new item of the same type is automatically ordered
if err := c.orderService.OrderItem(ctx, item); err != nil {
c.logger.Error(ctx, "Failed to order expired item", "itemID", itemID.String(), "error", err)
// Don't fail the entire operation if ordering fails, just log it
c.logger.Warn(ctx, "Item created but ordering failed", "itemID", itemID.String(), "error", err)
}
}
c.logger.Info(ctx, "Item created successfully", "itemID", itemID.String())
return itemID.String(), nil
}