На пальцах: чтобы бот работал, его функции должны вызываться при наступлении определённых событий (например, входящего сообщения). Проблема в том, что aiogram знает о возможных событиях, но ему нужно сказать, какие функции когда вызывать. Обычно это называется "зарегистрировать обработчик".
В питоне функции - объекты первого рода, т.е. их можно сохранять в переменные, передавать как параметры, возвращать как результат и вообще поступать с ними как с любым другим значением.
Т.е. по идее можно было бы сделать так:
async def echo(message: Message):
text = f"Привет, ты написал {message.text}"
await bot.send_message(chat_id=message.from_user.id, text=text)
dp.register_function_for_message(echo) # это не настоящий метод aiogram, только пример
Тогда каждому объявленному обработчику событий соответствовал бы вызов метода, ответственного за регистрацию этого обработчика.
Но то же самое можно сделать через декораторы. Декоратор - это сокращённый вызов функции, которая принимает в качестве параметра другую функцию.
Т.е. вот это
@dp.message_handler()
async def echo(message: Message):
text = f"Привет, ты написал {message.text}"
await bot.send_message(chat_id=message.from_user.id, text=text)
абсолютно эквивалентно вот этому
_decorator = dp.message_handler()
async def echo(message: Message):
text = f"Привет, ты написал {message.text}"
await bot.send_message(chat_id=message.from_user.id, text=text)
echo = _decorator(echo)
Как видишь, очень похоже на "простую" регистрацию обработчика, но записывается чуть короче.