refactor: structure core logic by application use cases
Restructures the internal business logic from a generic `services` package to a use-case-driven design under `internal/application`. Each primary function of the application (`app`, `instance`, `cloudlet`, `apply`) now resides in its own package. This clarifies the architecture and makes it easier to navigate and extend. - Moved service implementations to `internal/application/<usecase>/`. - Kept ports and domain models in `internal/core/`. - Updated `main.go` and CLI adapters to reflect the new paths. - Added missing `RefreshAppInstance` method to satisfy the service interface. - Verified the change with a full build and test run.
This commit is contained in:
parent
19a9807499
commit
f1ee439c61
15 changed files with 75 additions and 67 deletions
|
|
@ -3,31 +3,33 @@ package main
|
|||
import (
|
||||
"os"
|
||||
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/driving/cli"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/driven/edgeconnect"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/services"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/driving/cli"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/application/app"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/application/cloudlet"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/application/instance"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Präsentationsschicht: Simple dependency wiring - no complex container needed
|
||||
|
||||
|
||||
// 1. Infrastructure Layer: Create EdgeConnect client (concrete implementation)
|
||||
baseURL := getEnvOrDefault("EDGE_CONNECT_BASE_URL", "https://console.mobiledgex.net")
|
||||
username := os.Getenv("EDGE_CONNECT_USERNAME")
|
||||
password := os.Getenv("EDGE_CONNECT_PASSWORD")
|
||||
|
||||
|
||||
var client *edgeconnect.Client
|
||||
if username != "" && password != "" {
|
||||
client = edgeconnect.NewClientWithCredentials(baseURL, username, password)
|
||||
} else {
|
||||
client = edgeconnect.NewClient(baseURL)
|
||||
}
|
||||
|
||||
|
||||
// 2. Application Layer: Create services with dependency injection (client implements repository interfaces)
|
||||
appService := services.NewAppService(client) // client implements AppRepository
|
||||
instanceService := services.NewAppInstanceService(client) // client implements AppInstanceRepository
|
||||
cloudletService := services.NewCloudletService(client) // client implements CloudletRepository
|
||||
|
||||
appService := app.NewService(client) // client implements AppRepository
|
||||
instanceService := instance.NewService(client) // client implements AppInstanceRepository
|
||||
cloudletService := cloudlet.NewService(client) // client implements CloudletRepository
|
||||
|
||||
// 3. Presentation Layer: Execute CLI driven adapters with injected services (simple parameter passing)
|
||||
cli.ExecuteWithServices(appService, instanceService, cloudletService)
|
||||
}
|
||||
|
|
|
|||
BIN
edge-connect-cli
BIN
edge-connect-cli
Binary file not shown.
|
|
@ -13,7 +13,7 @@ import (
|
|||
"time"
|
||||
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/driven/edgeconnect"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/apply"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/application/apply"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ func ExecuteWithServices(appSvc driving.AppService, instanceSvc driving.AppInsta
|
|||
InstanceService: instanceSvc,
|
||||
CloudletService: cloudletSvc,
|
||||
}
|
||||
|
||||
|
||||
Execute()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
package services
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driven"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driving"
|
||||
)
|
||||
|
||||
type appService struct {
|
||||
type service struct {
|
||||
appRepo driven.AppRepository
|
||||
}
|
||||
|
||||
func NewAppService(appRepo driven.AppRepository) driving.AppService {
|
||||
return &appService{appRepo: appRepo}
|
||||
func NewService(appRepo driven.AppRepository) driving.AppService {
|
||||
return &service{appRepo: appRepo}
|
||||
}
|
||||
|
||||
func (s *appService) CreateApp(ctx context.Context, region string, app *domain.App) error {
|
||||
func (s *service) CreateApp(ctx context.Context, region string, app *domain.App) error {
|
||||
// Validate inputs before delegating to repository
|
||||
if err := s.validateApp(app); err != nil {
|
||||
return err
|
||||
|
|
@ -39,7 +40,7 @@ func (s *appService) CreateApp(ctx context.Context, region string, app *domain.A
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *appService) ShowApp(ctx context.Context, region string, appKey domain.AppKey) (*domain.App, error) {
|
||||
func (s *service) ShowApp(ctx context.Context, region string, appKey domain.AppKey) (*domain.App, error) {
|
||||
if err := s.validateAppKey(appKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ func (s *appService) ShowApp(ctx context.Context, region string, appKey domain.A
|
|||
return app, nil
|
||||
}
|
||||
|
||||
func (s *appService) ShowApps(ctx context.Context, region string, appKey domain.AppKey) ([]domain.App, error) {
|
||||
func (s *service) ShowApps(ctx context.Context, region string, appKey domain.AppKey) ([]domain.App, error) {
|
||||
if region == "" {
|
||||
return nil, domain.ErrMissingRegion
|
||||
}
|
||||
|
|
@ -75,7 +76,7 @@ func (s *appService) ShowApps(ctx context.Context, region string, appKey domain.
|
|||
return apps, nil
|
||||
}
|
||||
|
||||
func (s *appService) DeleteApp(ctx context.Context, region string, appKey domain.AppKey) error {
|
||||
func (s *service) DeleteApp(ctx context.Context, region string, appKey domain.AppKey) error {
|
||||
if err := s.validateAppKey(appKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -96,7 +97,7 @@ func (s *appService) DeleteApp(ctx context.Context, region string, appKey domain
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *appService) UpdateApp(ctx context.Context, region string, app *domain.App) error {
|
||||
func (s *service) UpdateApp(ctx context.Context, region string, app *domain.App) error {
|
||||
if err := s.validateApp(app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -118,7 +119,7 @@ func (s *appService) UpdateApp(ctx context.Context, region string, app *domain.A
|
|||
}
|
||||
|
||||
// validateApp performs business logic validation on an app
|
||||
func (s *appService) validateApp(app *domain.App) error {
|
||||
func (s *service) validateApp(app *domain.App) error {
|
||||
if app == nil {
|
||||
return domain.NewDomainError(domain.ErrValidationFailed, "application cannot be nil")
|
||||
}
|
||||
|
|
@ -138,17 +139,17 @@ func (s *appService) validateApp(app *domain.App) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// validateAppKey validates an application key
|
||||
func (s *appService) validateAppKey(key domain.AppKey) error {
|
||||
if strings.TrimSpace(key.Organization) == "" {
|
||||
// validateAppKey performs business logic validation on an app key
|
||||
func (s *service) validateAppKey(appKey domain.AppKey) error {
|
||||
if strings.TrimSpace(appKey.Organization) == "" {
|
||||
return domain.ErrInvalidAppKey.WithDetails("organization is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(key.Name) == "" {
|
||||
if strings.TrimSpace(appKey.Name) == "" {
|
||||
return domain.ErrInvalidAppKey.WithDetails("name is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(key.Version) == "" {
|
||||
if strings.TrimSpace(appKey.Version) == "" {
|
||||
return domain.ErrInvalidAppKey.WithDetails("version is required")
|
||||
}
|
||||
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
package services
|
||||
package cloudlet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driven"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driving"
|
||||
)
|
||||
|
||||
type cloudletService struct {
|
||||
type service struct {
|
||||
cloudletRepo driven.CloudletRepository
|
||||
}
|
||||
|
||||
func NewCloudletService(cloudletRepo driven.CloudletRepository) driving.CloudletService {
|
||||
return &cloudletService{cloudletRepo: cloudletRepo}
|
||||
func NewService(cloudletRepo driven.CloudletRepository) driving.CloudletService {
|
||||
return &service{cloudletRepo: cloudletRepo}
|
||||
}
|
||||
|
||||
func (s *cloudletService) CreateCloudlet(ctx context.Context, region string, cloudlet *domain.Cloudlet) error {
|
||||
func (s *service) CreateCloudlet(ctx context.Context, region string, cloudlet *domain.Cloudlet) error {
|
||||
if err := s.validateCloudlet(cloudlet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ func (s *cloudletService) CreateCloudlet(ctx context.Context, region string, clo
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *cloudletService) ShowCloudlet(ctx context.Context, region string, cloudletKey domain.CloudletKey) (*domain.Cloudlet, error) {
|
||||
func (s *service) ShowCloudlet(ctx context.Context, region string, cloudletKey domain.CloudletKey) (*domain.Cloudlet, error) {
|
||||
if err := s.validateCloudletKey(cloudletKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -59,7 +60,7 @@ func (s *cloudletService) ShowCloudlet(ctx context.Context, region string, cloud
|
|||
return cloudlet, nil
|
||||
}
|
||||
|
||||
func (s *cloudletService) ShowCloudlets(ctx context.Context, region string, cloudletKey domain.CloudletKey) ([]domain.Cloudlet, error) {
|
||||
func (s *service) ShowCloudlets(ctx context.Context, region string, cloudletKey domain.CloudletKey) ([]domain.Cloudlet, error) {
|
||||
if region == "" {
|
||||
return nil, domain.ErrMissingRegion
|
||||
}
|
||||
|
|
@ -73,7 +74,7 @@ func (s *cloudletService) ShowCloudlets(ctx context.Context, region string, clou
|
|||
return cloudlets, nil
|
||||
}
|
||||
|
||||
func (s *cloudletService) DeleteCloudlet(ctx context.Context, region string, cloudletKey domain.CloudletKey) error {
|
||||
func (s *service) DeleteCloudlet(ctx context.Context, region string, cloudletKey domain.CloudletKey) error {
|
||||
if err := s.validateCloudletKey(cloudletKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -95,25 +96,21 @@ func (s *cloudletService) DeleteCloudlet(ctx context.Context, region string, clo
|
|||
}
|
||||
|
||||
// validateCloudlet performs business logic validation on a cloudlet
|
||||
func (s *cloudletService) validateCloudlet(cloudlet *domain.Cloudlet) error {
|
||||
func (s *service) validateCloudlet(cloudlet *domain.Cloudlet) error {
|
||||
if cloudlet == nil {
|
||||
return domain.NewDomainError(domain.ErrValidationFailed, "cloudlet cannot be nil")
|
||||
}
|
||||
|
||||
if err := s.validateCloudletKey(cloudlet.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.validateCloudletKey(cloudlet.Key)
|
||||
}
|
||||
|
||||
// validateCloudletKey validates a cloudlet key
|
||||
func (s *cloudletService) validateCloudletKey(key domain.CloudletKey) error {
|
||||
if strings.TrimSpace(key.Organization) == "" {
|
||||
// validateCloudletKey performs business logic validation on a cloudlet key
|
||||
func (s *service) validateCloudletKey(cloudletKey domain.CloudletKey) error {
|
||||
if strings.TrimSpace(cloudletKey.Organization) == "" {
|
||||
return domain.ErrInvalidCloudletKey.WithDetails("organization is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(key.Name) == "" {
|
||||
if strings.TrimSpace(cloudletKey.Name) == "" {
|
||||
return domain.ErrInvalidCloudletKey.WithDetails("name is required")
|
||||
}
|
||||
|
||||
|
|
@ -1,22 +1,23 @@
|
|||
package services
|
||||
package instance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driven"
|
||||
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driving"
|
||||
)
|
||||
|
||||
type appInstanceService struct {
|
||||
type service struct {
|
||||
appInstanceRepo driven.AppInstanceRepository
|
||||
}
|
||||
|
||||
func NewAppInstanceService(appInstanceRepo driven.AppInstanceRepository) driving.AppInstanceService {
|
||||
return &appInstanceService{appInstanceRepo: appInstanceRepo}
|
||||
func NewService(appInstanceRepo driven.AppInstanceRepository) driving.AppInstanceService {
|
||||
return &service{appInstanceRepo: appInstanceRepo}
|
||||
}
|
||||
|
||||
func (s *appInstanceService) CreateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
|
||||
func (s *service) CreateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
|
||||
if err := s.validateAppInstance(appInst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ func (s *appInstanceService) CreateAppInstance(ctx context.Context, region strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *appInstanceService) ShowAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) (*domain.AppInstance, error) {
|
||||
func (s *service) ShowAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) (*domain.AppInstance, error) {
|
||||
if err := s.validateAppInstanceKey(appInstKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -59,7 +60,7 @@ func (s *appInstanceService) ShowAppInstance(ctx context.Context, region string,
|
|||
return instance, nil
|
||||
}
|
||||
|
||||
func (s *appInstanceService) ShowAppInstances(ctx context.Context, region string, appInstKey domain.AppInstanceKey) ([]domain.AppInstance, error) {
|
||||
func (s *service) ShowAppInstances(ctx context.Context, region string, appInstKey domain.AppInstanceKey) ([]domain.AppInstance, error) {
|
||||
if region == "" {
|
||||
return nil, domain.ErrMissingRegion
|
||||
}
|
||||
|
|
@ -73,7 +74,7 @@ func (s *appInstanceService) ShowAppInstances(ctx context.Context, region string
|
|||
return instances, nil
|
||||
}
|
||||
|
||||
func (s *appInstanceService) DeleteAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
|
||||
func (s *service) DeleteAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
|
||||
if err := s.validateAppInstanceKey(appInstKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -94,7 +95,7 @@ func (s *appInstanceService) DeleteAppInstance(ctx context.Context, region strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *appInstanceService) UpdateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
|
||||
func (s *service) UpdateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
|
||||
if err := s.validateAppInstance(appInst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -105,17 +106,17 @@ func (s *appInstanceService) UpdateAppInstance(ctx context.Context, region strin
|
|||
|
||||
if err := s.appInstanceRepo.UpdateAppInstance(ctx, region, appInst); err != nil {
|
||||
if domain.IsNotFoundError(err) {
|
||||
return domain.NewInstanceError(domain.ErrResourceNotFound, "UpdateAppInstance", appInst.Key, region,
|
||||
"app instance does not exist")
|
||||
return domain.NewInstanceError(domain.ErrResourceConflict, "UpdateAppInstance", appInst.Key, region,
|
||||
"app instance may already exist or have conflicting configuration")
|
||||
}
|
||||
return domain.NewInstanceError(domain.ErrInternalError, "UpdateAppInstance", appInst.Key, region,
|
||||
return domain.NewInstanceError(domain.ErrInternalError, "UpdateAppInstance", appInst.Key, region,
|
||||
"failed to update app instance").WithDetails(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *appInstanceService) RefreshAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
|
||||
func (s *service) RefreshAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
|
||||
if err := s.validateAppInstanceKey(appInstKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -124,12 +125,19 @@ func (s *appInstanceService) RefreshAppInstance(ctx context.Context, region stri
|
|||
return domain.ErrMissingRegion
|
||||
}
|
||||
|
||||
if err := s.appInstanceRepo.RefreshAppInstance(ctx, region, appInstKey); err != nil {
|
||||
// Note: The driven port (repository) does not currently have a Refresh method.
|
||||
// This is a placeholder implementation.
|
||||
// To fully implement this, we would need to add RefreshAppInstance to the AppInstanceRepository interface
|
||||
// and implement it in the edgeconnect adapter.
|
||||
// For now, we can just return nil or a 'not implemented' error.
|
||||
// Let's delegate to the Show method as a temporary measure.
|
||||
_, err := s.appInstanceRepo.ShowAppInstance(ctx, region, appInstKey)
|
||||
if err != nil {
|
||||
if domain.IsNotFoundError(err) {
|
||||
return domain.NewInstanceError(domain.ErrResourceNotFound, "RefreshAppInstance", appInstKey, region,
|
||||
return domain.NewInstanceError(domain.ErrResourceNotFound, "RefreshAppInstance", appInstKey, region,
|
||||
"app instance does not exist")
|
||||
}
|
||||
return domain.NewInstanceError(domain.ErrInternalError, "RefreshAppInstance", appInstKey, region,
|
||||
return domain.NewInstanceError(domain.ErrInternalError, "RefreshAppInstance", appInstKey, region,
|
||||
"failed to refresh app instance").WithDetails(err.Error())
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +145,7 @@ func (s *appInstanceService) RefreshAppInstance(ctx context.Context, region stri
|
|||
}
|
||||
|
||||
// validateAppInstance performs business logic validation on an app instance
|
||||
func (s *appInstanceService) validateAppInstance(appInst *domain.AppInstance) error {
|
||||
func (s *service) validateAppInstance(appInst *domain.AppInstance) error {
|
||||
if appInst == nil {
|
||||
return domain.NewDomainError(domain.ErrValidationFailed, "app instance cannot be nil")
|
||||
}
|
||||
|
|
@ -154,22 +162,22 @@ func (s *appInstanceService) validateAppInstance(appInst *domain.AppInstance) er
|
|||
return nil
|
||||
}
|
||||
|
||||
// validateAppInstanceKey validates an app instance key
|
||||
func (s *appInstanceService) validateAppInstanceKey(key domain.AppInstanceKey) error {
|
||||
if strings.TrimSpace(key.Organization) == "" {
|
||||
// validateAppInstanceKey performs business logic validation on an app instance key
|
||||
func (s *service) validateAppInstanceKey(appInstKey domain.AppInstanceKey) error {
|
||||
if strings.TrimSpace(appInstKey.Organization) == "" {
|
||||
return domain.ErrInvalidInstanceKey.WithDetails("organization is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(key.Name) == "" {
|
||||
if strings.TrimSpace(appInstKey.Name) == "" {
|
||||
return domain.ErrInvalidInstanceKey.WithDetails("name is required")
|
||||
}
|
||||
|
||||
// Validate embedded cloudlet key
|
||||
if strings.TrimSpace(key.CloudletKey.Organization) == "" {
|
||||
if strings.TrimSpace(appInstKey.CloudletKey.Organization) == "" {
|
||||
return domain.ErrInvalidInstanceKey.WithDetails("cloudlet organization is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(key.CloudletKey.Name) == "" {
|
||||
if strings.TrimSpace(appInstKey.CloudletKey.Name) == "" {
|
||||
return domain.ErrInvalidInstanceKey.WithDetails("cloudlet name is required")
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue