refactor: Align Epic 0 & Epic 1 with true microservices architecture
Refactor core kernel and infrastructure to support true microservices architecture where services are independently deployable. Phase 1: Core Kernel Cleanup - Remove database provider from CoreModule (services create their own) - Update ProvideHealthRegistry to not depend on database - Add schema support to database client (NewClientWithSchema) - Update main entry point to remove database dependency - Core kernel now provides only: config, logger, error bus, health, metrics, tracer, service registry Phase 2: Service Registry Implementation - Create ServiceRegistry interface (pkg/registry/registry.go) - Implement Consul registry (internal/registry/consul/consul.go) - Add Consul dependency (github.com/hashicorp/consul/api) - Add registry configuration to config/default.yaml - Add ProvideServiceRegistry() to DI container Phase 3: Service Client Interfaces - Create service client interfaces: - pkg/services/auth.go - AuthServiceClient - pkg/services/identity.go - IdentityServiceClient - pkg/services/authz.go - AuthzServiceClient - pkg/services/audit.go - AuditServiceClient - Create ServiceClientFactory (internal/client/factory.go) - Create stub gRPC client implementations (internal/client/grpc/) - Add ProvideServiceClientFactory() to DI container Phase 4: gRPC Service Definitions - Create proto files for all core services: - api/proto/auth.proto - api/proto/identity.proto - api/proto/authz.proto - api/proto/audit.proto - Add generate-proto target to Makefile Phase 5: API Gateway Implementation - Create API Gateway service entry point (cmd/api-gateway/main.go) - Create Gateway implementation (services/gateway/gateway.go) - Add gateway configuration to config/default.yaml - Gateway registers with Consul and routes requests to backend services All code compiles successfully. Core services (Auth, Identity, Authz, Audit) will be implemented in Epic 2 using these foundations.
This commit is contained in:
156
cmd/api-gateway/main.go
Normal file
156
cmd/api-gateway/main.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// Package main provides the API Gateway service entry point.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.dcentral.systems/toolz/goplt/internal/client"
|
||||
"git.dcentral.systems/toolz/goplt/internal/di"
|
||||
"git.dcentral.systems/toolz/goplt/internal/health"
|
||||
"git.dcentral.systems/toolz/goplt/internal/metrics"
|
||||
"git.dcentral.systems/toolz/goplt/internal/server"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/config"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/errorbus"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/registry"
|
||||
"git.dcentral.systems/toolz/goplt/services/gateway"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create DI container with core kernel services
|
||||
container := di.NewContainer(
|
||||
// Invoke lifecycle hooks
|
||||
fx.Invoke(di.RegisterLifecycleHooks),
|
||||
// Create API Gateway
|
||||
fx.Invoke(func(
|
||||
cfg config.ConfigProvider,
|
||||
log logger.Logger,
|
||||
healthRegistry *health.Registry,
|
||||
metricsRegistry *metrics.Metrics,
|
||||
errorBus errorbus.ErrorPublisher,
|
||||
tracer trace.TracerProvider,
|
||||
serviceRegistry registry.ServiceRegistry,
|
||||
clientFactory *client.ServiceClientFactory,
|
||||
lc fx.Lifecycle,
|
||||
) {
|
||||
// Create HTTP server using server foundation
|
||||
srv, err := server.NewServer(cfg, log, healthRegistry, metricsRegistry, errorBus, tracer)
|
||||
if err != nil {
|
||||
log.Error("Failed to create API Gateway server",
|
||||
logger.Error(err),
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup gateway routes
|
||||
gateway, err := gateway.NewGateway(cfg, log, clientFactory, serviceRegistry)
|
||||
if err != nil {
|
||||
log.Error("Failed to create API Gateway",
|
||||
logger.Error(err),
|
||||
)
|
||||
os.Exit(1)
|
||||
}
|
||||
gateway.SetupRoutes(srv.Router())
|
||||
|
||||
// Register with Consul
|
||||
gatewayPort := cfg.GetInt("gateway.port")
|
||||
if gatewayPort == 0 {
|
||||
gatewayPort = 8080
|
||||
}
|
||||
gatewayHost := cfg.GetString("gateway.host")
|
||||
if gatewayHost == "" {
|
||||
gatewayHost = "localhost"
|
||||
}
|
||||
|
||||
serviceInstance := ®istry.ServiceInstance{
|
||||
ID: fmt.Sprintf("api-gateway-%d", os.Getpid()),
|
||||
Name: "api-gateway",
|
||||
Address: gatewayHost,
|
||||
Port: gatewayPort,
|
||||
Tags: []string{"gateway", "http"},
|
||||
Metadata: map[string]string{
|
||||
"version": "1.0.0",
|
||||
},
|
||||
}
|
||||
|
||||
// Register lifecycle hooks
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
// Register with service registry
|
||||
if err := serviceRegistry.Register(ctx, serviceInstance); err != nil {
|
||||
log.Warn("Failed to register API Gateway with service registry",
|
||||
logger.Error(err),
|
||||
)
|
||||
// Continue anyway - gateway can work without registry
|
||||
} else {
|
||||
log.Info("API Gateway registered with service registry",
|
||||
logger.String("service_id", serviceInstance.ID),
|
||||
)
|
||||
}
|
||||
|
||||
// Start HTTP server
|
||||
addr := fmt.Sprintf("%s:%d", cfg.GetString("server.host"), gatewayPort)
|
||||
log.Info("API Gateway starting",
|
||||
logger.String("addr", addr),
|
||||
)
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
if err := srv.Start(); err != nil && err != http.ErrServerClosed {
|
||||
log.Error("API Gateway server failed",
|
||||
logger.String("error", err.Error()),
|
||||
)
|
||||
errChan <- err
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait a short time to detect immediate binding errors
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return fmt.Errorf("API Gateway failed to start: %w", err)
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
log.Info("API Gateway started successfully",
|
||||
logger.String("addr", addr),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
// Deregister from service registry
|
||||
if err := serviceRegistry.Deregister(ctx, serviceInstance.ID); err != nil {
|
||||
log.Warn("Failed to deregister API Gateway from service registry",
|
||||
logger.Error(err),
|
||||
)
|
||||
} else {
|
||||
log.Info("API Gateway deregistered from service registry")
|
||||
}
|
||||
|
||||
// Shutdown HTTP server
|
||||
return srv.Shutdown(ctx)
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// Create root context
|
||||
ctx := context.Background()
|
||||
|
||||
// Start the application
|
||||
if err := container.Start(ctx); err != nil {
|
||||
log := logger.GetGlobalLogger()
|
||||
if log != nil {
|
||||
log.Error("Failed to start API Gateway",
|
||||
logger.Error(err),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Failed to start API Gateway: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -7,23 +7,28 @@ import (
|
||||
"os"
|
||||
|
||||
"git.dcentral.systems/toolz/goplt/internal/di"
|
||||
"git.dcentral.systems/toolz/goplt/internal/infra/database"
|
||||
"git.dcentral.systems/toolz/goplt/internal/server"
|
||||
"git.dcentral.systems/toolz/goplt/internal/health"
|
||||
"git.dcentral.systems/toolz/goplt/internal/metrics"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/config"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create DI container with lifecycle hooks
|
||||
// We need to invoke the HTTP server to ensure all providers execute
|
||||
// This is a minimal entry point for testing core kernel infrastructure
|
||||
// Services will have their own entry points (cmd/{service}/main.go)
|
||||
container := di.NewContainer(
|
||||
// Invoke lifecycle hooks
|
||||
fx.Invoke(di.RegisterLifecycleHooks),
|
||||
// Force HTTP server to be created (which triggers all dependencies)
|
||||
// This ensures database, health, metrics, etc. are all created
|
||||
fx.Invoke(func(_ *server.Server, _ *database.Client) {
|
||||
// Both server and database are created, hooks are registered
|
||||
// This ensures all providers execute
|
||||
// Verify core kernel services are available
|
||||
fx.Invoke(func(
|
||||
_ config.ConfigProvider,
|
||||
_ logger.Logger,
|
||||
_ *health.Registry,
|
||||
_ *metrics.Metrics,
|
||||
) {
|
||||
// Core kernel services are available
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user