ecotoneframework / ecotone-dev

Ecotone Framework Development - This is Monorepo which contains all official public modules
https://docs.ecotone.tech
Other
37 stars 16 forks source link

Provide the ability to use existing entities in commands for cascading persistence of state aggregate. #307

Open lifinsky opened 6 months ago

lifinsky commented 6 months ago

Description
In aggregates, it's possible to use, for instance, Doctrine entities with cascading persistence, but only for their insertion. Because ObjectManagerInterceptor clear object manager on transaction start.

Example
The only way to correctly save an aggregate with a one-to-many or many-to-many collection of entities is to inject the repository into the command handler of the aggregate or to use a "Before" interceptor that adds entities into the command before passing it to the handler.

lifinsky commented 6 months ago

Current possible solution: Add interceptor on #[Before(pointcut: CommandHandler::class)], that takes the command with an entity id, then uses the repository to find the entity and returns the same or a different command instance, but with the entity instead of the id.

dgafka commented 6 months ago

Can you provide PR with failing scenario?

lifinsky commented 6 months ago

Hi. I will add code snippets describing the problem. After clear all existing in database entities doctrines are perceived as new and their insertion and binding occurs, instead of just binding to the aggregate

lifinsky commented 6 months ago

@dgafka https://gist.github.com/sushchyk/8ce3c718270f0ac75eb07264f899722a

dgafka commented 6 months ago

So the case is that when we've main Entity (Aggregate Root) like Post and we change it's child Entities Tag, those child Entities won't be saved in database. And this is because we've not have not called ->persist(Tag) on each of them, so Doctrine does not know that they are meant to be saved.

@lifinsky do I understand the problem correctly?

lifinsky commented 6 months ago

No, existing tag was saved but as new record (doctrine allows cascade: ['persist']):

// ObjectManagerInterceptor
            foreach ($managerRegistries as $managerRegistry) {
                /** @var EntityManagerInterface $objectManager */
                foreach ($managerRegistry->getManagers() as $name => $objectManager) {
                    if (! $objectManager->isOpen()) {
                        $managerRegistry->resetManager($name);
                    }
                    if ($this->depthCount === 1) {
                        $objectManager->clear();
                    }
                }
            }

I think we should clear only agregates in unit of work. Entity manager supports clear($entityName = null) - null|string $entityName – if given, only entities of this type will get detached.

lifinsky commented 6 months ago

Currently for this example we should find tag in interceptor with pointcut "before" on CommandHandler (after transaction start)

dgafka commented 6 months ago

So the problem is that we create Tag Entity before outside of Post Aggregate. As Post Aggregate is created via Command, Unity of Work is cleared, therefore the reference to Tag is also cleared. This makes Doctrine ORM think this is new Entity, which need to be inserted instead of updated.

@lifinsky is this understanding correct?

lifinsky commented 6 months ago

Problem in using of existing entity, with current functionality of Ecotone we should inject tag repository to aggregate class or use command handler interceptor that find entity by id and fill attribute in command or return new command object (command is ugly with default arguments with null, for example int $tagId = null, Tag $tag = null. We should not clear entities in unit of work before command transaction

dgafka commented 6 months ago

We should not clear entities in unit of work before command transaction

I think, it's fine to set up clearing after, as long as we can clean with each Message, we are good. Feel free to provide a PR with a test case you mentioned.

benr77 commented 2 months ago

I'm running into this same issue. Did anything get sorted in the end as the last comment is quite a while ago and I can't find any related PR. Thanks

lifinsky commented 2 months ago

I'm running into this same issue. Did anything get sorted in the end as the last comment is quite a while ago and I can't find any related PR. Thanks

This issue is actual and not resolved