Вопрос: Правильная ли декомпозиция базы данных?

Лучший ответ:
я бы сделал так:
есть набор мест, которые как-то там характеризируются (пускай это будет сектор, ряд и место - на самом деле это неважно) и каждая такая запись имеет свою айдишку:[Seat]
id
sector
row
number
на сколько я понимаю, покупаются/резервируются места на некоторое мероприятие (матч, концерт и т.д.). соответственно, должна быть таблица этих событий. дл простоты охарактеризируем ее только датой. нам и дата не нужна, но пускай себе будет[Event]
id
datetime
резервирование или покупка места должна относиться к таблице Seat и к таблице Event:[Reservation]
id
seat_id
event_id
// another fieldsSeat к Reservation как one-to-manyEvent к Reservation как one-to-many
пользователя можно втулить сюда же, но бы выделил связь с юзером в отдельны сущности: Ticket как билет и еще одну сущность как факт продажи билета некому юзеру TicketTransfer. но это уже зависит от вашего сервиса, который вы реализуете
я считаю, что так будет правильно

Худший ответ:
В целом, у вас всё достаточно просто. Я предложу вам вариант схемы, а вы сравните его со своей.
Попытаемся рассуждать в терминах множеств - это всегда полезно при проектировании реляционных БД, т.к. отношения - это множества.
Итак, у вас есть множество мест, доступных для бронирования. Будем считать, что БД обслуживает только ОДИН стадион. Сектор, ряд и номер места - это всё иерархические координаты места, однозначно это место определяющие. Таким образом, ключом для места является (sector, row, number).
Далее нам нужно решить, как мы будем представлять множество ВСЕХ мест. Вы указали, что количество мест известно заранее. По идее, в этом случае нам даже не нужно хранить множество этих мест, т.к. оно определяется правилом, однако рано или поздно вы столкнётесь с тем, что правило необходимо нарушить - например, какое-то из мест в данный момент оказалось непригодным для продажи билетов (сломали стул, отвалился порог, и т.д.). Поэтому ИМХО стоит всё-таки завести отношение "Место" для хранения множества ВСЕХ имеющихся мест, как вы собственно и сделали. Т.е. мы имеем первое отношение: Seat(sector, row, number).
Теперь мы хотим хранить множество забронированных мест. Т.к. событий на стадионе будет много, для каждого из событий мы будем иметь своё множество забронированных мест. Значит, в первичный ключ отношения "забронированное место" должен попасть первичный ключ события. Предположим, что первичный ключ события это id (т.к. других подробностей вы не указали). Остальные атрибуты отношения "забронированное место" должны ссылаться на одно из имеющихся мест, т.е. у нас должен быть внешний ключ в отношение "Место" (Seat). Итак, мы имеем второе отношение: ReservedSeat(event_id, sector, row, number). При проектировании реляционной БД очень важно четко понимать, что значит НАЛИЧИЕ или ОТСУТСТВИЕ записи в каждом из отношений. Наличие записи в отношении ReservedSeat говорит нам, что конкретное место забронировано на конкретное событие. Ни больше, ни меньше. Если некоторой записи в отношении ReservedSeat нет, значит конкретное место на конкретное событие все еще свободно.
И да, кажется мы забыли главное - а кем место-то занято? Нам нужен еще один атрибут, внешний ключ в отношение User. Добавим его: ReservedSeat(event_id, sector, row, number, user_id). Важно, что этот атрибут не входит в первичный ключ, т.к. разные клиенты не могут забронировать одно и то же место (т.е. место всегда бронируется кем-то одним).
Собственно, всё. Отношение User переписываем как есть, т.к. вы не указали подробностей о том, что там хранить. Отношение Event я придумаю "с потолка", добавив туда только атрибут name помимо ключа id.
Итого (атрибуты, входящие в первичный ключ, выделены жирным):
User(id, name, surname);
Seat(sector, row, number);
Event(id, name);
ReservedSeat(event_id, sector, row, number, user_id); внешние ключи: event_id -> Event(id), user_id -> User(id); (sector, row, number) -> Seat(sector, row, number).
Надеюсь, процесс принятия решений описан достаточно понятно. Если остались вопросы - задавайте.