The border between database abstraction layer and business logic

Hello, everyone. I have a question about the architecture and where to put my code. The problem is I don’t have much experience with anything other than ActiveRecord and Rails, but I really would like to learn.

As far as I understand everything ROM does lies in database abstraction layer and has only one purpose - communication with database. However, entities which ROM returns are actually business entities which can have some business logic in them. My colleague thinks that this is not necessarily true. So we decided to ask a question here.

Let’s imagine that we have a sort of commerce system. There are orders in this system which have many line items, each with its own price. At some point in time order may have no line items at all, which is totally ok, and in this case we consider that the order’s total price is 0. An order also has many payments. When the amount of money paid is equal to the order’s total price we consider the order paid. When no payments exist, we consider the amount of money paid is 0, and the order is not paid.

Considering all the requirements above, I want to create a method paid? in my order entity which answers true if the order is paid and false if it’s not. In my opinion an order is a business entity which is actually an aggregate - a thing that always must be instantiated from the database with its line items and payments, so it can work with them as with simple arrays of plain ruby objects and answer properly if it is paid or not.
Yet my colleague have different opinion. He has several ideas:

The first one that there is no point in always instantiating order with its payments and line items. So in this case by default paid? should raise an exception and before using it to get real information we have to instantiate the order properly with some method from repository, .e.g by_id_with_line_items_and_payments(id).

The second idea is not to use entity at all and write a method in repository, something like is_paid?(id). However in my opinion this is business logic and belongs to entity not to repository.

Anyway, as I said earlier, we don’t have much experience. Could someone explain us what is the proper and the most convenient way to write something that answers if the order is paid?

This one is actually quite simple IMO. You need to explicitly store order’s status and update it once a new payment or line item is added. It’s too essential for the business logic to have it evaluated “on the fly”. Then I’d have a dedicated function/class that accepts payments and order lines and returns the correct status so that you can call this function from any place. Finally, I’d call this function after adding new payment or order lines to update order status. Of course, details will vary depending on your specific cases, it’s just I quit mixing data and logic in AR-style a long time ago, never felt better.