# Register Named Services

In Parsely, you can register services with specific names to support more granular control and flexible resolution of service instances. Named services are beneficial when you need to manage multiple implementations of the same interface or provide different configurations for different contexts.

When registering named services, you associate a unique name with each implementation. This allows you to resolve and retrieve the correct service based on its name, enhancing your application's flexibility and configurability.

Suppose you have an interface DataService with two implementations: remoteDataService and localDataService.

package internal

import "fmt"

type DataService interface {
	FetchData() string
}

type remoteDataService struct {
	tenantId string
}

func (s *remoteDataService) FetchData() string {
	return fmt.Sprintf("Data from remote server for tenant %s", s.tenantId)
}

type localDataService struct{}

func (s *localDataService) FetchData() string {
	return "Data from local database"
}

func NewRemoteDataService() DataService {
	return &remoteDataService{}
}

func NewRemoteDataServiceFactory() func(string) DataService {
	return func(tenantId string) DataService {
		return &remoteDataService{
			tenantId: tenantId,
		}
	}
}

func NewLocalDataService() DataService {
	return &localDataService{}
}

func NewLocalDataServiceFactory() func(string) DataService {
	return func(_ string) DataService {
		return &localDataService{}
	}
}

# Register named services

Register each service with a unique name using RegisterNamed. This allows you to specify different names for each implementation and control their lifecycle.

registry := registration.NewServiceRegistry()

_ = features.RegisterNamed[internal.DataService](registry,
	registration.NamedServiceRegistration("remote", internal.NewRemoteDataService, types.LifetimeTransient),
	registration.NamedServiceRegistration("local", internal.NewLocalDataService, types.LifetimeTransient))

In this example, RegisterNamed is used to register the constructor functions NewRemoteDataService and NewLocalDataService with the names remote and local, respectively. The LifetimeTransient parameter indicates that each service instance is transient and will be created anew each time it is resolved.

Note Implementation types with names are automatically registered as an unnamed service with the desired interface type and lifetime scope. So, named services can be resolved (separately) by name or as a list of service instances per interface type.

# Resolve and use named services

To resolve a named service, you use a factory function that takes the name of the service (a string parameter) and returns an instance of the service along with any errors. This is done through the ResolveRequiredService function:

serviceFactory, _ := resolving.ResolveRequiredService[func(string) (internal.DataService, error)](resolver, scope)
localDataService, err := serviceFactory("local")
if err != nil {
	panic(err)
}

data := localDataService.FetchData()
fmt.Println(data)

Note You can combine the concept of factory functions and named services, but you must ensure that the signature of the factory function is the same for all named services.