Ho implementato clean architecture per la mia app e ho alcune domande.
In genere, il puro DI viene discusso per un modello di Localizzazione del servizio, perché è molto esplicito e più verificabile.
Tuttavia, mi piace l'idea di avere un oggetto che contenga tutti i miei servizi o tutti i miei repository e posso semplicemente iniettare quel singolo oggetto invece di iniettare i vari servizi / repository per facilitare lo sviluppo.
Ma non sono sicuro se questo segue ancora i principi SOLIDI.
Ad esempio, ecco un DI tipico:
// Database
type Database interface {
Insert(...)
Delete(...)
}
// User object
type User struct {}
// User Service
type UserService interface {
Create(user User)
}
func NewUserService(userRepo UserRepository) UserService {
return &userService{userRepo: userRepo}
}
type userService struct {
userRepo UserRepository
}
func (s *userService) Create(user User) {
s.userRepo.Create(user)
}
// User Repository
type UserRepository interface {
Create(user User)
}
func NewUserRepository(db Database) UserRepository {
return &userRepository{db: db}
}
type userRepository struct {
db Database
}
func (r *userRepository) Create(user User) {
r.db.Insert(user)
}
main() {
// Create db
db := database.New(...)
// Create repo by injecting db
userRepo := NewUserRepository(db)
// Create service by injecting repo
userService := NewUserService(userRepo)
}
Come vedi nell'esempio sopra, sta seguendo un approccio DI puro.
Il problema che ho con questo è che gli argomenti possono arrivare a oltre 30 cose che vengono iniettate in un'applicazione più grande.
Quindi mi sono imbattuto in un modello diverso che fondamentalmente inietta un singolo ServiceFactory
o RepositoryFactory
, e quelli conterranno i puntatori a tutte le altre classi di servizi / repository più granulari.
Quindi il nuovo codice può essere cambiato in qualcosa di simile:
// User Service
type UserService interface {
Create(user User)
}
func NewUserService(repos RepositoryFactory) UserService {
return &userService{repos: repos}
}
type userService struct {
repos RepositoryFactory
}
func (s *userService) Create(user User) {
s.repos.UserRepository.Create(user)
}
// Repository factory
type RepositoryFactory struct {
UserRepository UserRepository
}
func NewRepositoryFactory(db Database) RepositoryFactory {
return RepositoryFactory{
UserRepository: NewUserRepository(db),
}
}
main() {
// Create db
db := database.New(...)
// Create repo factory
repos := NewRepositoryFactory(db)
// Create services by injecting repo factory
userService := NewUserService(repos)
fooService := NewFooService(repos)
barService := NewBarService(repos)
bazService := NewBazService(repos)
}
Questo approccio conferma ancora i principi SOLID? Rende lo sviluppo molto più veloce e utilizza ancora il DI, quindi non vedo perché non possa ancora essere testato perché potrei semplicemente prendere in giro la classe factory del repository nello stesso modo in cui avrei preso in giro le singole classi del repository ...
Consiglieresti questo approccio, perché / perché no? Grazie.