If you were to draw out all the objects in your system and their relationship to each other at runtime, that would be a graphical representation of your object model.
Object-oriented programming is about modeling the behavior of entities. It is a programming paradigm that, when used correctly, helps us build resilient systems that are straightforward to understand and extend. But we gain these benefits only by correctly using the object-oriented paradigm, which involves having a domain model that reflects what's being modeled.
Designs often start out with a good domain model but then degrade over time as new features are added. Features get hacked in and new behaviors are bolted onto existing objects rather than expanding the object model to include these new classes. This distorts the object model and makes it more difficult to understand. Behaviors, and even entirely new classes, can be hiding in long methods. Pulling them out makes the code clearer and cleaner.
Object-oriented programming languages allow us to define classes and instantiate objects, but I find some developers resistant to doing that, having the false impression that creating objects is expensive and makes a system less efficient. But creating object instances are cheap, and modern runtimes are optimized for this. We should be defining classes and making objects all the time.
Any group of behaviors that converge around a set of values or state should be defined as a class. Since a class can represent anything, we can think of it in many ways, but most fundamentally as a concept that aggregates behaviors, often around a common set of instance data. For example, a savings account aggregates behaviors like deposits and withdrawals around an account balance.
Classes and objects, their runtime representations, can contain data that represents its state. They can also contain methods that represent its behaviors.
Though classes may only contain data and methods, object-oriented languages provide a rich environment for modeling anything. Classes can represent anything from tangible objects to ephemeral ideas. They can be a part of a larger whole, or they can represent the relationship between different entities.
Classes can be anything.
And there's the rub.
We have to name our classes well. If we don't create good, intention revealing names, our object model becomes distorted. If we stuff behavior into another object that belongs to its own object, the object model gets distorted. If we fail to call out classes in our domain or spread responsibilities across multiple, unrelated classes, our object model gets distorted.
When an object model gets distorted it becomes hard to read and understand. Worse still, a distorted object model will often lack flexibility precisely where flexibility is most needed.
Going against a design to special case features can often lead to maintainability issues, so we want to keep our object model robust and clear. We want it to reflect reality: the thing we're modeling. The way we do this is by calling out classes when we find them. We must actively look for classes and continue to expand our object model as our knowledge of our system expands. This is how we keep a system maintainable.