From f6c38393b07985fb79656f9bffae151f8baa1d14 Mon Sep 17 00:00:00 2001 From: Primadi Setiawan Date: Sun, 30 Nov 2025 10:41:44 +0700 Subject: [PATCH] create lokstra_registry.RegisterServiceType and RegisterRouterServiceType. --- core/annotation/codegen.go | 22 +- .../multifile_test/zz_generated.lokstra.go | 396 -------- .../research/circular_dependency_test.go | 32 +- .../research/lazy_loading_proof_test.go | 8 - .../internal/test/registry_concrete_test.go | 5 +- .../test/registry_servicetype_test.go | 73 +- .../test/registry_string_factory_test.go | 8 +- core/deploy/internal/test/registry_test.go | 17 +- core/deploy/loader/builder.go | 4 +- core/deploy/registry.go | 123 +-- core/deploy/schema/lokstra.schema.json | 5 - core/deploy/schema/temp.txt | 844 ------------------ core/deploy/service_options.go | 161 ---- .../order/application/zz_cache.lokstra.json | 8 +- .../order/application/zz_generated.lokstra.go | 32 +- .../modules/order/register.go | 2 +- .../user/application/zz_cache.lokstra.json | 8 +- .../user/application/zz_generated.lokstra.go | 36 +- .../modules/user/register.go | 2 +- .../02-multi-deployment-yaml/register.go | 19 +- .../03-multi-deployment-pure-code/register.go | 24 +- .../04-external-services/main.go | 24 +- .../full-framework/05-remote-router/main.go | 6 +- .../06-inline-definitions-example/register.go | 10 - .../02-lazyload-vs-getservice/main.go | 2 +- .../examples/01-service-factories/main.go | 12 +- .../examples/02-remote-services/main.go | 12 +- .../examples/04-service-composition/main.go | 4 +- .../examples/05-service-architecture/main.go | 3 - .../02-service/examples/06-testing/main.go | 3 - .../examples/07-performance/main.go | 4 - .../examples/08-migration-pattern/main.go | 4 - .../examples/01-basic-config/main.go | 1 - .../04-config/examples/06-handlers/main.go | 1 - lokstra_registry/registry.go | 47 +- .../01_medium_system/register.go | 27 +- .../modules/order/register.go | 14 +- .../modules/user/register.go | 14 +- .../order/application/zz_cache.lokstra.json | 8 +- .../order/application/zz_generated.lokstra.go | 32 +- .../modules/order/register.go | 2 +- .../user/application/zz_cache.lokstra.json | 8 +- .../user/application/zz_generated.lokstra.go | 42 +- .../modules/user/register.go | 2 +- services/dbpool_pg/module.go | 3 +- services/kvstore_redis/module.go | 3 +- services/metrics_prometheus/module.go | 3 +- services/redis/module.go | 2 +- 48 files changed, 378 insertions(+), 1744 deletions(-) delete mode 100644 core/annotation/internal/multifile_test/zz_generated.lokstra.go delete mode 100644 core/deploy/schema/temp.txt diff --git a/core/annotation/codegen.go b/core/annotation/codegen.go index 44f19fd0..d0cb97d3 100644 --- a/core/annotation/codegen.go +++ b/core/annotation/codegen.go @@ -855,27 +855,25 @@ func {{$service.RemoteTypeName}}Factory(deps, config map[string]any) any { // - @Route annotations on methods func Register{{$service.StructName}}() { // Register service type with router configuration - lokstra_registry.RegisterServiceType("{{$service.ServiceName}}-factory", + lokstra_registry.RegisterRouterServiceType("{{$service.ServiceName}}-factory", {{$service.StructName}}Factory, {{$service.RemoteTypeName}}Factory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: {{quote $service.Prefix}}, Middlewares: []string{ {{range $i, $mw := $service.Middlewares}}{{if $i}}, {{end}}{{quote $mw}}{{end}} }, - CustomRoutes: map[string]string{ + RouteOverrides: map[string]deploy.RouteConfig{ {{- range $method := sortedKeys $service.Routes }} {{- $route := index $service.Routes $method }} - {{quote $method}}: {{quote $route}}, -{{- end }} - }, -{{- if gt (len $service.RouteMiddlewares) 0 }} - RouteMiddlewares: map[string][]string{ -{{- range $method := sortedKeys $service.RouteMiddlewares }} {{- $middlewares := index $service.RouteMiddlewares $method }} - {{quote $method}}: { {{range $i, $mw := $middlewares}}{{if $i}}, {{end}}{{quote $mw}}{{end}} }, + {{quote $method}}: { + Path: {{quote $route}}, +{{- if $middlewares }} + Middlewares: []string{ {{range $i, $mw := $middlewares}}{{if $i}}, {{end}}{{quote $mw}}{{end}} }, {{- end }} - }, + }, {{- end }} - }), + }, + }, ) // Register lazy service with auto-detected dependencies diff --git a/core/annotation/internal/multifile_test/zz_generated.lokstra.go b/core/annotation/internal/multifile_test/zz_generated.lokstra.go deleted file mode 100644 index 80b43ae8..00000000 --- a/core/annotation/internal/multifile_test/zz_generated.lokstra.go +++ /dev/null @@ -1,396 +0,0 @@ -// AUTO-GENERATED CODE - DO NOT EDIT -// Generated by lokstra-annotation from annotations in this folder -// Annotations: @RouterService, @Inject, @Route - -package main - -import ( - "github.com/primadi/lokstra/core/deploy" - "github.com/primadi/lokstra/core/proxy" - "github.com/primadi/lokstra/lokstra_registry" - authdomain "github.com/primadi/lokstra/core/annotation/internal/multifile_test/authdomain" - userdomain "github.com/primadi/lokstra/core/annotation/internal/multifile_test/userdomain" - service "github.com/primadi/lokstra/core/service" -) - -// Auto-register on package import -func init() { - RegisterAuthService() - RegisterConfigEagerService() - RegisterConfigService() - RegisterProfileService() - RegisterUserService() -} - -// ============================================================ -// FILE: auth_service.go -// ============================================================ - -// AuthServiceRemote implements AuthServiceInterface with HTTP proxy -// Auto-generated from AuthService interface methods -type AuthServiceRemote struct { - proxyService *proxy.Service -} - -// NewAuthServiceRemote creates a new remote auth-service proxy -func NewAuthServiceRemote(proxyService *proxy.Service) *AuthServiceRemote { - return &AuthServiceRemote{ - proxyService: proxyService, - } -} - -// Login via HTTP -// Generated from: @Route "POST /login" -func (s *AuthServiceRemote) Login(p *authdomain.LoginRequest) (*authdomain.AuthResponse, error) { - return proxy.CallWithData[*authdomain.AuthResponse](s.proxyService, "Login", p) -} - -// Logout via HTTP -// Generated from: @Route "POST /logout" -func (s *AuthServiceRemote) Logout(p *authdomain.LogoutRequest) (*authdomain.AuthResponse, error) { - return proxy.CallWithData[*authdomain.AuthResponse](s.proxyService, "Logout", p) -} - - -func AuthServiceFactory(deps map[string]any, config map[string]any) any { - return &AuthService{ - AuthRepo: deps["auth-repo"].(*service.Cached[any]), - } -} - -// AuthServiceRemoteFactory creates a remote HTTP client for AuthServiceInterface -// Auto-generated from @RouterService annotation -func AuthServiceRemoteFactory(deps, config map[string]any) any { - proxyService, ok := config["remote"].(*proxy.Service) - if !ok { - panic("remote factory requires 'remote' (proxy.Service) in config") - } - return NewAuthServiceRemote(proxyService) -} - -// RegisterAuthService registers the auth-service with the registry -// Auto-generated from annotations: -// - @RouterService name="auth-service", prefix="/api/v1/auth" -// - @Inject annotations -// - @Route annotations on methods -func RegisterAuthService() { - // Register service type with router configuration - lokstra_registry.RegisterServiceType("auth-service-factory", - AuthServiceFactory, - AuthServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - PathPrefix: "/api/v1/auth", - Middlewares: []string{ }, - CustomRoutes: map[string]string{ - "Login": "POST /login", - "Logout": "POST /logout", - }, - }), - ) - - // Register lazy service with auto-detected dependencies - lokstra_registry.RegisterLazyService("auth-service", - "auth-service-factory", - map[string]any{ - "depends-on": []string{ "auth-repo", }, - }) -} - -// ============================================================ -// FILE: config_eager_service.go -// ============================================================ - -// ConfigEagerServiceRemote implements ConfigEagerServiceInterface with HTTP proxy -// Auto-generated from ConfigEagerService interface methods -type ConfigEagerServiceRemote struct { - proxyService *proxy.Service -} - -// NewConfigEagerServiceRemote creates a new remote config-eager-service proxy -func NewConfigEagerServiceRemote(proxyService *proxy.Service) *ConfigEagerServiceRemote { - return &ConfigEagerServiceRemote{ - proxyService: proxyService, - } -} - -// GetStatus via HTTP -// Generated from: @Route "GET /status" -func (s *ConfigEagerServiceRemote) GetStatus() (any, error) { - return proxy.CallWithData[any](s.proxyService, "GetStatus", nil) -} - - -func ConfigEagerServiceFactory(deps map[string]any, config map[string]any) any { - return &ConfigEagerService{ - DB: deps["database"].(*DBPool), - Redis: deps["redis"].(*RedisClient), - UserRepo: deps["user-repository"].(*service.Cached[any]), - } -} - -// ConfigEagerServiceRemoteFactory creates a remote HTTP client for ConfigEagerServiceInterface -// Auto-generated from @RouterService annotation -func ConfigEagerServiceRemoteFactory(deps, config map[string]any) any { - proxyService, ok := config["remote"].(*proxy.Service) - if !ok { - panic("remote factory requires 'remote' (proxy.Service) in config") - } - return NewConfigEagerServiceRemote(proxyService) -} - -// RegisterConfigEagerService registers the config-eager-service with the registry -// Auto-generated from annotations: -// - @RouterService name="config-eager-service", prefix="/api/v1/config" -// - @Inject annotations -// - @Route annotations on methods -func RegisterConfigEagerService() { - // Register service type with router configuration - lokstra_registry.RegisterServiceType("config-eager-service-factory", - ConfigEagerServiceFactory, - ConfigEagerServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - PathPrefix: "/api/v1/config", - Middlewares: []string{ }, - CustomRoutes: map[string]string{ - "GetStatus": "GET /status", - }, - }), - ) - - // Register lazy service with auto-detected dependencies - lokstra_registry.RegisterLazyService("config-eager-service", - "config-eager-service-factory", - map[string]any{ - "depends-on": []string{ "database", "redis", "user-repository", }, - }) -} - -// ============================================================ -// FILE: config_service.go -// ============================================================ - -// ConfigServiceRemote implements ConfigServiceInterface with HTTP proxy -// Auto-generated from ConfigService interface methods -type ConfigServiceRemote struct { - proxyService *proxy.Service -} - -// NewConfigServiceRemote creates a new remote config-service proxy -func NewConfigServiceRemote(proxyService *proxy.Service) *ConfigServiceRemote { - return &ConfigServiceRemote{ - proxyService: proxyService, - } -} - -// GetSettings via HTTP -// Generated from: @Route "GET /settings" -func (s *ConfigServiceRemote) GetSettings() (any, error) { - return proxy.CallWithData[any](s.proxyService, "GetSettings", nil) -} - -// GetVersion via HTTP -// Generated from: @Route "GET /version" -func (s *ConfigServiceRemote) GetVersion() (string, error) { - return proxy.CallWithData[string](s.proxyService, "GetVersion", nil) -} - -// Reload via HTTP -// Generated from: @Route "POST /reload" -func (s *ConfigServiceRemote) Reload() error { - return proxy.Call(s.proxyService, "Reload", nil) -} - -func ConfigServiceFactory(deps map[string]any, config map[string]any) any { - return &ConfigService{ - ConfigRepo: deps["config-repo"].(*service.Cached[any]), - } -} - -// ConfigServiceRemoteFactory creates a remote HTTP client for ConfigServiceInterface -// Auto-generated from @RouterService annotation -func ConfigServiceRemoteFactory(deps, config map[string]any) any { - proxyService, ok := config["remote"].(*proxy.Service) - if !ok { - panic("remote factory requires 'remote' (proxy.Service) in config") - } - return NewConfigServiceRemote(proxyService) -} - -// RegisterConfigService registers the config-service with the registry -// Auto-generated from annotations: -// - @RouterService name="config-service", prefix="/api/config" -// - @Inject annotations -// - @Route annotations on methods -func RegisterConfigService() { - // Register service type with router configuration - lokstra_registry.RegisterServiceType("config-service-factory", - ConfigServiceFactory, - ConfigServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - PathPrefix: "/api/config", - Middlewares: []string{ }, - CustomRoutes: map[string]string{ - "GetSettings": "GET /settings", - "GetVersion": "GET /version", - "Reload": "POST /reload", - }, - }), - ) - - // Register lazy service with auto-detected dependencies - lokstra_registry.RegisterLazyService("config-service", - "config-service-factory", - map[string]any{ - "depends-on": []string{ "config-repo", }, - }) -} - -// ============================================================ -// FILE: profile_service.go -// ============================================================ - -// ProfileServiceRemote implements ProfileServiceInterface with HTTP proxy -// Auto-generated from ProfileService interface methods -type ProfileServiceRemote struct { - proxyService *proxy.Service -} - -// NewProfileServiceRemote creates a new remote profile-service proxy -func NewProfileServiceRemote(proxyService *proxy.Service) *ProfileServiceRemote { - return &ProfileServiceRemote{ - proxyService: proxyService, - } -} - -// GetByID via HTTP -// Generated from: @Route "GET /profiles/{id}" -func (s *ProfileServiceRemote) GetByID(p string) (string, error) { - return proxy.CallWithData[string](s.proxyService, "GetByID", p) -} - -// Update via HTTP -// Generated from: @Route "PUT /profiles/{id}" -func (s *ProfileServiceRemote) Update(p string) error { - return proxy.Call(s.proxyService, "Update", p) -} - - -func ProfileServiceFactory(deps map[string]any, config map[string]any) any { - return &ProfileService{ - ProfileRepo: deps["profile-repo"].(*service.Cached[any]), - } -} - -// ProfileServiceRemoteFactory creates a remote HTTP client for ProfileServiceInterface -// Auto-generated from @RouterService annotation -func ProfileServiceRemoteFactory(deps, config map[string]any) any { - proxyService, ok := config["remote"].(*proxy.Service) - if !ok { - panic("remote factory requires 'remote' (proxy.Service) in config") - } - return NewProfileServiceRemote(proxyService) -} - -// RegisterProfileService registers the profile-service with the registry -// Auto-generated from annotations: -// - @RouterService name="profile-service", prefix="/api/profiles" -// - @Inject annotations -// - @Route annotations on methods -func RegisterProfileService() { - // Register service type with router configuration - lokstra_registry.RegisterServiceType("profile-service-factory", - ProfileServiceFactory, - ProfileServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - PathPrefix: "/api/profiles", - Middlewares: []string{ }, - CustomRoutes: map[string]string{ - "GetByID": "GET /profiles/{id}", - "Update": "PUT /profiles/{id}", - }, - }), - ) - - // Register lazy service with auto-detected dependencies - lokstra_registry.RegisterLazyService("profile-service", - "profile-service-factory", - map[string]any{ - "depends-on": []string{ "profile-repo", }, - }) -} - - -// ============================================================ -// FILE: user_service.go -// ============================================================ - -// UserServiceRemote implements UserServiceInterface with HTTP proxy -// Auto-generated from UserService interface methods -type UserServiceRemote struct { - proxyService *proxy.Service -} - -// NewUserServiceRemote creates a new remote user-service proxy -func NewUserServiceRemote(proxyService *proxy.Service) *UserServiceRemote { - return &UserServiceRemote{ - proxyService: proxyService, - } -} - -// Create via HTTP -// Generated from: @Route "POST /users" -func (s *UserServiceRemote) Create(p *userdomain.CreateUserRequest) (*userdomain.UserDTO, error) { - return proxy.CallWithData[*userdomain.UserDTO](s.proxyService, "Create", p) -} - -// GetByID via HTTP -// Generated from: @Route "GET /users/{id}" -func (s *UserServiceRemote) GetByID(p string) (*userdomain.UserDTO, error) { - return proxy.CallWithData[*userdomain.UserDTO](s.proxyService, "GetByID", p) -} - - -func UserServiceFactory(deps map[string]any, config map[string]any) any { - return &UserService{ - UserRepo: deps["user-repo"].(*service.Cached[any]), - } -} - -// UserServiceRemoteFactory creates a remote HTTP client for UserServiceInterface -// Auto-generated from @RouterService annotation -func UserServiceRemoteFactory(deps, config map[string]any) any { - proxyService, ok := config["remote"].(*proxy.Service) - if !ok { - panic("remote factory requires 'remote' (proxy.Service) in config") - } - return NewUserServiceRemote(proxyService) -} - -// RegisterUserService registers the user-service with the registry -// Auto-generated from annotations: -// - @RouterService name="user-service", prefix="/api/v1/users" -// - @Inject annotations -// - @Route annotations on methods -func RegisterUserService() { - // Register service type with router configuration - lokstra_registry.RegisterServiceType("user-service-factory", - UserServiceFactory, - UserServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - PathPrefix: "/api/v1/users", - Middlewares: []string{ }, - CustomRoutes: map[string]string{ - "Create": "POST /users", - "GetByID": "GET /users/{id}", - }, - }), - ) - - // Register lazy service with auto-detected dependencies - lokstra_registry.RegisterLazyService("user-service", - "user-service-factory", - map[string]any{ - "depends-on": []string{ "user-repo", }, - }) -} - diff --git a/core/deploy/internal/research/circular_dependency_test.go b/core/deploy/internal/research/circular_dependency_test.go index e6582fef..4ae9a606 100644 --- a/core/deploy/internal/research/circular_dependency_test.go +++ b/core/deploy/internal/research/circular_dependency_test.go @@ -21,9 +21,7 @@ func TestCircularDependency_StillCrashes(t *testing.T) { }{ B: service.Cast[any](deps["service-b"]), } - }, - nil, - ) + }) // Service B depends on A (CIRCULAR!) reg.RegisterServiceType("service-b-factory", @@ -33,9 +31,7 @@ func TestCircularDependency_StillCrashes(t *testing.T) { }{ A: service.Cast[any](deps["service-a"]), } - }, - nil, - ) + }) // Register with circular dependencies reg.RegisterLazyService("service-a", "service-a-factory", map[string]any{ @@ -79,9 +75,7 @@ func TestCached_UsefulForConditionalLoading(t *testing.T) { reg.RegisterServiceType("cache-factory", func(deps, cfg map[string]any) any { return &struct{ Name string }{Name: "Cache"} - }, - nil, - ) + }) // Expensive service reg.RegisterServiceType("expensive-api-factory", @@ -89,9 +83,7 @@ func TestCached_UsefulForConditionalLoading(t *testing.T) { expensiveAPICalled = true t.Logf("💰 Expensive API factory called") return &struct{ Name string }{Name: "ExpensiveAPI"} - }, - nil, - ) + }) // Analytics service with LAZY dependencies reg.RegisterServiceType("analytics-factory", @@ -103,9 +95,7 @@ func TestCached_UsefulForConditionalLoading(t *testing.T) { Cache: service.Cast[any](deps["cache"]), ExpensiveAPI: service.Cast[any](deps["expensive-api"]), } - }, - nil, - ) + }) reg.RegisterLazyService("cache", "cache-factory", nil) reg.RegisterLazyService("expensive-api", "expensive-api-factory", nil) @@ -141,18 +131,14 @@ func TestEagerInjection_WorksNormally(t *testing.T) { func(deps, cfg map[string]any) any { paymentAPICalled = true return &struct{ Name string }{Name: "PaymentAPI"} - }, - nil, - ) + }) // Email Service reg.RegisterServiceType("email-service-factory", func(deps, cfg map[string]any) any { emailSvcCalled = true return &struct{ Name string }{Name: "EmailService"} - }, - nil, - ) + }) // Order service with EAGER dependencies (no service.Cached) type OrderService struct { @@ -166,9 +152,7 @@ func TestEagerInjection_WorksNormally(t *testing.T) { PaymentAPI: deps["payment-api"], // Direct, no Cast EmailSvc: deps["email-service"], // Direct, no Cast } - }, - nil, - ) + }) reg.RegisterLazyService("payment-api", "payment-api-factory", nil) reg.RegisterLazyService("email-service", "email-service-factory", nil) diff --git a/core/deploy/internal/research/lazy_loading_proof_test.go b/core/deploy/internal/research/lazy_loading_proof_test.go index ba1c8030..a23c4c42 100644 --- a/core/deploy/internal/research/lazy_loading_proof_test.go +++ b/core/deploy/internal/research/lazy_loading_proof_test.go @@ -19,7 +19,6 @@ func TestLazyLoadingProof_OnDemandCreation(t *testing.T) { callLog = append(callLog, "service-a-factory") return &struct{ Name string }{Name: "ServiceA"} }, - nil, ) reg.RegisterServiceType("service-b-factory", @@ -27,7 +26,6 @@ func TestLazyLoadingProof_OnDemandCreation(t *testing.T) { callLog = append(callLog, "service-b-factory") return &struct{ Name string }{Name: "ServiceB"} }, - nil, ) reg.RegisterServiceType("service-c-factory", @@ -35,7 +33,6 @@ func TestLazyLoadingProof_OnDemandCreation(t *testing.T) { callLog = append(callLog, "service-c-factory") return &struct{ Name string }{Name: "ServiceC"} }, - nil, ) // Register lazy services @@ -122,7 +119,6 @@ func TestLazyLoadingProof_DependencyResolution(t *testing.T) { resolutionOrder = append(resolutionOrder, "repository") return &struct{ Name string }{Name: "Repository"} }, - nil, ) // Register service (depends on repository) @@ -131,7 +127,6 @@ func TestLazyLoadingProof_DependencyResolution(t *testing.T) { resolutionOrder = append(resolutionOrder, "service") return &struct{ Name string }{Name: "Service"} }, - nil, ) // Register as lazy services @@ -204,7 +199,6 @@ func TestLazyLoadingProof_UnusedDependencies(t *testing.T) { createdServices = append(createdServices, "service-a") return &struct{ Name string }{Name: "ServiceA"} }, - nil, ) // Service B depends on Service A @@ -213,7 +207,6 @@ func TestLazyLoadingProof_UnusedDependencies(t *testing.T) { createdServices = append(createdServices, "service-b") return &struct{ Name string }{Name: "ServiceB"} }, - nil, ) // Service C depends on Service B (transitive dependency on A) @@ -222,7 +215,6 @@ func TestLazyLoadingProof_UnusedDependencies(t *testing.T) { createdServices = append(createdServices, "service-c") return &struct{ Name string }{Name: "ServiceC"} }, - nil, ) // Register lazy services diff --git a/core/deploy/internal/test/registry_concrete_test.go b/core/deploy/internal/test/registry_concrete_test.go index 1b6436a2..e6b7b53c 100644 --- a/core/deploy/internal/test/registry_concrete_test.go +++ b/core/deploy/internal/test/registry_concrete_test.go @@ -36,19 +36,16 @@ func TestNormalizeServiceFactory_ConcreteReturnTypes(t *testing.T) { // Mode 1: func() *TestService (concrete return type!) g.RegisterServiceType("test-service-1", NewTestService, // func() *TestService - nil, ) // Mode 2: func(cfg map[string]any) *TestService g.RegisterServiceType("test-service-2", NewTestServiceWithConfig, - nil, ) // Mode 3: func(deps, cfg map[string]any) *TestService g.RegisterServiceType("test-service-3", NewTestServiceFull, - nil, ) // Test retrieval and invocation @@ -128,7 +125,7 @@ func TestNormalizeServiceFactory_InvalidSignatures(t *testing.T) { t.Errorf("expected panic for %s", tc.name) } }() - g.RegisterServiceType("invalid-"+tc.name, tc.factory, nil) + g.RegisterServiceType("invalid-"+tc.name, tc.factory) }) } } diff --git a/core/deploy/internal/test/registry_servicetype_test.go b/core/deploy/internal/test/registry_servicetype_test.go index 5c040900..4603d254 100644 --- a/core/deploy/internal/test/registry_servicetype_test.go +++ b/core/deploy/internal/test/registry_servicetype_test.go @@ -6,38 +6,41 @@ import ( "github.com/primadi/lokstra/core/deploy" ) -// Test RegisterServiceType with all three factory signatures -func TestRegisterServiceType_ThreeModes(t *testing.T) { +// Test RegisterRouterServiceType with all three factory signatures +func TestRegisterRouterServiceType_ThreeModes(t *testing.T) { g := deploy.NewGlobalRegistry() // Mode 1: func() any (simplest) - g.RegisterServiceType("simple-service", + g.RegisterRouterServiceType("simple-service", func() any { return "simple-local" }, func() any { return "simple-remote" }, + nil, ) // Mode 2: func(cfg map[string]any) any (config only) - g.RegisterServiceType("config-service", + g.RegisterRouterServiceType("config-service", func(cfg map[string]any) any { return "local-" + cfg["env"].(string) }, func(cfg map[string]any) any { return "remote-" + cfg["env"].(string) }, + nil, ) // Mode 3: func(deps, cfg map[string]any) any (full control) - g.RegisterServiceType("full-service", + g.RegisterRouterServiceType("full-service", func(deps, cfg map[string]any) any { return "full-local-" + cfg["mode"].(string) }, func(deps, cfg map[string]any) any { return "full-remote-" + cfg["mode"].(string) }, + nil, ) // Test local factory retrieval @@ -75,15 +78,44 @@ func TestRegisterServiceType_ThreeModes(t *testing.T) { } } -// Test RegisterServiceType with nil remote factory -func TestRegisterServiceType_NilRemote(t *testing.T) { +// Test RegisterServiceType simple signature (infrastructure services) +func TestRegisterServiceType_Simple(t *testing.T) { + g := deploy.NewGlobalRegistry() + + // Simple infrastructure service registration + g.RegisterServiceType("db-pool", + func(cfg map[string]any) any { + return "db-pool-instance" + }, + ) + + local := g.GetServiceFactory("db-pool", true) + if local == nil { + t.Fatal("db-pool factory not found") + } + + result := local(nil, map[string]any{"dsn": "test"}) + if result != "db-pool-instance" { + t.Errorf("expected 'db-pool-instance', got '%v'", result) + } + + // Remote should be nil for simple registration + remote := g.GetServiceFactory("db-pool", false) + if remote != nil { + t.Error("remote factory should be nil for simple RegisterServiceType") + } +} + +// Test RegisterRouterServiceType with nil remote factory +func TestRegisterRouterServiceType_NilRemote(t *testing.T) { g := deploy.NewGlobalRegistry() - g.RegisterServiceType("local-only", + g.RegisterRouterServiceType("local-only", func() any { return "local-service" }, nil, // No remote factory + nil, // No config ) local := g.GetServiceFactory("local-only", true) @@ -97,17 +129,19 @@ func TestRegisterServiceType_NilRemote(t *testing.T) { } } -// Test RegisterServiceType with metadata -func TestRegisterServiceType_WithMetadata(t *testing.T) { +// Test RegisterRouterServiceType with metadata +func TestRegisterRouterServiceType_WithMetadata(t *testing.T) { g := deploy.NewGlobalRegistry() - g.RegisterServiceType("user-service", + g.RegisterRouterServiceType("user-service", func() any { return "user-service-instance" }, nil, - deploy.WithResource("user", "users"), - deploy.WithConvention("rest"), + &deploy.ServiceTypeConfig{ + PathPrefix: "/api/users", + Middlewares: []string{"auth", "cors"}, + }, ) metadata := g.GetServiceMetadata("user-service") @@ -115,16 +149,12 @@ func TestRegisterServiceType_WithMetadata(t *testing.T) { t.Fatal("metadata not found") } - if metadata.Resource != "user" { - t.Errorf("expected resource 'user', got '%s'", metadata.Resource) + if metadata.PathPrefix != "/api/users" { + t.Errorf("expected PathPrefix '/api/users', got '%s'", metadata.PathPrefix) } - if metadata.ResourcePlural != "users" { - t.Errorf("expected resource plural 'users', got '%s'", metadata.ResourcePlural) - } - - if metadata.Convention != "rest" { - t.Errorf("expected convention 'rest', got '%s'", metadata.Convention) + if len(metadata.MiddlewareNames) != 2 { + t.Errorf("expected 2 middlewares, got %d", len(metadata.MiddlewareNames)) } } @@ -143,6 +173,5 @@ func TestRegisterServiceType_InvalidSignature(t *testing.T) { func(s string) any { return s }, - nil, ) } diff --git a/core/deploy/internal/test/registry_string_factory_test.go b/core/deploy/internal/test/registry_string_factory_test.go index 6e122a04..4ed97ee2 100644 --- a/core/deploy/internal/test/registry_string_factory_test.go +++ b/core/deploy/internal/test/registry_string_factory_test.go @@ -14,7 +14,7 @@ func TestRegisterLazyService_StringFactoryType(t *testing.T) { reg := deploy.NewGlobalRegistry() // Register a factory type first - reg.RegisterServiceType("user-factory", + reg.RegisterRouterServiceType("user-factory", func(deps, cfg map[string]any) any { return map[string]any{ "type": "user-service", @@ -24,9 +24,7 @@ func TestRegisterLazyService_StringFactoryType(t *testing.T) { }, nil, // No remote factory &deploy.ServiceTypeConfig{ - Resource: "User", - ResourcePlural: "Users", - Convention: "rest", + PathPrefix: "/api/users", }, ) @@ -37,7 +35,6 @@ func TestRegisterLazyService_StringFactoryType(t *testing.T) { "type": "user-repository", } }, - nil, ) // Register dependency first @@ -166,7 +163,6 @@ func TestRegisterLazyService_YAMLEquivalence(t *testing.T) { "maxOrd": cfg["max-orders"], } }, - nil, ) // Simulate YAML loading (what loader/builder.go does) diff --git a/core/deploy/internal/test/registry_test.go b/core/deploy/internal/test/registry_test.go index 16c4b95d..81589be3 100644 --- a/core/deploy/internal/test/registry_test.go +++ b/core/deploy/internal/test/registry_test.go @@ -158,7 +158,7 @@ func TestGlobalRegistry_FactoryRegistration(t *testing.T) { } // Register service type - reg.RegisterServiceType("test-service", localFactory, remoteFactory) + reg.RegisterRouterServiceType("test-service", localFactory, remoteFactory, nil) // Get local factory local := reg.GetServiceFactory("test-service", true) @@ -217,14 +217,21 @@ func TestGlobalSingleton(t *testing.T) { } // Test registration via global - reg1.RegisterServiceType("test", nil, nil) + reg1.RegisterServiceType("test", func() any { return nil }) // Should be accessible from reg2 factory := reg2.GetServiceFactory("test", true) - if factory != nil { - t.Error("expected nil factory (we registered nil)") + if factory == nil { + t.Fatal("expected factory to be registered") + } + + // Factory should return nil when called + result := factory(nil, nil) + if result != nil { + t.Errorf("expected factory to return nil, got %v", result) } - // But the type should be registered + + // Remote factory should be nil (we didn't register one) if reg2.GetServiceFactory("test", false) != nil { t.Error("expected nil remote factory") } diff --git a/core/deploy/loader/builder.go b/core/deploy/loader/builder.go index 0b597bbd..552be573 100644 --- a/core/deploy/loader/builder.go +++ b/core/deploy/loader/builder.go @@ -703,9 +703,7 @@ func RegisterDefinitionsForRuntime(registry *deploy.GlobalRegistry, config *sche } // Check if metadata has router configuration - hasRouterConfig := metadata.Resource != "" || - len(metadata.RouteOverrides) > 0 || - metadata.PathPrefix != "" + hasRouterConfig := len(metadata.RouteOverrides) > 0 || metadata.PathPrefix != "" if !hasRouterConfig { // Skip services without router configuration diff --git a/core/deploy/registry.go b/core/deploy/registry.go index ad0860bc..679464ce 100644 --- a/core/deploy/registry.go +++ b/core/deploy/registry.go @@ -93,9 +93,6 @@ type LazyServiceEntry struct { // ServiceMetadata holds metadata for a service type registration // Can be populated from ServiceTypeConfig or legacy functional options type ServiceMetadata struct { - Resource string // Singular resource name (e.g., "user") - ResourcePlural string // Plural resource name (e.g., "users") - Convention string // Convention type (e.g., "rest", "rpc") PathPrefix string // Path prefix for all routes MiddlewareNames []string // Router-level middleware names HiddenMethods []string // Methods to hide from router @@ -186,24 +183,24 @@ func NewGlobalRegistry() *GlobalRegistry { // ===== FACTORY REGISTRATION (CODE) ===== -// RegisterServiceType registers a service factory with configuration -// Supports two patterns: -// -// 1. Struct-based (recommended): -// RegisterServiceType(type, local, remote, &ServiceTypeConfig{...}) -// -// 2. Functional options (legacy): -// RegisterServiceType(type, local, remote, WithResource(...), WithConvention(...)) -// // Factory signatures (auto-wrapped by framework): // - func(deps, cfg map[string]any) any - full control (canonical) // - func(cfg map[string]any) any - only config // - func() any - no params // // Both local and remote factories support all three signatures. -func (g *GlobalRegistry) RegisterServiceType(serviceType string, local, remote any, - configOrOptions ...any) { - LogDebug("[RegisterServiceType CALLED] serviceType=%s, options count=%d", serviceType, len(configOrOptions)) +// RegisterRouterServiceType registers a service type with HTTP routing configuration. +// Use this for services that expose HTTP endpoints (annotated with @RouterService). +// For simple infrastructure services (DB, Redis, etc), use RegisterServiceType instead. +// +// Parameters: +// - serviceType: Unique identifier for this service type +// - local: Factory for local deployment (same process) +// - remote: Factory for remote deployment (HTTP client) +// - config: Optional routing configuration (path prefix, middlewares, route overrides) +func (g *GlobalRegistry) RegisterRouterServiceType(serviceType string, local, remote any, + config *ServiceTypeConfig) { + LogDebug("[RegisterRouterServiceType CALLED] serviceType=%s", serviceType) g.mu.Lock() defer g.mu.Unlock() @@ -212,72 +209,30 @@ func (g *GlobalRegistry) RegisterServiceType(serviceType string, local, remote a panic(fmt.Sprintf("service type %s already registered", serviceType)) } - // Build metadata from config or options - metadata := &ServiceMetadata{ - Convention: "rest", // Default convention - } - - // Check if first argument is ServiceTypeConfig - if len(configOrOptions) > 0 { - if config, ok := configOrOptions[0].(*ServiceTypeConfig); ok { - // Struct-based configuration (new pattern) - if config != nil { - metadata.Resource = config.Resource - metadata.ResourcePlural = config.ResourcePlural - metadata.Convention = config.Convention - metadata.PathPrefix = config.PathPrefix - metadata.MiddlewareNames = config.Middlewares - metadata.HiddenMethods = config.Hidden - - // Convert RouteConfig to RouteMetadata - if len(config.RouteOverrides) > 0 { - metadata.RouteOverrides = make(map[string]RouteMetadata) - for methodName, routeConfig := range config.RouteOverrides { - metadata.RouteOverrides[methodName] = RouteMetadata(routeConfig) - } - } - } - } else { - // Functional options (legacy pattern) - for _, opt := range configOrOptions { - if optFunc, ok := opt.(RegisterServiceTypeOption); ok { - optFunc(metadata) - } - } - } - } - - // Infer Resource from serviceType if not provided - if metadata.Resource == "" { - // Auto-generate from service type: "order-service-factory" -> "order" - resource := strings.TrimSuffix(serviceType, "-factory") - resource = strings.TrimSuffix(resource, "-service") - if resource != "" && resource != serviceType { - metadata.Resource = resource - LogDebug("[RegisterServiceType] %s: Inferred Resource=%s", serviceType, resource) - } - } + // Build metadata from config + metadata := &ServiceMetadata{} - // Infer ResourcePlural if Resource is set but ResourcePlural is empty - if metadata.Resource != "" && metadata.ResourcePlural == "" { - metadata.ResourcePlural = metadata.Resource + "s" - LogDebug("[RegisterServiceType] %s: Inferred ResourcePlural=%s", serviceType, metadata.ResourcePlural) - } + if config != nil { + metadata.PathPrefix = config.PathPrefix + metadata.MiddlewareNames = config.Middlewares + metadata.HiddenMethods = config.Hidden - // Set default convention if not provided - if metadata.Convention == "" { - metadata.Convention = "rest" + // Convert RouteConfig to RouteMetadata + if len(config.RouteOverrides) > 0 { + metadata.RouteOverrides = make(map[string]RouteMetadata) + for methodName, routeConfig := range config.RouteOverrides { + metadata.RouteOverrides[methodName] = RouteMetadata(routeConfig) + } + } } // Debug: log metadata before filtering - LogDebug("[RegisterServiceType] %s (before filter): Resource='%s', PathPrefix='%s', RouteOverrides=%d", - serviceType, metadata.Resource, metadata.PathPrefix, len(metadata.RouteOverrides)) + LogDebug("[RegisterServiceType] %s (before filter): PathPrefix='%s', RouteOverrides=%d", + serviceType, metadata.PathPrefix, len(metadata.RouteOverrides)) // Store metadata if any meaningful configuration is provided var metadataPtr *ServiceMetadata - hasConfig := metadata.Resource != "" || - metadata.Convention != "rest" || // Non-default convention - metadata.PathPrefix != "" || + hasConfig := metadata.PathPrefix != "" || len(metadata.RouteOverrides) > 0 || len(metadata.MiddlewareNames) > 0 || len(metadata.HiddenMethods) > 0 @@ -285,8 +240,8 @@ func (g *GlobalRegistry) RegisterServiceType(serviceType string, local, remote a if hasConfig { metadataPtr = metadata // Debug log - LogDebug("[RegisterServiceType] %s: STORED - Resource=%s, PathPrefix=%s, RouteOverrides count=%d", - serviceType, metadata.Resource, metadata.PathPrefix, len(metadata.RouteOverrides)) + LogDebug("[RegisterServiceType] %s: STORED - PathPrefix=%s, RouteOverrides count=%d", + serviceType, metadata.PathPrefix, len(metadata.RouteOverrides)) for methodName, route := range metadata.RouteOverrides { LogDebug(" - %s: method=%s, path=%s", methodName, route.Method, route.Path) } @@ -310,6 +265,20 @@ func (g *GlobalRegistry) RegisterServiceType(serviceType string, local, remote a Remote: remoteFactory, Metadata: metadataPtr, } + + LogDebug("[RegisterRouterServiceType] %s: registered (local=%v, remote=%v)", + serviceType, localFactory != nil, remoteFactory != nil) +} + +// RegisterServiceType registers a simple service type without HTTP routing. +// Use this for infrastructure services like database pools, Redis clients, metrics, etc. +// For services that expose HTTP endpoints, use RegisterRouterServiceType instead. +// +// Parameters: +// - serviceType: Unique identifier for this service type +// - factory: Factory function (supports multiple signatures - see normalizeServiceFactory) +func (g *GlobalRegistry) RegisterServiceType(serviceType string, factory any) { + g.RegisterRouterServiceType(serviceType, factory, nil, nil) } // converts any supported factory signature to canonical ServiceFactory @@ -441,8 +410,8 @@ func (g *GlobalRegistry) GetServiceMetadata(serviceType string) *ServiceMetadata } if entry.Metadata != nil { - LogDebug("[GetServiceMetadata] serviceType '%s' FOUND: Resource=%s, RouteOverrides=%d", - serviceType, entry.Metadata.Resource, len(entry.Metadata.RouteOverrides)) + LogDebug("[GetServiceMetadata] serviceType '%s' FOUND: PathPrefix=%s, RouteOverrides=%d", + serviceType, entry.Metadata.PathPrefix, len(entry.Metadata.RouteOverrides)) } else { LogDebug("[GetServiceMetadata] serviceType '%s' FOUND but Metadata=nil", serviceType) } diff --git a/core/deploy/schema/lokstra.schema.json b/core/deploy/schema/lokstra.schema.json index fa8a3ebf..63a8d255 100644 --- a/core/deploy/schema/lokstra.schema.json +++ b/core/deploy/schema/lokstra.schema.json @@ -26,11 +26,6 @@ "type": "object", "description": "Router definition for auto-generated routes", "properties": { - "convention": { - "type": "string", - "description": "Convention type (rest, rpc, graphql)", - "pattern": "^[a-z][a-z0-9-]*$" - }, "path-prefix": { "type": "string", "description": "Path prefix for all routes" diff --git a/core/deploy/schema/temp.txt b/core/deploy/schema/temp.txt deleted file mode 100644 index 66edf7c0..00000000 --- a/core/deploy/schema/temp.txt +++ /dev/null @@ -1,844 +0,0 @@ -"external-service-definitions": { - "type": "object", - "description": "External service definitions (services outside this deployment). For external services, you typically need to override everything since their API structure may differ from internal conventions.", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["url"], - "properties": { - "url": { - "type": "string", - "description": "Base URL of external service (supports resolver: ${VAR:default} or direct URL)", - "oneOf": [ - { "pattern": "^https?://" }, - { "pattern": "^\\$\\{.*\\}$" } - ] - }, - "type": { - "type": "string", - "description": "Factory type (optional) - Auto-creates service wrapper. Required if external service will be used in published-services for auto-router generation. Not needed if only accessed via GetRemoteService.", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "config": { - "type": "object", - "description": "Additional configuration for the factory (optional)", - "additionalProperties": true - }, - "router": { - "type": "object", - "description": "Router configuration for this external service. For external services, you typically need to override everything since their API structure may differ from internal conventions.", - "properties": { - "convention": { - "type": "string", - "description": "Convention type (rest, rpc, graphql) - Override of RegisterServiceType metadata", - "pattern": "^[a-z][a-z-]*$" - }, - "resource": { - "type": "string", - "description": "Resource name (singular) - Override of RegisterServiceType metadata", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name - Override", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes (e.g., '/api/v1')" - }, - "path-rewrites": { - "type": "object", - "description": "Path rewrites for specific methods (method_name: new_path)", - "additionalProperties": { - "type": "string" - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Method names to hide from router", - "items": { - "type": "string", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string", - "description": "Method name", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "method": { - "type": "string", - "description": "HTTP method override (GET, POST, PUT, DELETE, PATCH)", - "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] - }, - "path": { - "type": "string", - "description": "Path override (e.g., '/custom/{id}')" - }, - "middlewares": { - "type": "array", - "description": "Route-specific middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - } - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - } - }, - "deployments": { - "type": "object", - "description": "Deployment configurations", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "properties": { - "config-overrides": { - "type": "object", - "description": "Configuration overrides for this deployment", - "additionalProperties": true - }, - "middleware-definitions": { - "type": "object", - "description": "Deployment-level middleware definitions (will be normalized to {deployment}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["type"], - "properties": { - "type": { - "type": "string", - "description": "Middleware factory type identifier", - "pattern": "^[a-z][a-z0-9_-]*$" - }, - "config": { - "type": "object", - "description": "Middleware-specific configuration", - "additionalProperties": true - } - }, - "additionalProperties": false - } - } - }, - "service-definitions": { - "type": "object", - "description": "Deployment-level service definitions (will be normalized to {deployment}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["type"], - "properties": { - "type": { - "type": "string", - "description": "Service factory type identifier", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "depends-on": { - "type": "array", - "description": "List of service dependencies", - "items": { - "type": "string", - "description": "Dependency in format 'paramName:serviceName' or just 'serviceName'", - "pattern": "^([a-zA-Z][a-zA-Z0-9]*:)?[a-z][a-z0-9-]*$" - } - }, - "middlewares": { - "type": "array", - "description": "DEPRECATED: Use router.middlewares instead", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "router": { - "type": "object", - "description": "Embedded router definition for this service", - "properties": { - "convention": { - "type": "string", - "description": "Convention type (rest, rpc, graphql)", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "resource": { - "type": "string", - "description": "Singular form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes" - }, - "path-rewrites": { - "type": "array", - "description": "Regex-based path rewrites", - "items": { - "type": "object", - "required": ["pattern", "replacement"], - "properties": { - "pattern": { - "type": "string" - }, - "replacement": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Methods to hide from router", - "items": { - "type": "string" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string" - }, - "method": { - "type": "string", - "pattern": "^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$" - }, - "path": { - "type": "string" - }, - "middlewares": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - }, - "config": { - "type": "object", - "description": "Service-specific configuration", - "additionalProperties": true - } - }, - "additionalProperties": false - } - } - }, - "router-definitions": { - "type": "object", - "description": "Deployment-level router definitions (will be normalized to {deployment}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "properties": { - "convention": { - "type": "string", - "description": "Convention type (rest, rpc, graphql)", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "resource": { - "type": "string", - "description": "Singular form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes" - }, - "path-rewrites": { - "type": "array", - "description": "Regex-based path rewrites", - "items": { - "type": "object", - "required": ["pattern", "replacement"], - "properties": { - "pattern": { - "type": "string", - "description": "Regex pattern to match" - }, - "replacement": { - "type": "string", - "description": "Replacement string" - } - }, - "additionalProperties": false - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Method names to hide from router", - "items": { - "type": "string", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string", - "description": "Method name", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "method": { - "type": "string", - "description": "HTTP method override", - "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] - }, - "path": { - "type": "string", - "description": "Path override" - }, - "middlewares": { - "type": "array", - "description": "Route-specific middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - } - } - }, - "external-service-definitions": { - "type": "object", - "description": "Deployment-level external service definitions (will be normalized to {deployment}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["url"], - "properties": { - "url": { - "type": "string", - "description": "Base URL of external service", - "oneOf": [ - { "pattern": "^https?://" }, - { "pattern": "^\\$\\{.*\\}$" } - ] - }, - "type": { - "type": "string", - "description": "Factory type (optional)", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "config": { - "type": "object", - "description": "Additional configuration", - "additionalProperties": true - }, - "router": { - "type": "object", - "description": "Router configuration for this external service", - "properties": { - "convention": { - "type": "string", - "description": "Convention type", - "pattern": "^[a-z][a-z-]*$" - }, - "resource": { - "type": "string", - "description": "Resource name (singular)", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes" - }, - "path-rewrites": { - "type": "object", - "description": "Path rewrites for specific methods", - "additionalProperties": { - "type": "string" - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Method names to hide from router", - "items": { - "type": "string", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string", - "description": "Method name", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "method": { - "type": "string", - "description": "HTTP method override", - "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] - }, - "path": { - "type": "string", - "description": "Path override" - }, - "middlewares": { - "type": "array", - "description": "Route-specific middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - } - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - } - }, - "servers": { - "type": "object", - "description": "Server definitions", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["base-url"], - "properties": { - "base-url": { - "type": "string", - "description": "Server base URL (supports resolver: ${VAR:default} or direct URL)", - "oneOf": [ - { "pattern": "^https?://" }, - { "pattern": "^\\$\\{[^}]+\\}$" } - ] - }, - "middleware-definitions": { - "type": "object", - "description": "Server-level middleware definitions (will be normalized to {deployment}.{server}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["type"], - "properties": { - "type": { - "type": "string", - "description": "Middleware factory type identifier", - "pattern": "^[a-z][a-z0-9_-]*$" - }, - "config": { - "type": "object", - "description": "Middleware-specific configuration", - "additionalProperties": true - } - }, - "additionalProperties": false - } - } - }, - "service-definitions": { - "type": "object", - "description": "Server-level service definitions (will be normalized to {deployment}.{server}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["type"], - "properties": { - "type": { - "type": "string", - "description": "Service factory type identifier", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "depends-on": { - "type": "array", - "description": "List of service dependencies", - "items": { - "type": "string", - "description": "Dependency in format 'paramName:serviceName' or just 'serviceName'", - "pattern": "^([a-zA-Z][a-zA-Z0-9]*:)?[a-z][a-z0-9.-]*$" - } - }, - "middlewares": { - "type": "array", - "description": "DEPRECATED: Use router.middlewares instead", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - }, - "router": { - "type": "object", - "description": "Embedded router definition for this service", - "properties": { - "convention": { - "type": "string", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "resource": { - "type": "string", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string" - }, - "path-rewrites": { - "type": "array", - "items": { - "type": "object", - "required": ["pattern", "replacement"], - "properties": { - "pattern": { "type": "string" }, - "replacement": { "type": "string" } - }, - "additionalProperties": false - } - }, - "middlewares": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - }, - "hidden": { - "type": "array", - "items": { "type": "string" } - }, - "custom": { - "type": "array", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { "type": "string" }, - "method": { - "type": "string", - "pattern": "^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$" - }, - "path": { "type": "string" }, - "middlewares": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - }, - "config": { - "type": "object", - "description": "Service-specific configuration", - "additionalProperties": true - } - }, - "additionalProperties": false - } - } - }, - "router-definitions": { - "type": "object", - "description": "Server-level router definitions (will be normalized to {deployment}.{server}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "properties": { - "convention": { - "type": "string", - "description": "Convention type (rest, rpc, graphql)", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "resource": { - "type": "string", - "description": "Singular form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes" - }, - "path-rewrites": { - "type": "array", - "description": "Regex-based path rewrites", - "items": { - "type": "object", - "required": ["pattern", "replacement"], - "properties": { - "pattern": { - "type": "string", - "description": "Regex pattern to match" - }, - "replacement": { - "type": "string", - "description": "Replacement string" - } - }, - "additionalProperties": false - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Method names to hide from router", - "items": { - "type": "string", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string", - "description": "Method name", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "method": { - "type": "string", - "description": "HTTP method override", - "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] - }, - "path": { - "type": "string", - "description": "Path override" - }, - "middlewares": { - "type": "array", - "description": "Route-specific middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - } - } - }, - "external-service-definitions": { - "type": "object", - "description": "Server-level external service definitions (will be normalized to {deployment}.{server}.{name})", - "patternProperties": { - "^[a-z][a-z0-9-]*$": { - "type": "object", - "required": ["url"], - "properties": { - "url": { - "type": "string", - "description": "Base URL of external service", - "oneOf": [ - { "pattern": "^https?://" }, - { "pattern": "^\\$\\{.*\\}$" } - ] - }, - "type": { - "type": "string", - "description": "Factory type (optional)", - "pattern": "^[a-z][a-z0-9-]*$" - }, - "config": { - "type": "object", - "description": "Additional configuration", - "additionalProperties": true - }, - "router": { - "type": "object", - "description": "Router configuration for this external service", - "properties": { - "convention": { - "type": "string", - "description": "Convention type", - "pattern": "^[a-z][a-z-]*$" - }, - "resource": { - "type": "string", - "description": "Resource name (singular)", - "pattern": "^[a-z][a-z-]*$" - }, - "resource-plural": { - "type": "string", - "description": "Plural form of resource name", - "pattern": "^[a-z][a-z-]*$" - }, - "path-prefix": { - "type": "string", - "description": "Path prefix for all routes" - }, - "path-rewrites": { - "type": "object", - "description": "Path rewrites for specific methods", - "additionalProperties": { - "type": "string" - } - }, - "middlewares": { - "type": "array", - "description": "Router-level middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - }, - "hidden": { - "type": "array", - "description": "Method names to hide from router", - "items": { - "type": "string", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - } - }, - "custom": { - "type": "array", - "description": "Custom route definitions", - "items": { - "type": "object", - "required": ["name"], - "properties": { - "name": { - "type": "string", - "description": "Method name", - "pattern": "^[A-Z][a-zA-Z0-9]*$" - }, - "method": { - "type": "string", - "description": "HTTP method override", - "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] - }, - "path": { - "type": "string", - "description": "Path override" - }, - "middlewares": { - "type": "array", - "description": "Route-specific middleware names", - "items": { - "type": "string", - "pattern": "^[a-z][a-z0-9.-]*$" - } - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - } - }, - "apps" diff --git a/core/deploy/service_options.go b/core/deploy/service_options.go index 4de263a4..bec7b807 100644 --- a/core/deploy/service_options.go +++ b/core/deploy/service_options.go @@ -1,17 +1,7 @@ package deploy -import ( - "strings" -) - // ServiceTypeConfig is a structured configuration for service type registration -// This provides a cleaner, more maintainable alternative to functional options type ServiceTypeConfig struct { - // Basic metadata - Resource string // Singular resource name (e.g., "user") - ResourcePlural string // Plural resource name (e.g., "users") - Convention string // Convention type (e.g., "rest", "rpc", "graphql") - // Router-level configuration PathPrefix string // Path prefix for all routes (e.g., "/api/v1") Middlewares []string // Middleware names to apply to all routes @@ -30,154 +20,3 @@ type RouteConfig struct { Path string // Custom path (e.g., "/auth/login", "/users/{id}/orders") Middlewares []string // Route-specific middleware names (in addition to router-level) } - -// RegisterServiceTypeOption configures service type registration (legacy functional options) -// Deprecated: Use ServiceTypeConfig struct for better readability -type RegisterServiceTypeOption func(*ServiceMetadata) - -// WithResource sets the resource name (singular and plural) -func WithResource(singular, plural string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - m.Resource = singular - m.ResourcePlural = plural - } -} - -// WithConvention sets the convention type (default: "rest") -func WithConvention(convention string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - m.Convention = convention - } -} - -// WithRouteOverride adds a custom route path for a method -// Deprecated: Use ServiceTypeConfig.RouteOverrides for better control including route-level middlewares -func WithRouteOverride(methodName, pathSpec string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - if m.RouteOverrides == nil { - m.RouteOverrides = make(map[string]RouteMetadata) - } - - // Parse pathSpec: "POST /path" or "/path" - parts := strings.SplitN(strings.TrimSpace(pathSpec), " ", 2) - method := "" - path := pathSpec - - if len(parts) == 2 { - possibleMethod := strings.ToUpper(parts[0]) - switch possibleMethod { - case "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS": - method = possibleMethod - path = strings.TrimSpace(parts[1]) - } - } - - m.RouteOverrides[methodName] = RouteMetadata{ - Method: method, - Path: path, - } - } -} - -// WithHiddenMethods hides methods from auto-generated router -func WithHiddenMethods(methods ...string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - m.HiddenMethods = append(m.HiddenMethods, methods...) - } -} - -// WithPathPrefix sets a path prefix for all routes -func WithPathPrefix(prefix string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - m.PathPrefix = prefix - } -} - -// WithMiddlewares adds middleware names to apply to all routes -func WithMiddlewares(middlewares ...string) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - m.MiddlewareNames = append(m.MiddlewareNames, middlewares...) - } -} - -// ServiceTypeRouter defines router configuration for a service type -// This is a cleaner alternative to functional options -type ServiceTypeRouter struct { - Resource string // Singular resource name (e.g., "user") - ResourcePlural string // Plural resource name (e.g., "users") - Convention string // Convention type (e.g., "rest", "rpc") - PathPrefix string // Path prefix for all routes (e.g., "/api") - Middlewares []string // Middleware names to apply to all routes - Hidden []string // Method names to hide from router - CustomRoutes map[string]string // Custom route overrides: methodName -> pathSpec (e.g., "GET /path") - RouteMiddlewares map[string][]string // Per-route middlewares: methodName -> []middleware -} - -// WithRouter creates a RegisterServiceTypeOption from router configuration -// Example: -// -// deploy.WithRouter(&deploy.ServiceTypeRouter{ -// Resource: "order", -// ResourcePlural: "orders", -// Convention: "rest", -// CustomRoutes: map[string]string{ -// "GetByUserID": "/users/{user_id}/orders", -// "UpdateStatus": "PATCH /orders/{id}/status", -// }, -// }) -func WithRouter(config *ServiceTypeRouter) RegisterServiceTypeOption { - return func(m *ServiceMetadata) { - if config.Resource != "" { - m.Resource = config.Resource - } - if config.ResourcePlural != "" { - m.ResourcePlural = config.ResourcePlural - } - if config.Convention != "" { - m.Convention = config.Convention - } - if config.PathPrefix != "" { - m.PathPrefix = config.PathPrefix - } - if len(config.Middlewares) > 0 { - m.MiddlewareNames = append(m.MiddlewareNames, config.Middlewares...) - } - if len(config.Hidden) > 0 { - m.HiddenMethods = append(m.HiddenMethods, config.Hidden...) - } - if len(config.CustomRoutes) > 0 { - if m.RouteOverrides == nil { - m.RouteOverrides = make(map[string]RouteMetadata) - } - for methodName, pathSpec := range config.CustomRoutes { - // Parse pathSpec: "POST /path" or "/path" - parts := strings.SplitN(strings.TrimSpace(pathSpec), " ", 2) - method := "" - path := pathSpec - - if len(parts) == 2 { - possibleMethod := strings.ToUpper(parts[0]) - switch possibleMethod { - case "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS": - method = possibleMethod - path = strings.TrimSpace(parts[1]) - } - } - - // Check if this method has per-route middlewares - var routeMiddlewares []string - if config.RouteMiddlewares != nil { - if middlewares, ok := config.RouteMiddlewares[methodName]; ok { - routeMiddlewares = middlewares - } - } - - m.RouteOverrides[methodName] = RouteMetadata{ - Method: method, - Path: path, - Middlewares: routeMiddlewares, - } - } - } - } -} diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_cache.lokstra.json b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_cache.lokstra.json index a05a7421..f2cf1f1e 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_cache.lokstra.json +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_cache.lokstra.json @@ -5,13 +5,13 @@ "filename": "order_service.go", "checksum": "26aef99438617cd599d7c34643ffc3c5e80865ffc65aa7c124d6cfa54bb52735", "annotations": 9, - "last_scan": "2025-11-25T00:27:33.3800998+07:00", + "last_scan": "2025-11-30T10:40:48.7556005+07:00", "generated": [ "zz_generated.lokstra.go" ], - "generated_mod_time": "2025-11-25T00:27:33.3800998+07:00" + "generated_mod_time": "2025-11-30T10:40:48.7556005+07:00" } }, - "updated_at": "2025-11-25T00:27:46.8696542+07:00", - "generated_checksum": "ab7e4488950bd24260582737755cfe8089d164888fda28e5d66ca930dd7a7dd0" + "updated_at": "2025-11-30T10:40:56.8931814+07:00", + "generated_checksum": "b00cfe5537f9a9dd8a026c7c8f8f5f3409fc00250bbe2e4c7b244035bda814d9" } \ No newline at end of file diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_generated.lokstra.go b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_generated.lokstra.go index 6bf3dea8..6ef5fa84 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_generated.lokstra.go +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/application/zz_generated.lokstra.go @@ -95,21 +95,33 @@ func OrderServiceImplRemoteFactory(deps, config map[string]any) any { // - @Route annotations on methods func RegisterOrderServiceImpl() { // Register service type with router configuration - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", OrderServiceImplFactory, OrderServiceImplRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{ "recovery", "request-logger" }, - CustomRoutes: map[string]string{ - "Cancel": "POST /orders/{id}/cancel", - "Create": "POST /orders", - "Delete": "DELETE /orders/{id}", - "GetByID": "GET /orders/{id}", - "List": "GET /orders", - "UpdateStatus": "PUT /orders/{id}/status", + RouteOverrides: map[string]deploy.RouteConfig{ + "Cancel": { + Path: "POST /orders/{id}/cancel", + }, + "Create": { + Path: "POST /orders", + }, + "Delete": { + Path: "DELETE /orders/{id}", + }, + "GetByID": { + Path: "GET /orders/{id}", + }, + "List": { + Path: "GET /orders", + }, + "UpdateStatus": { + Path: "PUT /orders/{id}/status", + }, }, - }), + }, ) // Register lazy service with auto-detected dependencies diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/register.go b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/register.go index 8c0752d6..e3b3103b 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/register.go +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/order/register.go @@ -11,7 +11,7 @@ import ( func Register() { // Register order repository (infrastructure - local only) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.OrderRepositoryFactory, nil) + repository.OrderRepositoryFactory) lokstra_registry.RegisterLazyService("order-repository", "order-repository-factory", nil) diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_cache.lokstra.json b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_cache.lokstra.json index 8084c6be..563ffd0c 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_cache.lokstra.json +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_cache.lokstra.json @@ -5,13 +5,13 @@ "filename": "user_service.go", "checksum": "35cbadac2b728595f39cdeeb4cf0196c0f2c0bce1025f985d09fa567e64706c4", "annotations": 9, - "last_scan": "2025-11-25T00:27:33.3716456+07:00", + "last_scan": "2025-11-30T10:40:48.7561108+07:00", "generated": [ "zz_generated.lokstra.go" ], - "generated_mod_time": "2025-11-25T00:27:33.3716456+07:00" + "generated_mod_time": "2025-11-30T10:40:48.7561108+07:00" } }, - "updated_at": "2025-11-25T00:27:46.8696542+07:00", - "generated_checksum": "520b98bcc5029343738077515db5968c9d6691308bedadea024742ffcd75da69" + "updated_at": "2025-11-30T10:40:56.8931814+07:00", + "generated_checksum": "4939ca2e28783fb546918996f71a5a18eb37825c20b4a42532a822d5d9312e73" } \ No newline at end of file diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_generated.lokstra.go b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_generated.lokstra.go index 159374b5..d0597c71 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_generated.lokstra.go +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/application/zz_generated.lokstra.go @@ -99,22 +99,36 @@ func UserServiceImplRemoteFactory(deps, config map[string]any) any { // - @Route annotations on methods func RegisterUserServiceImpl() { // Register service type with router configuration - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", UserServiceImplFactory, UserServiceImplRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{ "recovery", "request-logger" }, - CustomRoutes: map[string]string{ - "Activate": "POST /users/{id}/activate", - "Create": "POST /users", - "Delete": "DELETE /users/{id}", - "GetByID": "GET /users/{id}", - "List": "GET /users", - "Suspend": "POST /users/{id}/suspend", - "Update": "PUT /users/{id}", + RouteOverrides: map[string]deploy.RouteConfig{ + "Activate": { + Path: "POST /users/{id}/activate", + }, + "Create": { + Path: "POST /users", + }, + "Delete": { + Path: "DELETE /users/{id}", + }, + "GetByID": { + Path: "GET /users/{id}", + }, + "List": { + Path: "GET /users", + }, + "Suspend": { + Path: "POST /users/{id}/suspend", + }, + "Update": { + Path: "PUT /users/{id}", + }, }, - }), + }, ) // Register lazy service with auto-detected dependencies diff --git a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/register.go b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/register.go index 4a4c59d9..5248e4ae 100644 --- a/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/register.go +++ b/docs/00-introduction/examples/full-framework/01_enterprise_router_service/modules/user/register.go @@ -11,7 +11,7 @@ import ( func Register() { // Register user repository (infrastructure - local only) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.UserRepositoryFactory, nil) + repository.UserRepositoryFactory) lokstra_registry.RegisterLazyService("user-repository", "user-repository-factory", nil) diff --git a/docs/00-introduction/examples/full-framework/02-multi-deployment-yaml/register.go b/docs/00-introduction/examples/full-framework/02-multi-deployment-yaml/register.go index dbc6065e..704ba87b 100644 --- a/docs/00-introduction/examples/full-framework/02-multi-deployment-yaml/register.go +++ b/docs/00-introduction/examples/full-framework/02-multi-deployment-yaml/register.go @@ -17,26 +17,27 @@ func registerServiceTypes() { // Register repositories (infrastructure layer - local only) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.NewUserRepositoryMemory, nil) + repository.NewUserRepositoryMemory) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.NewOrderRepositoryMemory, nil) + repository.NewOrderRepositoryMemory) // Register services (application layer - local and remote) // Metadata provided via RegisterServiceType options (not in factory structs) - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", service.UserServiceFactory, service.UserServiceRemoteFactory, - deploy.WithResource("user", "users"), - deploy.WithConvention("rest"), + nil, // No custom config - uses default REST routing ) - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", service.OrderServiceFactory, service.OrderServiceRemoteFactory, - deploy.WithResource("order", "orders"), - deploy.WithConvention("rest"), - deploy.WithRouteOverride("GetByUserID", "/users/{user_id}/orders"), + &deploy.ServiceTypeConfig{ + RouteOverrides: map[string]deploy.RouteConfig{ + "GetByUserID": {Path: "/users/{user_id}/orders"}, + }, + }, ) } diff --git a/docs/00-introduction/examples/full-framework/03-multi-deployment-pure-code/register.go b/docs/00-introduction/examples/full-framework/03-multi-deployment-pure-code/register.go index 1a6dc71c..e8ae659b 100644 --- a/docs/00-introduction/examples/full-framework/03-multi-deployment-pure-code/register.go +++ b/docs/00-introduction/examples/full-framework/03-multi-deployment-pure-code/register.go @@ -17,28 +17,30 @@ func registerServiceTypes() { // Register repositories (infrastructure layer) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.NewUserRepositoryMemory, nil) + repository.NewUserRepositoryMemory) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.NewOrderRepositoryMemory, nil) + repository.NewOrderRepositoryMemory) // Register services (application layer) // Metadata provided via RegisterServiceType options (not in factory structs) - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", service.UserServiceFactory, service.UserServiceRemoteFactory, - deploy.WithResource("user", "users"), - deploy.WithConvention("rest"), - deploy.WithMiddlewares("recovery", "before-after-logger"), + &deploy.ServiceTypeConfig{ + Middlewares: []string{"recovery", "before-after-logger"}, + }, ) - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", service.OrderServiceFactory, service.OrderServiceRemoteFactory, - deploy.WithResource("order", "orders"), - deploy.WithConvention("rest"), - deploy.WithRouteOverride("GetByUserID", "/users/{user_id}/orders"), - deploy.WithMiddlewares("recovery", "before-after-logger"), + &deploy.ServiceTypeConfig{ + Middlewares: []string{"recovery", "before-after-logger"}, + RouteOverrides: map[string]deploy.RouteConfig{ + "GetByUserID": {Path: "/users/{user_id}/orders"}, + }, + }, ) } diff --git a/docs/00-introduction/examples/full-framework/04-external-services/main.go b/docs/00-introduction/examples/full-framework/04-external-services/main.go index 9ef6d5b2..df7c1fdb 100644 --- a/docs/00-introduction/examples/full-framework/04-external-services/main.go +++ b/docs/00-introduction/examples/full-framework/04-external-services/main.go @@ -11,20 +11,24 @@ import ( func main() { // Register service factories - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", svc.OrderServiceFactory, nil, - deploy.WithResource("order", "orders"), - deploy.WithConvention("rest"), - deploy.WithRouteOverride("Refund", "POST /orders/{id}/refund"), + &deploy.ServiceTypeConfig{ + RouteOverrides: map[string]deploy.RouteConfig{ + "Refund": {Method: "POST", Path: "/orders/{id}/refund"}, + }, + }, ) - lokstra_registry.RegisterServiceType("payment-service-remote-factory", + lokstra_registry.RegisterRouterServiceType("payment-service-remote-factory", nil, svc.PaymentServiceRemoteFactory, - deploy.WithResource("payment", "payments"), - deploy.WithConvention("rest"), - deploy.WithRouteOverride("CreatePayment", "POST /payments"), - deploy.WithRouteOverride("GetPayment", "GET /payments/{id}"), - deploy.WithRouteOverride("Refund", "POST /payments/{id}/refund"), + &deploy.ServiceTypeConfig{ + RouteOverrides: map[string]deploy.RouteConfig{ + "CreatePayment": {Method: "POST", Path: "/payments"}, + "GetPayment": {Method: "GET", Path: "/payments/{id}"}, + "Refund": {Method: "POST", Path: "/payments/{id}/refund"}, + }, + }, ) printStartInfo() diff --git a/docs/00-introduction/examples/full-framework/05-remote-router/main.go b/docs/00-introduction/examples/full-framework/05-remote-router/main.go index 73c14df8..3e1a4b7e 100644 --- a/docs/00-introduction/examples/full-framework/05-remote-router/main.go +++ b/docs/00-introduction/examples/full-framework/05-remote-router/main.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/primadi/lokstra/core/deploy" "github.com/primadi/lokstra/core/deploy/loader" svc "github.com/primadi/lokstra/docs/00-introduction/examples/full-framework/05-remote-router/service" "github.com/primadi/lokstra/lokstra_registry" @@ -20,10 +19,7 @@ func main() { // Register service factory lokstra_registry.RegisterServiceType("weather-service-factory", - svc.WeatherServiceFactory, nil, - deploy.WithResource("weather-report", "weather-reports"), - deploy.WithConvention("rest"), - ) + svc.WeatherServiceFactory) // Load config and build deployment topology if err := loader.LoadAndBuild([]string{"config.yaml"}); err != nil { diff --git a/docs/00-introduction/examples/full-framework/06-inline-definitions-example/register.go b/docs/00-introduction/examples/full-framework/06-inline-definitions-example/register.go index 4dc4ced3..139d1410 100644 --- a/docs/00-introduction/examples/full-framework/06-inline-definitions-example/register.go +++ b/docs/00-introduction/examples/full-framework/06-inline-definitions-example/register.go @@ -3,7 +3,6 @@ package main import ( "fmt" - "github.com/primadi/lokstra/core/deploy" "github.com/primadi/lokstra/core/request" "github.com/primadi/lokstra/lokstra_registry" ) @@ -146,22 +145,13 @@ func registerServiceTypes() { // Register service types with REST metadata lokstra_registry.RegisterServiceType("user-service-type", UserServiceFactory, - nil, - deploy.WithResource("user", "users"), - deploy.WithConvention("rest"), ) lokstra_registry.RegisterServiceType("product-service-type", ProductServiceFactory, - nil, - deploy.WithResource("product", "products"), - deploy.WithConvention("rest"), ) lokstra_registry.RegisterServiceType("order-service-type", OrderServiceFactory, - nil, - deploy.WithResource("order", "orders"), - deploy.WithConvention("rest"), ) } diff --git a/docs/01-router-guide/02-service/examples/02-lazyload-vs-getservice/main.go b/docs/01-router-guide/02-service/examples/02-lazyload-vs-getservice/main.go index c86fcac3..06d2c134 100644 --- a/docs/01-router-guide/02-service/examples/02-lazyload-vs-getservice/main.go +++ b/docs/01-router-guide/02-service/examples/02-lazyload-vs-getservice/main.go @@ -32,7 +32,7 @@ func main() { lokstra_registry.RegisterServiceType("user-service", func() any { log.Println("🏭 UserService factory called (creating instance)") return &UserService{} - }, nil) + }) // Create router with 3 different access methods router := lokstra.NewRouter("demo") diff --git a/docs/02-framework-guide/02-service/examples/01-service-factories/main.go b/docs/02-framework-guide/02-service/examples/01-service-factories/main.go index 77483e86..19d3dac2 100644 --- a/docs/02-framework-guide/02-service/examples/01-service-factories/main.go +++ b/docs/02-framework-guide/02-service/examples/01-service-factories/main.go @@ -356,12 +356,12 @@ func Home() *response.Response { func main() { // Register all services - lokstra_registry.RegisterServiceType("cache-service", CacheServiceFactory, nil) - lokstra_registry.RegisterServiceType("logger-service", LoggerServiceFactory, nil) - lokstra_registry.RegisterServiceType("simple-service", SimpleServiceFactory, nil) - lokstra_registry.RegisterServiceType("configurable-service", ConfigurableServiceFactory, nil) - lokstra_registry.RegisterServiceType("dependent-service", DependentServiceFactory, nil) - lokstra_registry.RegisterServiceType("lifecycle-service", LifecycleServiceFactory, nil) + lokstra_registry.RegisterServiceType("cache-service", CacheServiceFactory) + lokstra_registry.RegisterServiceType("logger-service", LoggerServiceFactory) + lokstra_registry.RegisterServiceType("simple-service", SimpleServiceFactory) + lokstra_registry.RegisterServiceType("configurable-service", ConfigurableServiceFactory) + lokstra_registry.RegisterServiceType("dependent-service", DependentServiceFactory) + lokstra_registry.RegisterServiceType("lifecycle-service", LifecycleServiceFactory) // Define services lokstra_registry.RegisterLazyService("cache-service", CacheServiceFactory, nil) diff --git a/docs/02-framework-guide/02-service/examples/02-remote-services/main.go b/docs/02-framework-guide/02-service/examples/02-remote-services/main.go index 5aa2ce76..1428caad 100644 --- a/docs/02-framework-guide/02-service/examples/02-remote-services/main.go +++ b/docs/02-framework-guide/02-service/examples/02-remote-services/main.go @@ -150,14 +150,12 @@ func Home() *response.Response { // ============================================ func main() { - // Register remote service type - lokstra_registry.RegisterServiceType("remote-user-service", RemoteUserServiceFactory, nil) - // Define remote service with configuration - lokstra_registry.RegisterLazyService("remote-user-service", RemoteUserServiceFactory, map[string]any{ - "router": "user-api", - "path-prefix": "/api/v1", - }) + lokstra_registry.RegisterLazyService("remote-user-service", RemoteUserServiceFactory, + map[string]any{ + "router": "user-api", + "path-prefix": "/api/v1", + }) // Setup router router := lokstra.NewRouter("remote-services") diff --git a/docs/02-framework-guide/02-service/examples/04-service-composition/main.go b/docs/02-framework-guide/02-service/examples/04-service-composition/main.go index 969127d4..949b986c 100644 --- a/docs/02-framework-guide/02-service/examples/04-service-composition/main.go +++ b/docs/02-framework-guide/02-service/examples/04-service-composition/main.go @@ -74,8 +74,8 @@ func Home() *response.Response { } func main() { - lokstra_registry.RegisterServiceType("data-service", DataServiceFactory, nil) - lokstra_registry.RegisterServiceType("cached-data-service", CachedDataServiceFactory, nil) + lokstra_registry.RegisterServiceType("data-service", DataServiceFactory) + lokstra_registry.RegisterServiceType("cached-data-service", CachedDataServiceFactory) lokstra_registry.RegisterLazyService("data-service", DataServiceFactory, nil) lokstra_registry.RegisterLazyServiceWithDeps("cached-data-service", diff --git a/docs/02-framework-guide/02-service/examples/05-service-architecture/main.go b/docs/02-framework-guide/02-service/examples/05-service-architecture/main.go index 6b75287d..0960881a 100644 --- a/docs/02-framework-guide/02-service/examples/05-service-architecture/main.go +++ b/docs/02-framework-guide/02-service/examples/05-service-architecture/main.go @@ -96,9 +96,6 @@ func Home() *response.Response { } func main() { - lokstra_registry.RegisterServiceType("user-repository", UserRepositoryFactory, nil) - lokstra_registry.RegisterServiceType("user-service", UserServiceFactory, nil) - lokstra_registry.RegisterLazyService("user-repository", UserRepositoryFactory, nil) lokstra_registry.RegisterLazyService("user-service", UserServiceFactory, nil) diff --git a/docs/02-framework-guide/02-service/examples/06-testing/main.go b/docs/02-framework-guide/02-service/examples/06-testing/main.go index 1cf8ded8..a2c98497 100644 --- a/docs/02-framework-guide/02-service/examples/06-testing/main.go +++ b/docs/02-framework-guide/02-service/examples/06-testing/main.go @@ -108,9 +108,6 @@ func Home() *response.Response { } func main() { - lokstra_registry.RegisterServiceType("email-service", SMTPEmailServiceFactory, nil) - lokstra_registry.RegisterServiceType("notification-service", NotificationServiceFactory, nil) - // Use mock for testing lokstra_registry.RegisterLazyService("email-service", MockEmailServiceFactory, nil) lokstra_registry.RegisterLazyService("notification-service", NotificationServiceFactory, nil) diff --git a/docs/02-framework-guide/02-service/examples/07-performance/main.go b/docs/02-framework-guide/02-service/examples/07-performance/main.go index 8908ca7f..0383e667 100644 --- a/docs/02-framework-guide/02-service/examples/07-performance/main.go +++ b/docs/02-framework-guide/02-service/examples/07-performance/main.go @@ -116,10 +116,6 @@ func Home() *response.Response { } func main() { - lokstra_registry.RegisterServiceType("data-service", DataServiceFactory, nil) - lokstra_registry.RegisterServiceType("lazy-consumer", LazyConsumerFactory, nil) - lokstra_registry.RegisterServiceType("direct-consumer", DirectConsumerFactory, nil) - lokstra_registry.RegisterLazyService("data-service", DataServiceFactory, nil) lokstra_registry.RegisterLazyServiceWithDeps("lazy-consumer", LazyConsumerFactory, diff --git a/docs/02-framework-guide/02-service/examples/08-migration-pattern/main.go b/docs/02-framework-guide/02-service/examples/08-migration-pattern/main.go index 974cff9a..c990c939 100644 --- a/docs/02-framework-guide/02-service/examples/08-migration-pattern/main.go +++ b/docs/02-framework-guide/02-service/examples/08-migration-pattern/main.go @@ -108,10 +108,6 @@ func Home() *response.Response { } func main() { - lokstra_registry.RegisterServiceType("monolith-service", MonolithServiceFactory, nil) - lokstra_registry.RegisterServiceType("user-microservice", UserMicroserviceFactory, nil) - lokstra_registry.RegisterServiceType("facade-service", FacadeServiceFactory, nil) - lokstra_registry.RegisterLazyService("monolith-service", MonolithServiceFactory, nil) lokstra_registry.RegisterLazyService("user-microservice", UserMicroserviceFactory, nil) diff --git a/docs/02-framework-guide/04-config/examples/01-basic-config/main.go b/docs/02-framework-guide/04-config/examples/01-basic-config/main.go index f3e932db..87ab4385 100644 --- a/docs/02-framework-guide/04-config/examples/01-basic-config/main.go +++ b/docs/02-framework-guide/04-config/examples/01-basic-config/main.go @@ -34,7 +34,6 @@ func registerServiceTypes() { lokstra_registry.RegisterServiceType( "user-repository-factory", NewUserRepository, - nil, // no remote factory ) // Register as lazy service diff --git a/docs/02-framework-guide/04-config/examples/06-handlers/main.go b/docs/02-framework-guide/04-config/examples/06-handlers/main.go index 2a77b20b..38d9e05f 100644 --- a/docs/02-framework-guide/04-config/examples/06-handlers/main.go +++ b/docs/02-framework-guide/04-config/examples/06-handlers/main.go @@ -33,7 +33,6 @@ func registerServiceTypes() { lokstra_registry.RegisterServiceType( "user-repository-factory", NewUserRepository, - nil, ) lokstra_registry.RegisterLazyService( diff --git a/lokstra_registry/registry.go b/lokstra_registry/registry.go index e3d768b0..65e692e2 100644 --- a/lokstra_registry/registry.go +++ b/lokstra_registry/registry.go @@ -189,8 +189,51 @@ func GetAllRouters() map[string]router.Router { // }, // nil, // ) -func RegisterServiceType(serviceType string, local, remote any, configOrOptions ...any) { - deploy.Global().RegisterServiceType(serviceType, local, remote, configOrOptions...) +// +// RegisterRouterServiceType registers a service type with HTTP routing configuration. +// Use this for services that expose HTTP endpoints (annotated with @RouterService). +// For simple infrastructure services (DB, Redis, etc), use RegisterServiceType instead. +// +// Parameters: +// - serviceType: Unique identifier for this service type +// - local: Factory for local deployment (same process) +// - remote: Factory for remote deployment (HTTP client) +// - config: Optional routing configuration (path prefix, middlewares, route overrides) +// +// Example: +// +// lokstra_registry.RegisterRouterServiceType("user-service-factory", +// application.UserServiceFactory, nil, +// &deploy.ServiceTypeConfig{ +// PathPrefix: "/api/users", +// Middlewares: []string{"auth"}, +// }) +func RegisterRouterServiceType(serviceType string, local, remote any, config *deploy.ServiceTypeConfig) { + deploy.Global().RegisterRouterServiceType(serviceType, local, remote, config) +} + +// RegisterServiceType registers a simple service type without HTTP routing. +// Use this for infrastructure services like database pools, Redis clients, metrics, etc. +// For services that expose HTTP endpoints, use RegisterRouterServiceType instead. +// +// Parameters: +// - serviceType: Unique identifier for this service type +// - factory: Factory function that creates service instances +// +// Supported factory signatures: +// - func() any +// - func(deps map[string]any) any +// - func(cfg map[string]any) any +// - func(deps, cfg map[string]any) any +// +// Example: +// +// lokstra_registry.RegisterServiceType("db-pool-factory", +// func(cfg map[string]any) any { +// return db.NewPool(cfg["dsn"].(string)) +// }) +func RegisterServiceType(serviceType string, factory any) { + deploy.Global().RegisterServiceType(serviceType, factory) } // GetServiceFactory returns the service factory for a service type diff --git a/project_templates/02_app_framework/01_medium_system/register.go b/project_templates/02_app_framework/01_medium_system/register.go index 2747ac1f..6f7aefa0 100644 --- a/project_templates/02_app_framework/01_medium_system/register.go +++ b/project_templates/02_app_framework/01_medium_system/register.go @@ -15,30 +15,35 @@ import ( func registerServiceTypes() { // Register repositories (infrastructure layer - local only) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.NewUserRepositoryMemory, nil) + repository.NewUserRepositoryMemory) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.NewOrderRepositoryMemory, nil) + repository.NewOrderRepositoryMemory) // Register services (application layer - local and remote) - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", service.UserServiceFactory, service.UserServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{"recovery", "request-logger"}, - }), + }, ) - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", service.OrderServiceFactory, service.OrderServiceRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ - CustomRoutes: map[string]string{ - "GetByUserID": "/users/{user_id}/orders", - "UpdateStatus": "PATCH /orders/{id}/status", + &deploy.ServiceTypeConfig{ + RouteOverrides: map[string]deploy.RouteConfig{ + "GetByUserID": { + Path: "/users/{user_id}/orders", + }, + "UpdateStatus": { + Method: "PATCH", + Path: "/orders/{id}/status", + }, }, - }), + }, ) } diff --git a/project_templates/02_app_framework/02_enterprise_modular/modules/order/register.go b/project_templates/02_app_framework/02_enterprise_modular/modules/order/register.go index 15b40dc5..463f186d 100644 --- a/project_templates/02_app_framework/02_enterprise_modular/modules/order/register.go +++ b/project_templates/02_app_framework/02_enterprise_modular/modules/order/register.go @@ -13,20 +13,20 @@ import ( func Register() { // Register order repository (infrastructure - local only) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.OrderRepositoryFactory, nil) + repository.OrderRepositoryFactory) // Register order service (application - local and remote) - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", application.OrderServiceFactory, OrderServiceRemoteFactory, // Remote factory for microservices - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{"recovery", "request-logger"}, - CustomRoutes: map[string]string{ - "UpdateStatus": "PUT /orders/{id}/status", - "Cancel": "POST /orders/{id}/cancel", + RouteOverrides: map[string]deploy.RouteConfig{ + "UpdateStatus": {Method: "PUT", Path: "/orders/{id}/status"}, + "Cancel": {Method: "POST", Path: "/orders/{id}/cancel"}, }, - }), + }, ) lokstra_registry.RegisterLazyService("order-repository", "order-repository-factory", nil) diff --git a/project_templates/02_app_framework/02_enterprise_modular/modules/user/register.go b/project_templates/02_app_framework/02_enterprise_modular/modules/user/register.go index 628561b5..fdfbd38c 100644 --- a/project_templates/02_app_framework/02_enterprise_modular/modules/user/register.go +++ b/project_templates/02_app_framework/02_enterprise_modular/modules/user/register.go @@ -13,20 +13,20 @@ import ( func Register() { // Register user repository (infrastructure - local only) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.UserRepositoryFactory, nil) + repository.UserRepositoryFactory) // Register user service (application - local and remote) - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", application.UserServiceFactory, UserServiceRemoteFactory, // Remote factory for microservices - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{"recovery", "request-logger"}, - CustomRoutes: map[string]string{ - "Suspend": "POST /user/{id}/suspend", - "Activate": "POST /user/{id}/activate", + RouteOverrides: map[string]deploy.RouteConfig{ + "Suspend": {Method: "POST", Path: "/user/{id}/suspend"}, + "Activate": {Method: "POST", Path: "/user/{id}/activate"}, }, - }), + }, ) lokstra_registry.RegisterLazyService("user-repository", "user-repository-factory", nil) diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_cache.lokstra.json b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_cache.lokstra.json index 33a2cde3..fbce5427 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_cache.lokstra.json +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_cache.lokstra.json @@ -5,13 +5,13 @@ "filename": "order_service.go", "checksum": "5e116e5cc481b631bc794c80476e2b4920c6f8cfc05390414c0e23e42b3e2d2b", "annotations": 9, - "last_scan": "2025-11-27T18:17:07.6193849+07:00", + "last_scan": "2025-11-30T10:23:33.3978465+07:00", "generated": [ "zz_generated.lokstra.go" ], - "generated_mod_time": "2025-11-27T18:17:07.6193849+07:00" + "generated_mod_time": "2025-11-30T10:23:33.3978465+07:00" } }, - "updated_at": "2025-11-27T18:17:14.5472139+07:00", - "generated_checksum": "7fee19831cf3d38f52df538a41f1841c3f59cfffc829032fccd7614498b1fa2c" + "updated_at": "2025-11-30T10:23:33.3978465+07:00", + "generated_checksum": "3fb2e5d7a602905fbf3254b458feed0435d6f9c6dce72ef18ed4d83c6a659616" } \ No newline at end of file diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_generated.lokstra.go b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_generated.lokstra.go index 2754a633..63f4fd34 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_generated.lokstra.go +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/application/zz_generated.lokstra.go @@ -95,21 +95,33 @@ func OrderServiceImplRemoteFactory(deps, config map[string]any) any { // - @Route annotations on methods func RegisterOrderServiceImpl() { // Register service type with router configuration - lokstra_registry.RegisterServiceType("order-service-factory", + lokstra_registry.RegisterRouterServiceType("order-service-factory", OrderServiceImplFactory, OrderServiceImplRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{ "recovery", "request-logger" }, - CustomRoutes: map[string]string{ - "Cancel": "POST /orders/{id}/cancel", - "Create": "POST /orders", - "Delete": "DELETE /orders/{id}", - "GetByID": "GET /orders/{id}", - "List": "GET /orders", - "UpdateStatus": "PUT /orders/{id}/status", + RouteOverrides: map[string]deploy.RouteConfig{ + "Cancel": { + Path: "POST /orders/{id}/cancel", + }, + "Create": { + Path: "POST /orders", + }, + "Delete": { + Path: "DELETE /orders/{id}", + }, + "GetByID": { + Path: "GET /orders/{id}", + }, + "List": { + Path: "GET /orders", + }, + "UpdateStatus": { + Path: "PUT /orders/{id}/status", + }, }, - }), + }, ) // Register lazy service with auto-detected dependencies diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/register.go b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/register.go index 13374c47..d9221f0a 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/order/register.go +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/order/register.go @@ -11,7 +11,7 @@ import ( func Register() { // Register order repository (infrastructure - local only) lokstra_registry.RegisterServiceType("order-repository-factory", - repository.OrderRepositoryFactory, nil) + repository.OrderRepositoryFactory) lokstra_registry.RegisterLazyService("order-repository", "order-repository-factory", nil) diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_cache.lokstra.json b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_cache.lokstra.json index a74cbc29..8c824b9b 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_cache.lokstra.json +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_cache.lokstra.json @@ -5,13 +5,13 @@ "filename": "user_service.go", "checksum": "05c3a8ec83619efd7c01f0efc3bc3d324063658f7ef8eba9a23021aec2b9e212", "annotations": 9, - "last_scan": "2025-11-27T18:17:07.6193849+07:00", + "last_scan": "2025-11-30T10:23:33.3978465+07:00", "generated": [ "zz_generated.lokstra.go" ], - "generated_mod_time": "2025-11-27T18:17:07.6193849+07:00" + "generated_mod_time": "2025-11-30T10:23:33.3978465+07:00" } }, - "updated_at": "2025-11-27T18:17:14.5472139+07:00", - "generated_checksum": "8df4ef55370db74cf00106055bc829aca0b5cc52671e2f1644a0337990c3162f" + "updated_at": "2025-11-30T10:23:33.3978465+07:00", + "generated_checksum": "869a0cafd3701e7ad5837bfb32c46c42dcae1e07e9ceb538f0cc01c9f6d3a188" } \ No newline at end of file diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_generated.lokstra.go b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_generated.lokstra.go index 763df135..fed89676 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_generated.lokstra.go +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/application/zz_generated.lokstra.go @@ -99,26 +99,38 @@ func UserServiceImplRemoteFactory(deps, config map[string]any) any { // - @Route annotations on methods func RegisterUserServiceImpl() { // Register service type with router configuration - lokstra_registry.RegisterServiceType("user-service-factory", + lokstra_registry.RegisterRouterServiceType("user-service-factory", UserServiceImplFactory, UserServiceImplRemoteFactory, - deploy.WithRouter(&deploy.ServiceTypeRouter{ + &deploy.ServiceTypeConfig{ PathPrefix: "/api", Middlewares: []string{ "recovery", "request-logger" }, - CustomRoutes: map[string]string{ - "Activate": "POST /users/{id}/activate", - "Create": "POST /users", - "Delete": "DELETE /users/{id}", - "GetByID": "GET /users/{id}", - "List": "GET /users", - "Suspend": "POST /users/{id}/suspend", - "Update": "PUT /users/{id}", + RouteOverrides: map[string]deploy.RouteConfig{ + "Activate": { + Path: "POST /users/{id}/activate", + }, + "Create": { + Path: "POST /users", + }, + "Delete": { + Path: "DELETE /users/{id}", + }, + "GetByID": { + Path: "GET /users/{id}", + Middlewares: []string{ "mw-test param1=123, param2=abc" }, + }, + "List": { + Path: "GET /users", + Middlewares: []string{ "mw-test" }, + }, + "Suspend": { + Path: "POST /users/{id}/suspend", + }, + "Update": { + Path: "PUT /users/{id}", + }, }, - RouteMiddlewares: map[string][]string{ - "GetByID": { "mw-test param1=123, param2=abc" }, - "List": { "mw-test" }, - }, - }), + }, ) // Register lazy service with auto-detected dependencies diff --git a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/register.go b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/register.go index 3f1d78cc..a86fa418 100644 --- a/project_templates/02_app_framework/03_enterprise_router_service/modules/user/register.go +++ b/project_templates/02_app_framework/03_enterprise_router_service/modules/user/register.go @@ -11,7 +11,7 @@ import ( func Register() { // Register user repository (infrastructure - local only) lokstra_registry.RegisterServiceType("user-repository-factory", - repository.UserRepositoryFactory, nil) + repository.UserRepositoryFactory) lokstra_registry.RegisterLazyService("user-repository", "user-repository-factory", nil) diff --git a/services/dbpool_pg/module.go b/services/dbpool_pg/module.go index 19803fb7..d15cbf5b 100644 --- a/services/dbpool_pg/module.go +++ b/services/dbpool_pg/module.go @@ -94,6 +94,5 @@ func ServiceFactory(params map[string]any) any { } func Register() { - lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory, - nil) + lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory) } diff --git a/services/kvstore_redis/module.go b/services/kvstore_redis/module.go index ea13793c..ca19e887 100644 --- a/services/kvstore_redis/module.go +++ b/services/kvstore_redis/module.go @@ -119,6 +119,5 @@ func ServiceFactory(params map[string]any) any { } func Register() { - lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory, - nil) + lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory) } diff --git a/services/metrics_prometheus/module.go b/services/metrics_prometheus/module.go index 826d4154..76e3eb6e 100644 --- a/services/metrics_prometheus/module.go +++ b/services/metrics_prometheus/module.go @@ -147,6 +147,5 @@ func ServiceFactory(params map[string]any) any { } func Register() { - lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory, - nil) + lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory) } diff --git a/services/redis/module.go b/services/redis/module.go index 47be339b..efa8bdb7 100644 --- a/services/redis/module.go +++ b/services/redis/module.go @@ -52,5 +52,5 @@ func ServiceFactory(params map[string]any) any { } func Register() { - lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory, nil) + lokstra_registry.RegisterServiceType(SERVICE_TYPE, ServiceFactory) }