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.
 
 
 
 
 
 

25 KiB

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.