Today, while reading 97 things every programmer should know (disclaimer: affiliate link), I came across an explanation of the Single Responsibility Principle that finally clicked for me. I've read a fair amount of literature on Best Practices for programming, but I've often faced trouble in applying these best practices in real life projects. Here was an explanation that finally made sense.
The Single Responsibility Principle says that each class should have only one reason to change. This makes perfect sense in theory, but somehow I just couldn't find a way to implement it in practical code. However, after reading this chapter in the above book, I realized that I was getting the semantics wrong - objects in OOP don't have to correspond to objects in the real world. You don't need to have a single (possibly quite large) class for an Employee that handles the business logic related to Employees (eg. calculating wages, sick days, etc), as well as the database logic (inserting into and retrieving from a database), as well as any report generation logic - classes should be more flexible and correspond to what makes them easier to maintain.
The chapter linked above, for example, says that instead of having an Employee class with functions that handle business/database/reporting logic, it makes more sense to have an Employee class that handles the business logic, then an EmployeeRepository class for the database logic and an EmployeeReporting class for the reporting logic. This means that if the database logic ever needs to change, I will only be touching the EmployeeRepository class that handles only the database logic. The business and reporting logic will remain untouched in their respective classes. Now, each class truly has only one reason to change.
Conclusion: don't be rigid in insisting on having classes map to real-world entities. Code your classes based on what makes them easiest to read and maintain.