edge-connect-client/hexagonal-architecture-proposal.md

77 lines
4.1 KiB
Markdown
Raw Permalink Normal View History

# 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
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
* `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`
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
* `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:**
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
* 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.