Зачем задавать приватный модификатор доступа для свойств класса?
У вас очень логичный вопрос на счет сеттера: сеттер полностью уничтожает задумку с инкапсуляцией. Правильно это называется "семантическое нарушение инкапсуляции" — то есть как-бы мы инкапсулировали, но по факту назад открыли прямую запись в свойство напрямую. Все доводы "ну мы можем в сеттере валидировать" не корректны

Правильным способом будет не использовать сеттеры. Вот моя статья на эту тему:
https://habr.com/ru/post/469323/

Более подробно: каждый класс нужно проектирвовать так, чтобы данные внутри были максимально связаны. Например каждый метод работать должен в хорошем случае с максимальным числом полей класса, тогда у него высокий cohesion... В тоже время снаружи наужно работать с максимальн омалым числом метода, тогда будет низкая связанность (coupling). Это пара принципов из GRASP.
Когда вы делаете сеттеры и геттеры, то у вас данные внутри между собой почти никак не взаимодействут: с геттером работают снаружи, с сеттером работаю снаружи — весь класс нараспашку, а в нем в 100% случаев появляются данные, которые вместе не должны находиться и никак не связаны — анрушена и абстракция и инвариант и много чего еще...

Про инвариант отдельно: например есть платеж, у него есть значение, с которым платеж инициирвоан (initValue), есть значение холда (holdAmount) и есть значение чарджа на списание (chargeAmount)
Когда вы работаете с платежом, контролируя ивнариант в самом классе, то ваш каждый метод првоеряет др значения и позволяет перейти к др состоянию... методов будет 2-3, все инкапсулировано и безопасно.

Например:
class Payment {
     pub func charge(amount int) void {
          if (this.holdAmount < amount && this.initAmount < amount) {
               throw new PaymentException('Unavailable charge amount')
          }

           if (this.status === PaymentStatus:finish) {
               throw new PaymentException('Payment already fisnished')
          }

          this.chargeAmount = amount
          this.holdAmount -= amount
          this.status = PaymentStatus:finish
     }
}

Тут в одном методе полные проверки и класс сам контролирует все состояние внутри, также соблюдается закон Деметры. У даннго кода высокий cohesion (из GRASP), тк внутри идет плотная работа с внутр данными (значит они корректно тут закроекны) и низкий coupling ( с теми данными для данного кейса только один метод работы, все внутри)

Когда вы раскроете сеттерами и геттерами, то ваше состояние становится непредсказуемо и полагается только на то, что снаружи точно подумали об инварианте (нет)
нет, не проще. 
С помощью методов можно задать критерии для изменения этих самых свойств. 
Допустим есть класс:

class Entity {

    private static array $ids = [];

    private static int $currentId = 0;
    
    private int $id;

    public function __construct() {
        $this->id = self::$currentId++;
        self::$ids[] = $this->id;
    }
    
    public function getId(): int {
        return $this->id;
    }

    public function setId(int $id): bool {
        if ($id < 0 || isset(self::$ids[$id])) {
            return false;
        }

        unset(self::$ids[$this->id]);
        self::$ids[] = $id;
        $this->id = $id;

        return true;
    }

}

Вы можете легко оперировать данными и делать им разные проверки.