AR focuses on data. Moreover, Eloquent makes this data public. Objects designed around an Eloquent model assume public access to those pieces of data, so encapsulation is harder and cohesion is blurred. Tracking where properties are accessed or modified is harder, even with advanced IDEs, because of magic properties and weak type hinting.
In the context of a Laravel project, data gets validated as arrays (most likely user input extracted from the Request) and, later on, added to an Eloquent model through generic methods:
According to its author, "Laravel has no opinion on where or how you do validation." By default, all your models will inherit generic methods to be constructed or updated:
Model::create(array $attributes);
Model::update($id, array $attributes);
$model->fill(array $attributes);
new Model(array $attributes);
This behavior moves the responsibility of validation and protecting invariants outside of the model. Depending on the architecture of the project, this may be a service layer, a command or an http layer (controller, form request). Having this responsibility outside of the object makes for weak objects, whose state can't be trusted to be valid.
Intercepting this behavior is very difficult, as it involves either overriding multiple methods of the Model class, some of which the Model itself assumes to be safe to call (empty constructors, for example).
While it is entirely possible to do performance optimizations in the AR pattern, Eloquent lacks work in this area. It has no IdentityMap to prevent hitting the database for the same record, does not handle join queries into joined models (with possible column collisions if done wrong!) and has removed it's small cache implementation since version 5.0.
The only methods that allows for query optimization are the with / load
relation eager-loading methods.
Implementing any sort of cache means intercepting internal ORM calls, which by the level of coupling between Model, Query\Builder and Relations, would need to be done at the ORM level. Overriding methods at the Model level cannot accomplish any of this.
Eloquent models inherit a large, generic API that assumes all models will need. This can be a problem when working in large teams, because knowledge of how things should be done is passed through convention instead of enforced by code. For example, a Model::all()
method call could be a self-destruct button in a rather large table. Preventing calls from an already existing public method is more difficult than preventing the creation of a yet-to-be-added method.
While this is true for Doctrine as well (generic repositories also have self-destruct findAll()
methods), Eloquent using inheritance makes this worst: it gets all of these methods closer to the consumer, which is a negative point in the case of unwanted API, and it statically couples to it, so you can't hide it behind an injected dependency.
DataMapper assumes that the object's state can be modeled in a relational manner, but most of the times we end up adapting our modeling decisions to this restriction. This topic is older than Doctrine itself, and while DMs have evolved through the years, it's still a very important constraint.
While database access and usage is no simple task, the mapping layer adds an extra level of complexity to it, one that ActiveRecord explicitly avoids. Reconsitution of objects from the database is dealt by the ORM, and that assumes an internal structure of the mapped objects, which also limits design. Hooking to those processes is possible, but demonstrates how much more complex it is than just overriding a method.
Anemic Domain Modeling is modeling objects with public setters and getters and no real behavior outside of transporting data around. This incurs in the cost of domain modeling, without the benefits of actually adding behavior related to the domain, as described by Fowler in his bliki.
While this is not intrinsic of the DM pattern, it does have something to do with it. In an empty AR model, behavior is always present: AR gives you database access for all your models. But if you design an Entity that does not know about database and does not have any relevant behavior, then you are arguably worst than with AR.