Taken to the extreme, you could model all intrinsic constraints and triggers at the relational database level, and have a perfectly functional anemic domain model.
Just use immutable data structures and be done with it. In departing from old OOP views and becoming more functional programming and data oriented programming friendly, C# introduced Records, which are immutable. Probably Java and Python have similar constructs. Javascript allowed the use of immutable data since long time ago.
If you insist of using fat models, you will still mutate the data all over the place by doing calls, but you just obfuscate it.
In Python, the closest you can get is a "frozen" dataclass, but you don't get true immutability[0]. What you _do_ get is effective enough for just about all practical use cases, except for the standard foot guns around mutable data structures.
@dataclass(frozen=True)
class MyModel:
...
[0]: https://docs.python.org/3/library/dataclasses.html#frozen-in...For instance, if you are updating a ShoppingCart model, all of that code which creates/updates/deletes a ShoppingCart could be kept in the ShoppingService - which will also create/update/delete the ShoppingCartItem models which are the line items for each item in the carts. So you don't have one Service class per table - but rather one service class per module of functionality.
Personally my take is business logic should be in the services and object specific validation in the like can be in the model. Unless your business logic is meant to deal entirely with single object types at a time you can hardly fit it in the pure OOP dogma. A behavior that deals with ModelA and ModelB seems just at home on serviceAB as it does on either model, from an OOP sense.
Domain models are hard to do well and I think the SOA era brought a lot of confusion between data transfer objects, serialized objects, anemic domain models, and domain models.
[0] https://martinfowler.com/bliki/AnemicDomainModel.html