Azure / azure-dev

A developer CLI that reduces the time it takes for you to get started on Azure. The Azure Developer CLI (azd) provides a set of developer-friendly commands that map to key stages in your workflow - code, build, deploy, monitor, repeat.
https://aka.ms/azd
MIT License
387 stars 180 forks source link

Refactor action instantiation to support multiple hosts #4101

Open wbreza opened 2 weeks ago

wbreza commented 2 weeks ago

Today we organize our CLI command into actions. These actions can be dispatched through Cobra or through an HTTP server. Today we have forks of code to handle the dispatching but there is an opportunity to refactor this with a more elegant and easy to use system.

Initial idea is to introduce the concept of a router with multiple implementations. Once implementation is based on cobra command execution and the other is from the HTP server.

weikanglim commented 2 weeks ago

I think we're saying we want the HTTP server to strictly interact using "actions" and maybe we want the same type of system that allows you to attach actions to commands, or to server routes.

I do think this works in a world where the web server exposes the same endpoints as the command and we don't expect these two entry points to be too different. One caveat is that I have seen something play out quite differently when working on IDE integration in VS. We actually want the server to expose more than the CLI. This means exposing the very core business logic. In this world, having less layers and being able to just use any piece of code is a more important in my mind.

Just as an illustration, the only time we use actions in vsrpc is in environment_service_deploy.go.

With all that being said, I do think there's room for improvement here since we have started vsrpc consumption of azd. Looking at our current example:

 // simulate cmd registration of flags and args
ioc.RegisterInstance[*cmd.ProvisionFlags](container.NestedContainer, provisionFlags)
ioc.RegisterInstance[*cmd.DeployFlags](container.NestedContainer, deployFlags)
ioc.RegisterInstance[[]string](container.NestedContainer, []string{})

 // simulate cmd registration of actions by a name
container.MustRegisterNamedTransient("provisionAction", cmd.NewProvisionAction)
container.MustRegisterNamedTransient("deployAction", cmd.NewDeployAction)

// reference action by name
var c struct {
  deployAction    actions.Action `container:"name"`
  provisionAction actions.Action `container:"name"`
}

if err := container.Fill(&c); err != nil {
return nil, err
}

c.provisionAction.Run()

It could be more natural to use an action as a class object:

provisionAction := cmd.ProvisionAction{envName: environmentName}

// resolve DI dependencies
if err := container.Fill(&provisionAction); err != nil {
        return nil, err
}

provisionAction.Run()