I'm still a beginner when it comes to domain driven design, and I am trying to model something like an RPG's battle system as a bounded context. I am trying to model a bounded context in which a Combatant has a list of abilities, and each Ability has a list of effects that it would apply. For example, a common Effect would be to deal damage to the target Combatant.

As far as I can tell, the two aggregate roots would be Combatant and Ability, with Effect being a value object.

Now, I am not certain where I should be putting my logic for the interaction between Combatants. In particular, I will need logic that handles the execution of an ability taking into account the source Combatant and the target Combatant. Likewise, I will need similar logic for the individual effects of an ability. Since the action and effects are being caused by one instance of a Combatant and are targeting a second instance of Combatant, I don't think the logic should be done within the Combatant aggregate itself.

The problem is that I just don't know where the logic should go. I initially thought a domain service would be the right spot, but after researching them, I think I may have been wrong. Does anyone have any insight as to where I should be putting this logic?

有帮助吗?

解决方案

The business logic that coordinates processes involving multiple Aggregates should stay in a Saga/Process manager. This does not mean that all the logic should stay there. No. Only the coordinating logic. The actual implementation depends on the programming language and the architecture but I could give you and example.

For example, let's suppose that a combatant inflects damage (fires) to other combatant. The firing process is handled by a Saga. But every combatant should have two command methods: inflectDamageTo and getDamagedBy. The Saga loads the shooter aggregate, calls shooter.fireAt(shootedId, effect) and persist the changes. Then it loads the shooted aggregate, calls shooted.getDamagedBy(shooterId, effect) and it persist the changes.

Before and after each step the Saga could persist its progress. This could be as simple as an Enum. You do this in order to have enough information to recover in case of something goes wrong and the Saga is forcefully stopped (i.e. process is stopped, server is restarted etc).

Keep in mind that in this process you have two transactions, not only one (or none, depending on the type of persistence). Also, notice that each aggregate receives only the ID of the other and not a full reference.

P.S. There are some domains where the performance is more important than the maintainability of the code or the consistency of the data; I base my answer on the fact that you have analyzed your business requirements and you still want to use the DDD approach.

许可以下: CC-BY-SA归因
scroll top