To sum up, I am going to ask:
a) It makes sense to prevent the use of direct object references between aggregates in all scenarios?
b) how can there be collisions in an optimistic concurrency control in relationships between aggregates using direct object reference but with lazy load?
Long explanation
Taking a look at classical resources about DDD: blue book, red book and lots of posts here, the rule of thumb is always the same: "different aggregates must be referenced between them by IDs not using 'direct objects reference'".
The arguments in favor are:
A) Aggregates with inferred object references are thus automatically smaller because references are never eagerly loaded ( red book)
B) It prevents updating more than one aggregate in a single transaction (If you don’t hold any reference, you can’t modify another Aggregate, red book)
C) It forces all connected ARs to be in the same database.
D) If you do EntityA->getEntitiesB() maybe you have a million B's and you are going to cause memory problems
E) If you go for a pessimistic concurrency control you are going to have a lot of blocks
F) You are going to have a lot of collisions using optimistic concurrency control
I know that in the DDD world you always have a big "depending on" and a lot of trade-offs, so IMHO, taking into account that I'm working in a PHP environment using Doctrine as ORM and the application will not grow to sharding.
So, in my scenario I will refuse above reasons:
A) => is not a problem here, I am using Lazy Loading.
B) => if you pay attention and use doctrine DEFERRED_EXPLICIT you don't have problems.
C) => not problems because no database sharding is expected.
D) => It could be but, not planned, due to the size of the application or can be optimized specifically using a repository method and paying attention.
E) => Not using pessimistic concurrency control
F) => no idea¿? I have been doing some POCs and I don't get a version increment in EntityA, when I do entityA->addEntityB(entityB) or entityA->removeEntityB(entityB).
And in this scenario, I guess I have more advantages using direct object references:
ADV1) The relationship is reflected in the domain (rich and encapsulated) and I can test my domain relationships in a domain layer.
ADV2) I don't need additional hops between domain, application layer, and repository to retrieve relationships (at least not the biggest ones).
So my questions are:
a) It makes sense to prevent the use of direct object references between aggregates in all scenarios/applications?
b) how can there be collisions in an optimistic concurrency control in relationships between aggregates using direct object reference but with lazy loading?
References:
Red Book: https://www.informit.com/articles/article.aspx?p=2020371&seqNum=4
StackOverflow: Domain driven design and aggregate references
DDD in PHP: https://www.amazon.es/Domain-Driven-Design-PHP-Carlos-Buenosvinos/dp/1787284948
Lazy loading and domain: https://enterprisecraftsmanship.com/posts/domain-model-purity-lazy-loading