|
|
|
|
@ -1,604 +0,0 @@
|
|
|
|
|
# Go Implementation Plan for AutoStore |
|
|
|
|
|
|
|
|
|
## Overview |
|
|
|
|
Implementation of AutoStore system using Go, following Clean Architecture principles. The system stores items with expiration dates and automatically orders new items when they expire. |
|
|
|
|
|
|
|
|
|
## Architecture Approach |
|
|
|
|
- **Clean Architecture** with clear separation of concerns |
|
|
|
|
- **Domain-Driven Design** with rich domain models |
|
|
|
|
- **Hexagonal Architecture** with dependency inversion |
|
|
|
|
- **Repository Pattern** for data persistence |
|
|
|
|
- **CQRS-like** command/query separation |
|
|
|
|
- **Dependency Injection** using Go's interface system and a DI container |
|
|
|
|
|
|
|
|
|
## Core Domain Logic |
|
|
|
|
|
|
|
|
|
### ItemExpirationSpec - Single Source of Truth for Expiration |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/specifications/item_expiration_spec.go` |
|
|
|
|
**Purpose**: Centralized expiration checking logic - the single source of truth for determining if items are expired |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `IsExpired(item *ItemEntity, currentTime time.Time) bool` - Checks if item expired |
|
|
|
|
- `GetSpec(currentTime time.Time) Specification[ItemEntity]` - Returns specification for repository queries |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Called by `AddItemCommand.Execute()` to check newly created items for immediate expiration |
|
|
|
|
- Called by `HandleExpiredItemsCommand.Execute()` to find expired items for processing |
|
|
|
|
- Used by `ItemRepository.FindWhere()` to query database for expired items |
|
|
|
|
|
|
|
|
|
## Detailed Implementation Plan |
|
|
|
|
|
|
|
|
|
### Domain Layer |
|
|
|
|
|
|
|
|
|
#### 1. Entities |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/entities/item.go` |
|
|
|
|
**Purpose**: Core business entity representing an item |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewItem(id ItemID, name string, expirationDate ExpirationDate, orderURL string, userID UserID) (*ItemEntity, error)` - Creates item with validation |
|
|
|
|
- `GetID() ItemID` - Returns item ID |
|
|
|
|
- `GetName() string` - Returns item name |
|
|
|
|
- `GetExpirationDate() ExpirationDate` - Returns expiration date |
|
|
|
|
- `GetOrderURL() string` - Returns order URL |
|
|
|
|
- `GetUserID() UserID` - Returns user ID |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Created by `AddItemCommand.Execute()` |
|
|
|
|
- Retrieved by `ItemRepository` methods |
|
|
|
|
- Passed to `ItemExpirationSpec.IsExpired()` for expiration checking |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/entities/user.go` |
|
|
|
|
**Purpose**: User entity for item ownership and authentication purposes |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewUser(id UserID, username string, passwordHash string) (*UserEntity, error)` - Creates user with validation |
|
|
|
|
- `GetID() UserID` - Returns user ID |
|
|
|
|
- `GetUsername() string` - Returns username |
|
|
|
|
- `GetPasswordHash() string` - Returns password hash |
|
|
|
|
- `ValidatePassword(password string) bool` - Validates password |
|
|
|
|
|
|
|
|
|
#### 2. Value Objects |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/value_objects/item_id.go` |
|
|
|
|
**Purpose**: Strong typing for item identifiers |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewItemID(value string) (ItemID, error)` - Validates UUID format |
|
|
|
|
- `String() string` - Returns string value |
|
|
|
|
- `Equals(other ItemID) bool` - Compares with another ItemID |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/value_objects/user_id.go` |
|
|
|
|
**Purpose**: Strong typing for user identifiers |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewUserID(value string) (UserID, error)` - Validates UUID format |
|
|
|
|
- `String() string` - Returns string value |
|
|
|
|
- `Equals(other UserID) bool` - Compares with another UserID |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/value_objects/expiration_date.go` |
|
|
|
|
**Purpose**: Immutable expiration date with validation |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewExpirationDate(value time.Time) (ExpirationDate, error)` - Validates date format (allows past dates per business rules) |
|
|
|
|
- `Time() time.Time` - Returns time.Time object |
|
|
|
|
- `String() string` - Returns ISO string format |
|
|
|
|
- `IsExpired(currentTime time.Time) bool` - Checks if date is expired |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Used by `ItemEntity` constructor for type-safe date handling |
|
|
|
|
- Validated by `ItemExpirationSpec.IsExpired()` for expiration logic |
|
|
|
|
|
|
|
|
|
#### 3. Specifications |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/specifications/specification.go` |
|
|
|
|
**Purpose**: Generic specification pattern interface |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `IsSatisfiedBy(candidate T) bool` - Evaluates specification |
|
|
|
|
- `And(other Specification[T]) Specification[T]` - Combines with AND |
|
|
|
|
- `Or(other Specification[T]) Specification[T]` - Combines with OR |
|
|
|
|
- `Not() Specification[T]` - Negates specification |
|
|
|
|
|
|
|
|
|
**File**: `internal/domain/specifications/item_expiration_spec.go` |
|
|
|
|
**Purpose**: Implementation of expiration specification |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `IsExpired(item *ItemEntity, currentTime time.Time) bool` - Checks if item is expired |
|
|
|
|
- `GetSpec(currentTime time.Time) Specification[ItemEntity]` - Returns specification for repository queries |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Implemented by `ItemExpirationSpec` for type-safe specifications |
|
|
|
|
- Used by `ItemRepository.FindWhere()` for database queries |
|
|
|
|
|
|
|
|
|
### Application Layer |
|
|
|
|
|
|
|
|
|
#### 4. Commands |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/commands/add_item_command.go` |
|
|
|
|
**Purpose**: Use case for creating new items with expiration handling |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewAddItemCommand(itemRepo IItemRepository, orderService IOrderService, timeProvider ITimeProvider, expirationSpec *ItemExpirationSpec, logger ILogger) *AddItemCommand` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context, name string, expirationDate time.Time, orderURL string, userID string) (string, error)` - Creates item, handles expired items immediately |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. `ItemsController.CreateItem()` calls `AddItemCommand.Execute()` |
|
|
|
|
2. Creates `ItemEntity` with validated data |
|
|
|
|
3. Calls `ItemExpirationSpec.IsExpired()` to check if item is expired |
|
|
|
|
4. If expired: |
|
|
|
|
- calls `OrderHTTPService.OrderItem()` |
|
|
|
|
- **returns item ID** (business rule: expired items trigger ordering but still return ID field that might be empty or invalid) |
|
|
|
|
5. If not expired: calls `ItemRepository.Save()` and returns item ID |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/commands/handle_expired_items_command.go` |
|
|
|
|
**Purpose**: Background command to process expired items |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewHandleExpiredItemsCommand(itemRepo IItemRepository, orderService IOrderService, timeProvider ITimeProvider, expirationSpec *ItemExpirationSpec, logger ILogger) *HandleExpiredItemsCommand` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context) error` - Finds and processes all expired items |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. `ExpiredItemsScheduler.Run()` calls `HandleExpiredItemsCommand.Execute()` |
|
|
|
|
2. Gets current time from `ITimeProvider` |
|
|
|
|
3. Calls `ItemExpirationSpec.GetSpec()` to get expiration specification |
|
|
|
|
4. Calls `ItemRepository.FindWhere()` to find expired items |
|
|
|
|
5. For each expired item: calls `OrderHTTPService.OrderItem()` then `ItemRepository.Delete()` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/commands/delete_item_command.go` |
|
|
|
|
**Purpose**: Use case for deleting user items |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewDeleteItemCommand(itemRepo IItemRepository, logger ILogger) *DeleteItemCommand` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context, itemID string, userID string) error` - Validates ownership and deletes item |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. `ItemsController.DeleteItem()` calls `DeleteItemCommand.Execute()` |
|
|
|
|
2. Calls `ItemRepository.FindByID()` to retrieve item |
|
|
|
|
3. Validates ownership by comparing user IDs |
|
|
|
|
4. Calls `ItemRepository.Delete()` to remove item |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/commands/login_user_command.go` |
|
|
|
|
**Purpose**: User authentication use case |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewLoginUserCommand(authService IAuthService, logger ILogger) *LoginUserCommand` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context, username string, password string) (string, error)` - Authenticates and returns JWT token |
|
|
|
|
|
|
|
|
|
#### 5. Queries |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/queries/get_item_query.go` |
|
|
|
|
**Purpose**: Retrieves single item by ID with authorization |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewGetItemQuery(itemRepo IItemRepository, logger ILogger) *GetItemQuery` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context, itemID string, userID string) (*ItemEntity, error)` - Validates ownership and returns item |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. `ItemsController.GetItem()` calls `GetItemQuery.Execute()` |
|
|
|
|
2. Calls `ItemRepository.FindByID()` to retrieve item |
|
|
|
|
3. Validates ownership by comparing user IDs |
|
|
|
|
4. Returns item entity |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/queries/list_items_query.go` |
|
|
|
|
**Purpose**: Retrieves all items for authenticated user |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewListItemsQuery(itemRepo IItemRepository, logger ILogger) *ListItemsQuery` - Constructor with dependency injection |
|
|
|
|
- `Execute(ctx context.Context, userID string) ([]*ItemEntity, error)` - Returns user's items |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. `ItemsController.ListItems()` calls `ListItemsQuery.Execute()` |
|
|
|
|
2. Calls `ItemRepository.FindByUserID()` to retrieve user's items |
|
|
|
|
3. Returns array of item entities |
|
|
|
|
|
|
|
|
|
#### 6. DTOs |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/dto/create_item_dto.go` |
|
|
|
|
**Purpose**: Request validation for item creation |
|
|
|
|
|
|
|
|
|
**Key Properties**: |
|
|
|
|
- `Name string` - Item name (validation: not empty, max 255 chars) |
|
|
|
|
- `ExpirationDate time.Time` - Date (validation: valid date) |
|
|
|
|
- `OrderURL string` - Order URL (validation: valid URL format) |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Validate() error` - Validates DTO fields |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Used by `ItemsController.CreateItem()` for request body validation |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/dto/item_response_dto.go` |
|
|
|
|
**Purpose**: Standardized item response format |
|
|
|
|
|
|
|
|
|
**Key Properties**: |
|
|
|
|
- `ID string` - Item ID |
|
|
|
|
- `Name string` - Item name |
|
|
|
|
- `ExpirationDate time.Time` - Expiration date |
|
|
|
|
- `OrderURL string` - Order URL |
|
|
|
|
- `UserID string` - Owner user ID |
|
|
|
|
- `CreatedAt time.Time` - Creation timestamp |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `FromEntity(item *ItemEntity) *ItemResponseDTO` - Creates DTO from entity |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Used by all item controller methods for response transformation |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/dto/login_dto.go` |
|
|
|
|
**Purpose**: Login request validation |
|
|
|
|
|
|
|
|
|
**Key Properties**: |
|
|
|
|
- `Username string` - Username (validation: not empty) |
|
|
|
|
- `Password string` - Password (validation: not empty) |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Validate() error` - Validates DTO fields |
|
|
|
|
|
|
|
|
|
#### 7. Interfaces |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/item_repository.go` |
|
|
|
|
**Purpose**: Repository interface for item persistence |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Save(ctx context.Context, item *ItemEntity) error` |
|
|
|
|
- `FindByID(ctx context.Context, id ItemID) (*ItemEntity, error)` |
|
|
|
|
- `FindByUserID(ctx context.Context, userID UserID) ([]*ItemEntity, error)` |
|
|
|
|
- `FindWhere(ctx context.Context, spec Specification[ItemEntity]) ([]*ItemEntity, error)` |
|
|
|
|
- `Delete(ctx context.Context, id ItemID) error` |
|
|
|
|
- `Exists(ctx context.Context, id ItemID) (bool, error)` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/user_repository.go` |
|
|
|
|
**Purpose**: Repository interface for user persistence |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `FindByUsername(ctx context.Context, username string) (*UserEntity, error)` |
|
|
|
|
- `FindByID(ctx context.Context, id UserID) (*UserEntity, error)` |
|
|
|
|
- `Save(ctx context.Context, user *UserEntity) error` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/auth_service.go` |
|
|
|
|
**Purpose**: Authentication service interface |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Authenticate(ctx context.Context, username string, password string) (string, error)` |
|
|
|
|
- `ValidateToken(ctx context.Context, token string) (bool, error)` |
|
|
|
|
- `GetUserIDFromToken(ctx context.Context, token string) (string, error)` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/order_service.go` |
|
|
|
|
**Purpose**: Order service interface |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `OrderItem(ctx context.Context, item *ItemEntity) error` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/time_provider.go` |
|
|
|
|
**Purpose**: Time provider interface for testing |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Now() time.Time` |
|
|
|
|
|
|
|
|
|
**File**: `internal/application/interfaces/logger.go` |
|
|
|
|
**Purpose**: Logger interface |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Info(ctx context.Context, msg string, fields ...interface{})` |
|
|
|
|
- `Error(ctx context.Context, msg string, fields ...interface{})` |
|
|
|
|
- `Debug(ctx context.Context, msg string, fields ...interface{})` |
|
|
|
|
- `Warn(ctx context.Context, msg string, fields ...interface{})` |
|
|
|
|
|
|
|
|
|
### Infrastructure Layer |
|
|
|
|
|
|
|
|
|
#### 8. Repositories |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/repositories/file_item_repository.go` |
|
|
|
|
**Purpose**: File-based implementation of item repository using JSON files |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Save(ctx context.Context, item *ItemEntity) error` - Persists item entity |
|
|
|
|
- `FindByID(ctx context.Context, id ItemID) (*ItemEntity, error)` - Finds by ID |
|
|
|
|
- `FindByUserID(ctx context.Context, userID UserID) ([]*ItemEntity, error)` - Finds by user |
|
|
|
|
- `FindWhere(ctx context.Context, spec Specification[ItemEntity]) ([]*ItemEntity, error)` - Finds by specification using `ItemExpirationSpec` |
|
|
|
|
- `Delete(ctx context.Context, id ItemID) error` - Deletes item |
|
|
|
|
- `Exists(ctx context.Context, id ItemID) (bool, error)` - Checks existence |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Called by all commands and queries for data persistence and retrieval |
|
|
|
|
- Uses `ItemExpirationSpec` for finding expired items |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/repositories/file_user_repository.go` |
|
|
|
|
**Purpose**: File-based implementation of user repository using JSON files |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `FindByUsername(ctx context.Context, username string) (*UserEntity, error)` - Finds by username |
|
|
|
|
- `FindByID(ctx context.Context, id UserID) (*UserEntity, error)` - Finds by ID |
|
|
|
|
- `Save(ctx context.Context, user *UserEntity) error` - Saves user |
|
|
|
|
|
|
|
|
|
#### 9. HTTP Services |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/http/order_http_service.go` |
|
|
|
|
**Purpose**: HTTP implementation of order service |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewOrderHTTPService(client *http.Client, logger ILogger) *OrderHTTPService` - Constructor with dependency injection |
|
|
|
|
- `OrderItem(ctx context.Context, item *ItemEntity) error` - Sends POST request to order URL |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Called by `AddItemCommand.Execute()` for expired items |
|
|
|
|
- Called by `HandleExpiredItemsCommand.Execute()` for batch processing |
|
|
|
|
|
|
|
|
|
#### 10. Authentication |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/auth/jwt_auth_service.go` |
|
|
|
|
**Purpose**: JWT implementation of authentication service |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewJWTAuthService(userRepo IUserRepository, secretKey string, logger ILogger) *JWTAuthService` - Constructor with dependency injection |
|
|
|
|
- `Authenticate(ctx context.Context, username string, password string) (string, error)` - Validates credentials and generates JWT |
|
|
|
|
- `ValidateToken(ctx context.Context, token string) (bool, error)` - Validates JWT token |
|
|
|
|
- `GetUserIDFromToken(ctx context.Context, token string) (string, error)` - Extracts user ID from token |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Called by `LoginUserCommand.Execute()` for user authentication |
|
|
|
|
- Used by `JWTMiddleware` for route protection |
|
|
|
|
|
|
|
|
|
#### 11. Time Provider |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/time/system_time_provider.go` |
|
|
|
|
**Purpose**: System time implementation |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewSystemTimeProvider() *SystemTimeProvider` - Constructor |
|
|
|
|
- `Now() time.Time` - Returns current system time |
|
|
|
|
|
|
|
|
|
#### 12. Logger |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/logging/standard_logger.go` |
|
|
|
|
**Purpose**: Standard library logger implementation |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewStandardLogger(writer io.Writer) *StandardLogger` - Constructor |
|
|
|
|
- `Info(ctx context.Context, msg string, fields ...interface{})` - Logs info message |
|
|
|
|
- `Error(ctx context.Context, msg string, fields ...interface{})` - Logs error message |
|
|
|
|
- `Debug(ctx context.Context, msg string, fields ...interface{})` - Logs debug message |
|
|
|
|
- `Warn(ctx context.Context, msg string, fields ...interface{})` - Logs warning message |
|
|
|
|
|
|
|
|
|
### Presentation Layer |
|
|
|
|
|
|
|
|
|
#### 13. Controllers |
|
|
|
|
|
|
|
|
|
**File**: `internal/presentation/controllers/items_controller.go` |
|
|
|
|
**Purpose**: REST API endpoints for item management |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewItemsController(addItemCmd *AddItemCommand, getItemQry *GetItemQuery, listItemsQry *ListItemsQuery, deleteItemCmd *DeleteItemCommand, logger ILogger) *ItemsController` - Constructor with dependency injection |
|
|
|
|
- `CreateItem(c *gin.Context)` - POST /items |
|
|
|
|
- `GetItem(c *gin.Context)` - GET /items/:id |
|
|
|
|
- `ListItems(c *gin.Context)` - GET /items |
|
|
|
|
- `DeleteItem(c *gin.Context)` - DELETE /items/:id |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
- Receives HTTP requests and validates input |
|
|
|
|
- Calls appropriate commands/queries based on HTTP method |
|
|
|
|
- Returns standardized responses with DTOs |
|
|
|
|
|
|
|
|
|
**File**: `internal/presentation/controllers/auth_controller.go` |
|
|
|
|
**Purpose**: Authentication endpoints |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewAuthController(loginUserCmd *LoginUserCommand, logger ILogger) *AuthController` - Constructor with dependency injection |
|
|
|
|
- `Login(c *gin.Context)` - POST /login |
|
|
|
|
|
|
|
|
|
#### 14. Middleware |
|
|
|
|
|
|
|
|
|
**File**: `internal/presentation/middleware/jwt_middleware.go` |
|
|
|
|
**Purpose**: JWT authentication middleware |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewJWTMiddleware(authService IAuthService, logger ILogger) *JWTMiddleware` - Constructor with dependency injection |
|
|
|
|
- `Middleware() gin.HandlerFunc` - Returns gin middleware function |
|
|
|
|
|
|
|
|
|
**Place in the flow**: |
|
|
|
|
- Applied to all protected routes by Gin router group |
|
|
|
|
- Uses `JWTAuthService` for token validation |
|
|
|
|
|
|
|
|
|
#### 15. Server |
|
|
|
|
|
|
|
|
|
**File**: `internal/presentation/server/server.go` |
|
|
|
|
**Purpose**: HTTP server setup and configuration |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewServer(config *Config, logger ILogger) *Server` - Constructor with dependency injection |
|
|
|
|
- `Start() error` - Starts the HTTP server |
|
|
|
|
- `SetupRoutes() *gin.Engine` - Sets up routes and middleware |
|
|
|
|
|
|
|
|
|
### Background Processing |
|
|
|
|
|
|
|
|
|
**File**: `internal/infrastructure/scheduler/expired_items_scheduler.go` |
|
|
|
|
**Purpose**: Scheduled job for processing expired items using a ticker |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewExpiredItemsScheduler(handleExpiredItemsCmd *HandleExpiredItemsCommand, logger ILogger) *ExpiredItemsScheduler` - Constructor with dependency injection |
|
|
|
|
- `Start(ctx context.Context) error` - Starts the scheduler |
|
|
|
|
- `Stop() error` - Stops the scheduler |
|
|
|
|
- `processExpiredItems(ctx context.Context) error` - Processes expired items |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. **On startup**: `processExpiredItems()` immediately calls `HandleExpiredItemsCommand.Execute()` |
|
|
|
|
2. **Every minute**: Ticker triggers `processExpiredItems()` to process expired items |
|
|
|
|
3. All methods use context for cancellation and error handling |
|
|
|
|
4. Comprehensive logging for monitoring and debugging |
|
|
|
|
|
|
|
|
|
**Configuration**: |
|
|
|
|
- Uses `time.Ticker` for periodic execution |
|
|
|
|
- Implements graceful shutdown with context cancellation |
|
|
|
|
- Configurable interval (default: 1 minute) |
|
|
|
|
|
|
|
|
|
### Dependency Injection |
|
|
|
|
|
|
|
|
|
**File**: `internal/container/container.go` |
|
|
|
|
**Purpose**: Dependency injection container setup |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `NewContainer(config *Config) *Container` - Constructor |
|
|
|
|
- `Initialize() error` - Initializes all dependencies |
|
|
|
|
- `GetItemRepository() IItemRepository` - Returns item repository |
|
|
|
|
- `GetUserRepository() IUserRepository` - Returns user repository |
|
|
|
|
- `GetAuthService() IAuthService` - Returns auth service |
|
|
|
|
- `GetOrderService() IOrderService` - Returns order service |
|
|
|
|
- `GetTimeProvider() ITimeProvider` - Returns time provider |
|
|
|
|
- `GetLogger() ILogger` - Returns logger |
|
|
|
|
- `GetAddItemCommand() *AddItemCommand` - Returns add item command |
|
|
|
|
- `GetHandleExpiredItemsCommand() *HandleExpiredItemsCommand` - Returns handle expired items command |
|
|
|
|
- `GetDeleteItemCommand() *DeleteItemCommand` - Returns delete item command |
|
|
|
|
- `GetLoginUserCommand() *LoginUserCommand` - Returns login user command |
|
|
|
|
- `GetGetItemQuery() *GetItemQuery` - Returns get item query |
|
|
|
|
- `GetListItemsQuery() *ListItemsQuery` - Returns list items query |
|
|
|
|
- `GetItemsController() *ItemsController` - Returns items controller |
|
|
|
|
- `GetAuthController() *AuthController` - Returns auth controller |
|
|
|
|
- `GetJWTMiddleware() *JWTMiddleware` - Returns JWT middleware |
|
|
|
|
- `GetExpiredItemsScheduler() *ExpiredItemsScheduler` - Returns expired items scheduler |
|
|
|
|
|
|
|
|
|
### Configuration |
|
|
|
|
|
|
|
|
|
**File**: `internal/config/config.go` |
|
|
|
|
**Purpose**: Application configuration |
|
|
|
|
|
|
|
|
|
**Key Properties**: |
|
|
|
|
- `ServerPort int` - Server port |
|
|
|
|
- `JWTSecret string` - JWT secret key |
|
|
|
|
- `DataDirectory string` - Directory for data files |
|
|
|
|
- `LogLevel string` - Log level |
|
|
|
|
- `SchedulerInterval time.Duration` - Scheduler interval |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `Load() (*Config, error)` - Loads configuration from environment variables |
|
|
|
|
- `Validate() error` - Validates configuration |
|
|
|
|
|
|
|
|
|
### Main Application |
|
|
|
|
|
|
|
|
|
**File**: `cmd/main.go` |
|
|
|
|
**Purpose**: Application entry point |
|
|
|
|
|
|
|
|
|
**Key Methods**: |
|
|
|
|
- `main()` - Main function |
|
|
|
|
- `setupGracefulShutdown(server *Server, scheduler *ExpiredItemsScheduler)` - Sets up graceful shutdown |
|
|
|
|
|
|
|
|
|
**Flow**: |
|
|
|
|
1. Loads configuration |
|
|
|
|
2. Initializes dependency container |
|
|
|
|
3. Creates server and scheduler |
|
|
|
|
4. Starts scheduler |
|
|
|
|
5. Starts server |
|
|
|
|
6. Sets up graceful shutdown |
|
|
|
|
|
|
|
|
|
## Complete Flow Summary |
|
|
|
|
|
|
|
|
|
### Item Creation Flow |
|
|
|
|
``` |
|
|
|
|
POST /items |
|
|
|
|
├── JWTMiddleware (authentication) |
|
|
|
|
├── CreateItemDTO validation |
|
|
|
|
├── ItemsController.CreateItem() |
|
|
|
|
│ ├── AddItemCommand.Execute() |
|
|
|
|
│ │ ├── ItemEntity constructor (validation) |
|
|
|
|
│ │ ├── ItemExpirationSpec.IsExpired() ← SINGLE SOURCE OF TRUTH |
|
|
|
|
│ │ ├── If expired: OrderHTTPService.OrderItem() |
|
|
|
|
│ │ └── If not expired: ItemRepository.Save() |
|
|
|
|
│ └── ItemResponseDTO transformation |
|
|
|
|
└── HTTP response |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
### Expired Items Processing Flow |
|
|
|
|
``` |
|
|
|
|
Scheduler (every minute) |
|
|
|
|
└── ExpiredItemsScheduler.processExpiredItems() |
|
|
|
|
└── HandleExpiredItemsCommand.Execute() |
|
|
|
|
├── ITimeProvider.Now() |
|
|
|
|
├── ItemExpirationSpec.GetSpec() ← SINGLE SOURCE OF TRUTH |
|
|
|
|
├── ItemRepository.FindWhere() (using spec) |
|
|
|
|
├── For each expired item: |
|
|
|
|
│ ├── OrderHTTPService.OrderItem() |
|
|
|
|
│ └── ItemRepository.Delete() |
|
|
|
|
└── Logging |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
### Item Retrieval Flow |
|
|
|
|
``` |
|
|
|
|
GET /items/:id |
|
|
|
|
├── JWTMiddleware (authentication) |
|
|
|
|
├── ItemsController.GetItem() |
|
|
|
|
│ ├── GetItemQuery.Execute() |
|
|
|
|
│ │ ├── ItemRepository.FindByID() |
|
|
|
|
│ │ ├── Ownership validation |
|
|
|
|
│ │ └── Return ItemEntity |
|
|
|
|
│ └── ItemResponseDTO transformation |
|
|
|
|
└── HTTP response |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
## Key Design Principles |
|
|
|
|
|
|
|
|
|
1. **Single Source of Truth**: `ItemExpirationSpec` is the only component that determines expiration logic |
|
|
|
|
2. **Clear Flow**: Each component has a well-defined place in the execution chain |
|
|
|
|
3. **Dependency Inversion**: High-level modules don't depend on low-level modules |
|
|
|
|
4. **Separation of Concerns**: Each layer has distinct responsibilities |
|
|
|
|
5. **Testability**: All components can be tested in isolation |
|
|
|
|
6. **Context Propagation**: All methods accept context for cancellation, deadlines, and tracing |
|
|
|
|
7. **Error Handling**: Comprehensive error handling with proper error types |
|
|
|
|
8. **Configuration Management**: Environment-based configuration with validation |
|
|
|
|
|
|
|
|
|
## Go-Specific Best Practices |
|
|
|
|
|
|
|
|
|
1. **Package Organization**: Follow Go's standard package layout with `internal/` for private packages |
|
|
|
|
2. **Error Handling**: Use Go's error handling pattern with proper error wrapping |
|
|
|
|
3. **Context Usage**: Use context for cancellation, deadlines, and request-scoped values |
|
|
|
|
4. **Interface Segregation**: Keep interfaces small and focused |
|
|
|
|
5. **Naming Conventions**: Follow Go's naming conventions (camelCase for exported, camelCase for unexported) |
|
|
|
|
6. **Pointer vs Value**: Use pointers for mutable state and large structs, values for immutable data |
|
|
|
|
7. **Concurrency**: Use goroutines and channels for concurrent operations |
|
|
|
|
8. **Testing**: Write comprehensive tests with table-driven tests |
|
|
|
|
9. **Documentation**: Use Go's documentation comments for all exported types and functions |
|
|
|
|
10. **Dependencies**: Use Go modules for dependency management |
|
|
|
|
|
|
|
|
|
## Directory Structure |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
golang/ |
|
|
|
|
├── cmd/ |
|
|
|
|
│ └── main.go # Application entry point |
|
|
|
|
├── internal/ |
|
|
|
|
│ ├── application/ |
|
|
|
|
│ │ ├── commands/ # Command handlers |
|
|
|
|
│ │ ├── queries/ # Query handlers |
|
|
|
|
│ │ ├── dto/ # Data transfer objects |
|
|
|
|
│ │ └── interfaces/ # Application interfaces |
|
|
|
|
│ ├── domain/ |
|
|
|
|
│ │ ├── entities/ # Domain entities |
|
|
|
|
│ │ ├── value_objects/ # Value objects |
|
|
|
|
│ │ └── specifications/ # Domain specifications |
|
|
|
|
│ ├── infrastructure/ |
|
|
|
|
│ │ ├── repositories/ # Repository implementations |
|
|
|
|
│ │ ├── http/ # HTTP services |
|
|
|
|
│ │ ├── auth/ # Authentication services |
|
|
|
|
│ │ ├── time/ # Time provider |
|
|
|
|
│ │ ├── logging/ # Logger implementations |
|
|
|
|
│ │ └── scheduler/ # Background scheduler |
|
|
|
|
│ ├── presentation/ |
|
|
|
|
│ │ ├── controllers/ # HTTP controllers |
|
|
|
|
│ │ ├── middleware/ # HTTP middleware |
|
|
|
|
│ │ └── server/ # HTTP server |
|
|
|
|
│ ├── config/ |
|
|
|
|
│ │ └── config.go # Configuration |
|
|
|
|
│ └── container/ |
|
|
|
|
│ └── container.go # Dependency injection container |
|
|
|
|
├── pkg/ |
|
|
|
|
│ └── specifications/ # Reusable specification patterns |
|
|
|
|
├── data/ # Data files (JSON) |
|
|
|
|
├── docker/ |
|
|
|
|
│ ├── docker-compose.yml # Docker compose |
|
|
|
|
│ └── Dockerfile # Docker image |
|
|
|
|
├── go.mod # Go module file |
|
|
|
|
├── go.sum # Go module checksums |
|
|
|
|
└── README.md # Go implementation README |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
This implementation plan ensures consistent development regardless of the implementer, providing clear flow definitions and emphasizing `ItemExpirationSpec` as the centralized source for expiration logic, while following Go best practices and idioms. |