DDD聚合根,以哪种方式创建具有不同类型的新子聚合元素? 工厂方法? [关闭]

Suppose we have Promotion as an Aggregate Root and Rules to satisfy a promotion as an Aggregate. Rules are a collection of Different Rule Elements that extends Abstract class Rule.

As I learned I can use factory method, example:

class Promotion (
    PromotionIdentity $identity,
    string $name,
){
    $this->identity = $identity;
    $this->name = $name;
    $this->rules = new RuleCollection();   
}

public function addRule(
    RuleIdentity $ruleIdentity,
    RuleType $ruleType,
    array $configuration
) {
    if (RuleType::EMAIL_LIST === $ruleType->value()) {
        $makeRule = new EmailListRule($ruleIdentity, $configuration);
        $this->rules->add($makeRule);
    }

    if (RuleType::MIN_ARTICLES === $ruleType->value()) {
        $makeRule = new MinimumArticlesRule($ruleIdentity, $configuration);
        $this->rules->add($makeRule);
    }

    ... and so on, for example 15 rules
}

I think that this can grow a lot, I see a code smell here.

It's ok to keep this logic of rule creation inside aggregate root?, can we move this responsability of rule creation to the application service within a factory and pass the builded Rule to the addRule Method? Other Options? Thanks Friends!

Rules to satisfy a promotion as an Aggregate

Well, an AR cannot hold instances of other ARs, so Rule may not be an AR here. Furthermore, at first glance and without knowing your domain, I see very little reason for Rule to even be an entity from the domain's perspective (it could from the storage perspective). Couldn't Rule just be a value object, entirely replaced if needed to be changed?

//Application service
changePromotionRules(String promotionId, Set<Map<String, String>> rulesConfig) {
    Set<Rule> rules = ruleFactory.rulesFromConfig(rulesConfig);
    Promotion promotion = promotionRepository.promotionOfId(new PromotionIdentity(promotionId));
    promotion.changeRules(rules);
}

//Promotion AR
changeRules(Set<Rule> rules) {
    validateRules(rules);
    this.rules = rules;
}

The above example assumes a CRUD-like behavior where all rules are replaced at once, but you could do the same with more granular addRule/removeRule operations which may be more suited.

If you have a disconnect between the UI behavior (e.g. all rules saved at once) and the domain (e.g. addRule/removeRule) then you could adapt this in the application service layer.

E.g.

//Application service
changePromotionRules(String promotionId, Set<Map<String, String>> rulesConfig) {
    Set<Rule> rules = ruleFactory.rulesFromConfig(rulesConfig);
    Promotion promotion = promotionRepository.promotionOfId(new PromotionIdentity(promotionId));
    withSetDiffBetween(rules, promotion.rules(), new SetDiffHandler<Rule>() {
         withAdd(Rule rule) { promotion.addRule(rule); }
         withRemove(Rule rule) { promotion.removeRule(rule); }
    });
}