In general, in Kubernetes, a controller is a running program that observes some Kubernetes objects and manages other objects based on that. For example, there is a controller that observes Deployments and creates ReplicaSets from them, and a controller that observes ReplicaSets and creates matching Pods. In this case, the ALB ingress controller observes Ingress objects and creates the corresponding AWS ALB rules.
Nothing stops you from doing the same setup by hand, or using a tool like Terraform that's a little more specialized for managing cloud resources. Using Ingress objects is a little more portable across environments (you can use a similar Ingress setup for a local minikube installation, for example). This approach would also let you use a single tool like Helm to create a Deployment, Service, and Ingress, and have the ALB automatically updated; if you delete the Helm chart, it will delete the Kubernetes objects, and the ALB will again update itself.
It's also possible that a development team would have permissions in Kubernetes to create Ingress objects, but wouldn't (directly) have permissions in AWS to create load balancers, and this setup might make sense depending on your local security and governance requirements.