The classic implementation of the application persistence layer is ORM. Its advantages and disadvantages have long been known and described many times, for example, by Martin Fowler. In short, the main problem with ORM is that OOP objects describe the behavior of entities, while ORM attempts to work with the data inside them. However, the ORM lacks information about which class fields represent the entity’s state that needs to be saved. To provide this information, you must mark these fields or describe the persistence logic elsewhere in the code. Often, ORM is used incorrectly due to a misunderstanding of its specifics and the universal nature of its implementation.
What is the alternative to ORM? We can view the problem differently: if combining an arbitrary object model with a relational data representation causes difficulties, we can limit the tool’s scope. However, simplifying the model often leads to stripping behavior from domain entities, leaving only data structures. This results in an anemic domain model, which is an anti-pattern.
I see the most appropriate solution as the explicit separation of domain entity state from behavior and helper fields. This can be achieved by aggregating separate state objects within entities. When entity state is explicitly defined, saving it (specifically, the entire state tree of the aggregation root and nested entities) becomes a simple technical task. It can be implemented as a separate universal library allowing for interchangeable storage backends, whether SQL, NoSQL, or otherwise.
Thus, the persistence layer can be implemented as a StateStore class—a thin wrapper over an ORM—explicitly separating domain entity state from business logic.