edge-connect-client/hexagonal-architecture-proposal.md
Stephan Lo 8e2e61d61e feat: implement dependency injection with proper hexagonal architecture
 Features:
- Simple dependency inversion following SOLID principles
- Clean constructor injection without complex DI containers
- Proper hexagonal architecture with driving/driven separation
- Presentation layer moved to cmd/cli for correct application structure

🏗️ Architecture Changes:
- Driving Adapters (Inbound): internal/adapters/driving/cli/
- Driven Adapters (Outbound): internal/adapters/driven/edgeconnect/
- Core Services: Dependency-injected via interface parameters
- main.go relocated from root to cmd/cli/main.go

📦 Application Flow:
1. cmd/cli/main.go - Entry point and dependency wiring
   └── Creates EdgeConnect client based on environment
   └── Instantiates services with injected repositories
   └── Executes CLI with properly wired dependencies

2. internal/adapters/driving/cli/ - User interface layer
   └── Receives user commands and input validation
   └── Delegates to core services via driving ports
   └── Handles presentation logic and output formatting

3. internal/core/services/ - Business logic layer
   └── NewAppService(appRepo, instanceRepo) - Constructor injection
   └── NewAppInstanceService(instanceRepo) - Interface dependencies
   └── NewCloudletService(cloudletRepo) - Clean separation

4. internal/adapters/driven/edgeconnect/ - Infrastructure layer
   └── Implements repository interfaces for external API
   └── Handles HTTP communication and data persistence
   └── Provides concrete implementations of driven ports

🔧 Build & Deployment:
- CLI Binary: make build → bin/edge-connect-cli
- Usage: ./bin/edge-connect-cli --help
- Tests: make test (all passing)
- Clean: make clean (updated paths)

💡 Benefits:
- Simple and maintainable dependency management
- Testable architecture with clear boundaries
- SOLID principles compliance without overengineering
- Proper separation of concerns in hexagonal structure
2025-10-08 18:15:26 +02:00

4.1 KiB

Proposal: Refactor to Hexagonal Architecture

This document proposes a refactoring of the edge-connect-client project to a Hexagonal Architecture (also known as Ports and Adapters). This will improve the project's maintainability, testability, and flexibility.

Current Architecture

The current project structure is a mix of concerns. The cmd package contains both CLI handling and business logic, the sdk package is a client for the EdgeXR API, and the internal package contains some business logic and configuration handling. This makes it difficult to test the business logic in isolation and to adapt the application to different use cases.

Proposed Hexagonal Architecture

The hexagonal architecture separates the application's core business logic from the outside world. The core communicates with the outside world through ports (interfaces), which are implemented by adapters.

Here is the proposed directory structure:

.
├── cmd/
│   └── main.go
├── internal/
│   ├── core/
│   │   ├── domain/
│   │   │   ├── app.go
│   │   │   └── instance.go
│   │   ├── ports/
│   │   │   ├── driven/
│   │   │   │   ├── app_repository.go
│   │   │   │   └── instance_repository.go
│   │   │   └── driving/
│   │   │       ├── app_service.go
│   │   │       └── instance_service.go
│   │   └── services/
│   │       ├── app_service.go
│   │       └── instance_service.go
│   └── adapters/
│       ├── cli/
│       │   ├── app.go
│       │   └── instance.go
│       └── edgeconnect/
│           ├── app.go
│           └── instance.go
├── go.mod
└── go.sum

Core

  • internal/core/domain: Contains the core domain objects (e.g., App, AppInstance). These are plain Go structs with no external dependencies.
  • internal/core/ports: Defines the interfaces for communication with the outside world.
    • driving: Interfaces for the services offered by the application (e.g., AppService, InstanceService).
    • driven: Interfaces for the services the application needs (e.g., AppRepository, InstanceRepository).
  • internal/core/services: Implements the driving port interfaces. This is where the core business logic resides.

Adapters

  • internal/adapters/driving/cli: The CLI adapter. It implements the user interface and calls the driving ports of the core.
  • internal/adapters/driven/edgeconnect: The EdgeXR API adapter. It implements the driven port interfaces and communicates with the EdgeXR API.

cmd

  • cmd/cli/main.go: The main entry point of the CLI application. It is responsible for wiring everything together: creating the adapters, injecting them into the core services, and starting the CLI.

Refactoring Steps

  1. Define domain models: Create the domain models in internal/core/domain.
  2. Define ports: Define the driving and driven port interfaces in internal/core/ports.
  3. Implement core services: Implement the core business logic in internal/core/services.
  4. Create adapters:
    • Move the existing CLI code from cmd to internal/adapters/driving/cli and adapt it to call the core services.
    • Move the existing sdk code to internal/adapters/driven/edgeconnect and adapt it to implement the repository interfaces.
  5. Wire everything together: Update cmd/cli/main.go to create the adapters and inject them into the core services.

Benefits

  • Improved Testability: The core business logic can be tested in isolation, without the need for the CLI framework or the EdgeXR API.
  • Increased Flexibility: The application can be easily adapted to different use cases by creating new adapters. For example, we could add a REST API by creating a new adapter.
  • Better Separation of Concerns: The hexagonal architecture enforces a clear separation between the business logic and the infrastructure, making the code easier to understand and maintain.