How are brew services different from launchctl services?

Brew handles the complexity of parsing each package and then issues the appropriate launchctl commands for you. So in the end, launchctl and launchd do the lifting, brew just is an intermediary to make it easier for you to get going.

% brew services -help

Usage: brew services subcommand

Manage background services with macOS' launchctl(1) daemon manager.

If sudo is passed, operate on /Library/LaunchDaemons (started at boot). Otherwise, operate on ~/Library/LaunchAgents (started at login).

This is all covered well in the first 6 lines of the help and you can inspect the code in github if you are curious how this actually works behind the scenes.