3

I am having little trouble with correct configuration of wire I have following setup

Router

func NewRouter(routes []RouterPath) AppRouter {
    r := &appRouter{
        routes: routes,
    }
    return r
}

Router interface

type RouterPath interface {
    Register(root *mux.Router) (p *mux.Router)
}

and i do have few controllers that implement this interface currently the best way how i find out how to make wire to solve DI was this

var routersSet = wire.NewSet(
    routers.NewAuth,
    routers.NewRoot,
    routers.NewUser,
    routers.NewPhpInfo,
)

func RouterProvider(info *routers.PhpInfo, root *routers.Root, user *routers.User) web.AppRouter {
    routes := []web.RouterPath{
        info,
        root,
        user,
    }
    return routers.NewRouter(routes)
}

func Init() Kernel {

    wire.Build(
        routersSet,
        RouterProvider,
        NewKernel,
    )

    return nil
}

what i have problem with is that i had to make transition layer to NewRouter because it expects array of routes. Which will grow very easily and method definition will be horible to maintain. I would love to see smtg like put wire.ProviderSet into array and use that as a parameter of NewRouter but i could not figure how to do this.

Is there any better approach instead of this ?

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412

1 Answers1

3

It's hard to see the whole picture of your application from just this small fragment, but it might be better to take in the dependencies of the routers that you're constructing into RouterProvider and just construct them in the function.

However, if you evaulate that and find that to be unsuitable, you still have some options. If you're only worried about the argument list to RouterProvider getting long, then you could use a struct provider:

type routers struct {
  Info *routers.PhpInfo
  Root *routers.Root
  User *routers.User
}

func RouterProvider(r routers) web.AppRouter {
  return routers.NewRouter([]RouterPath{
    r.Info,
    r.Root,
    r.User,
  })
}

If this gets really unwieldy, you could use reflection to zip up the fields into a slice:

type routers struct {
  Info *routers.PhpInfo
  Root *routers.Root
  User *routers.User
}

func RouterProvider(r routers) web.AppRouter {
  value := reflect.ValueOf(r)
  var paths []RouterPath
  for i := 0; i < value.NumField(); i++ {
    if p, ok := value.Field(i).Interface().(RouterPath); ok {
      paths = append(paths, p)
    }
  }
  return routers.NewRouter(paths)
}

I'd really recommend the first approach though, because it's much more clear to someone reading your code what's going on. Reflection always moves breakages to run-time instead of compile-time.

Ross Light
  • 4,769
  • 1
  • 26
  • 37