Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

cjconnor24's avatar

Data Modelling and Business Rules

Hi guys,

I have a question, which for some of the more experienced guys out there, might seem a bit mundane but - I'm a third-year software engineering student and would certainly love to hear your thoughts on the matter.

We do a ton of data modelling, have done since way back in first year, and it's obviously pivotal to get right when designing applications - especially in MVC from a model and associations point of view.

My question is this - when it comes to start to building the application...where do you start to implement the restrictions for business rules?

Lets take a restaurant booking scenario for example, rough model entitites would be as follows:

Entities

  • Customer
  • Booking
  • Table

Relationships

  • Customer -< Bookings
  • Table -< Bookings

-< (has many)

Question

So this is where the main point of my question is... obviously, the restaurant has a finite number of tables, and each table can only be booked for say 2 hours slots during the restaurants opening times...

Where and how, in the architecture of the application, would these sorts of rules be implemented?

Like I say, this may seem really rudimentary for a lot of you guys but it's something that has been puzzling me for a while and I haven't had a great deal of luck on Google.

Any tips or advice would be much appreciated!

Thanks, Chris

0 likes
7 replies
Robstar's avatar

You'd most likely end up with a pivot table consisting of the booking_id and table_id columns. However, you could also store the table_id on the booking table (this would limit the number of tables per booking to one, which may or may not be desirable).

You'd restrict bookings on the application side but could also restrict it at the database level using a compound primary key on the pivot table.

For application level side have a read of https://laravel.com/docs/5.5/eloquent-relationships

1 like
martinbean's avatar

@cjconnor24 You want to implement logic like that as close to your model as possible, because you may have multiple entry points where a booking can take place: a web UI, a PoS system, an API, and so on. If validation is separate from the model, then that’s a lot of places you need to duplicate your validation and keep in sync, and duplication’s bad for that reason.

Somewhere, you’d have a method that “books” a table at a restaurant, so you could put your availability checks in there:

class Restaurant extends Model
{
    public function bookTable(Table $table, DateTime $dateTime, Customer $customer, int $guests)
    {
        if ($table->restaurant->isNot($this)) {
            throw new InvalidTableException('Table does not belong to this restaurant.');
        }

        if ($table->unavailableAt($dateTime)) {
            throw new TableUnavailableException;
        }

        if ($guests > $table->capacity) {
            throw new TableOverCapacityException(
                sprintf('Table only has capacity for %d guests.', $table->capacity)
            );
        }

        return $table->bookings()->create([
            'customer_id' => $customer->getKey(),
            'datetime' => $dateTime->toDateTimeString(),
            'guests' => $guests,
        ]);
    }
}

Obviously this is a simplified example.

2 likes
robrogers3's avatar
Level 37

It would be helpful to have more scenarios. Instead of us trying to think them up.

But let's take yours, and expand on it.

the restaurant has a finite number of tables, and each table can only be booked for say 2 hours slots during the restaurants opening times...

a question could be: what kind of check needs to be made before say you book a table for 2 at 6PM.

so lets start with tdd and figure out how this might work, starting with a feature test.

test: a_table_for_cannot_be_booked_at_when_there_are_no_tables_available

/** given all tables are booked */

//what's a booked table? humm?

//this means a table needs to know if it's booked. //$table->booked() ??

/** when you attempt to make a reservation */

//how do you reserve a reservation?

//maybe reservation()->make($numberOfPlaces, $reservationTime) ??

// it it checks for tables for the Timeslot that can accommodate the number of parties requested. // crud, whats' a Timeslot?

//perhaps it returns true of false??

/** then, available tables should equal 0. */

//what is a available table ?

//this means a table needs to know if it's available?

//$table->available() ?

so, with this simple test idea, we know that:

  • You've got lots of tables each with a size
  • a Table knows a bit about itself. booked and available.
  • There's a thing called a Timeslot
  • You have many Reservations with associated Tables ** a reservation hasManyTables ** a reservation hasManyTimeSlots

And the major check point is in the Reservation class. it's the decider! But what does it decide? And what does it need to decide?

  • for starters, is there a Timeslot?

So where are the rules? i'd say close to Reservation::make() where make checks a number things, except in all likelyhood Reservation is also a model. (Is it doing too much stuff) Remember the first S in Solid (a class should only be responsible for one thing, or there should be only one reason to change it.

But, humm, where do we go from here?

Well how about a flushing out. Reservation with a unit test.

Reservation:tests

test: a_reservation_cannot_be_made_without_a_timeslot /** given there are no available Timeslots */ $timeslots = create('Timeslot', [availeble => false], 2); //what is available??

/** when we attempt to make a reservation */ $reservation->make($people, $time)

/** then the attempt to make the reservation it should throw a ReservationNotAvailable Exception <-- or something like that $this->assertException('NoTimeSlotAvalible') //this should be above reservation->make()

Well now we have to face the Timeslot problem/question. More, perhaps tables don't need to know about themselves as much.

So let's take a look at timeslot with a test

Timeslot Unit Test: test: you_can_check_for_available_timeslots //we need to come back to this one

test: a_timeslot_knows_if_it_it_is_available //given we have a timeslot

//and the timeslot is used //what's used mean?

//then a timeslot is not available

test: a_time_slot_knows_it_is_used_for_a_given_time() //this means we must mark somehow that a timeslot is used. also do we need times?

test: a_time_slot_can_be_marked_as_used.

test: timeslots_can_know_if_there_are_timeslots_unused_for_a_given_time.

test: timeslots_must_have_a_table

test: tables must have a seating property

well this is getting super long.

but what have we learned so far:

Reservations actually depend on timeslots.

Timeslots are really just time and table based

Timeslots depend on tables.

Tables have capacity

The rules are a bit more spread out then we anticipated (different objects need to know about various states). But the checking of rules doesn't need to be spread out. as in:

  • it must check for timeslots for time and capacity

  • there are likely a set number of timeslots

  • timeslots likely have a given time attribute. //humm, is Time a thing?

  • timeslots must check for tables for capacity

  • tables have capacity attributes (and likely a booked time)

And most likely place to check for availablity starts in reservation->make()

  • it check Timeslots for availablity
  • Timeslots checks for unbooked tables for a given time and table capacity.
  • Tables can should know availability by capacity

if all these rules pass, then Reservation::make() can return true or what not. and of course it updates properties on Timeslots, and tables.

Now, where do place the various Business Rules.

  • a lot of people say a rule should not be on the Model. but it could be.
  • you could have a Rules collection of sorts which depend on the models attributes.

I think depending on the complexity you could pick one or the other.

But again, in my humble opinion Reservation is the place to run them.

2 likes
cjconnor24's avatar

wow! first-off, thank you guys so much for taking the time to reply - this info is awesome.

Like I said, I'm pretty much just starting out properly with MVC - I completely get the architecture and I built an app using Laravel for my end-of-year project last year, but its these kinds of things you just don't learn at Uni!

@martinbean - That made a lot of sense - been reading on skinny controllers / fat models but there seems to be some debate there also :)

@robrogers3 - that was seriously awesome. It's that test-driven approach I really want to try and get to in terms of my thinking and development approach. Obviously, you need a good level of knowledge and experience to properly take that approach. So helpful and was great how quickly your approach led to identifying the other entities in order to facilitate the functionality.

Thanks again all - definitely my favourite online community for quality - non-judgmental advice :D

robrogers3's avatar

by the way there is a recently published TDD series on laracasts. It's the best I've seen on TDD.

Please or to participate in this conversation.