Service register best practices
I have been using do for a while. But I have a confused about service register
a typical service:
type Service struct {
meegoMaterial *material.MeegoMaterial[material.MeegoMeta] `do:""`
docMaterial *material.DocMaterial[material.DocMeta] `do:""`
anyMaterial *material.AnyMaterial[any] `do:""`
}
func NewService() (*Service, error) {
return di.InvokeStruct[Service]()
}
And I add service.Register() function in main.go.
func Register() {
di.Provide(material.NewMeegoMaterial)
di.Provide(material.NewAnyMaterial)
di.Provide(material.NewDocMaterial)
di.Provide(report.NewService)
}
It works, But when I testing report:
func init() {
service.Register()
}
func TestService_Report(t *testing.T) {
}
Circular dependencies error:
# ***/biz/service/report
package ***/biz/service/report
imports ***/biz/service
imports ***/biz/service/report: import cycle not allowed in test
I register service in test init func without report now.
func init() {
di.Provide(material.NewMeegoMaterial)
di.Provide(material.NewDocMaterial)
di.Provide(objectstorage.NewService)
di.Provide(material.NewAnyMaterial)
}
But I think it is ugly, Is there a better way? all in one service.Register() maybe no need to exist?
Of course, this is not do is problem, It should be my problem with using and understanding.
Yes, declaring services globally might create import cycles sometimes.
In v2-beta6, we added support for "do.Package" -> https://do.samber.dev/docs/getting-started#register-services-using-package-declaration
You can create a do.Package in ***/biz/service/ for a subset of your app.
go get -u github.com/samber/do/[email protected]
If you currently use v1, check the migration guide: https://do.samber.dev/docs/upgrading/from-v1-x-to-v2
I currently use v2 in my own project. You can consider it almost production-ready. Some APIs might change before release.
Thx, I try to do.Package.
I used v2 in production for some time, it is very good.
Hello samber, do.Package doesn't seem to solve my problem.
Maybe I didn't describe it in detail enough. I create a demo: https://github.com/iyaozhen/samber-do-learn/blob/main/service/car/car_test.go
import cycle:
package github.com/iyaozhen/samber-do-learn/service/car
imports github.com/iyaozhen/samber-do-learn/service
imports github.com/iyaozhen/samber-do-learn/service/car: import cycle not allowed in test
TestCar_Start_V2 It is work, but I will add lots of do.Provide.
service/register.go, Should it not exist?
I should use do.Package in car? But engine used in a other car2 service, car and car2 Lazy/Provide Duplicate?
Ok, i see your issue.
Maybe you could move your tests to a dedicated package? 🤔
I don't know the complexity of your app, but you can also have a do.Package in 2 sub-packages (here: service/engine/ and service/car/) and import them in tests: do.New(engine.Pkg, car.Pkg).
I don't know if this issue is related to DI, since you would have the same problem in traditional programming.
Did you try with interfaces? Using do.InvokeAs might prevent some troubles.
Thank you for your patient answer. There's no particular solution for me.
move your tests to a dedicated package
not golang customary norm
2 sub-packages
car.Package
do.Package(
do.Lazy(engine.NewEngine),
do.Lazy(NewCar),
)
airplane.Package
do.Package(
do.Lazy(engine.NewEngine),
do.Lazy(NewAirplane),
)
https://github.com/iyaozhen/samber-do-learn/blob/main/service/car/car_test.go
panic: DI: service `*/service/engine.Engine` has already been declared
interface
interface is a good way. But it will make the project more complex. In general, there is only one implementation of an interface.
This is not do is problem, the same problem in traditional programming.
I'll close this issue first, If there is a good solution, I will come back to update.