2

DDD was chosen as an architectural pattern to tame a wild database model, as well as ubiquitous language. However, there is a sticky part of our model -- many of the rules are dynamic, and I don't know how to reconcile them with what I understand of DDD.

class Account {
    private Mailing[] mailings;

    void SubscribeToMailing(Mailing m) {
        if (/* this is allowed */) {
            mailings.Add(m);
        }
    }
}

The problem I am encountering is that the "this is allowed" check must be determined at runtime. There are conditional rules the system can be configured for. It makes sense to me to check these in a service, because the service can get a rule check injected into it. But, to do that would drive me towards an anemic domain model, which I've been led to believe Is A Bad Thing.

Where should these runtime checks happen?

edit for more clarification:

The product is multi-tenant, so "one size fits all" does not necessarily apply. The tenants are across multiple industries, so industry-specific rulesets cannot even be baked in there. There are known hooks in the system that configuration can happen, and that is the language we are trying to capture.

Rulesets could be something like "this product costs $5.35 each, unless the account is in this role, in which case the product costs $5.15 each". The roles are created by the tenants, the product pricing is created by the tenant, and even the decision to apply different pricing at all is created by the tenant.

7
  • How does this 'not play nice' with DDD? The whole point of DDD is to allow capture and express these kind of complex rules.
    – Euphoric
    Commented Sep 22, 2020 at 14:34
  • As specified in the second part of the post, the complexity is that the rules cannot be baked into the source. I don't know conceptually where to fetch the rules or how to provide them to the domain model so it can successfully do the test. Commented Sep 22, 2020 at 14:38
  • 1
    Simply pass the service as an argument to the domain model's method.
    – Andy
    Commented Sep 22, 2020 at 16:09
  • Can you tell more about what kind of checks can only be performed at runtime? Maybe an example? I've seen cases where a system like this was setup, because the customer didn't yet know what they want/need. So by making the system highly customisable the idea/hope was that the system would be flexible enough to support future changes without hiring expensive coders again.
    – Rik D
    Commented Sep 22, 2020 at 19:23
  • Why are the rules dynamic? If you had processes where you could easily edit, review, check and deploy your application code within an hour or two could you just as well use hard-coded rules?
    – bdsl
    Commented Sep 22, 2020 at 20:31

3 Answers 3

1

Where should these runtime checks happen?

If it is domain logic, then the checks should happen inside the domain model. If it is application logic, then the checks should happen in the application code.


If it is domain logic, and the configured information isn't copied into the data structure of the domain entity, then that configuration information would be passed to the domain model as an argument.

3
  • Curious how you differentiate between domain logic and application logic? What’s the fundamental difference? Commented Oct 12, 2020 at 16:50
  • Domain logic is derived from the problem you are trying to solve; application logic is an artifact of the fact that you are solving the problem with a computer program. Commented Oct 12, 2020 at 19:52
  • Do you think it’s possible to always keep a clear separation between the two? Or does it sometimes become blurred in the implementation details? Because the application has to know how to use the domain model anyway — which aggregates to load, which methods to call, in what order, etc. Commented Oct 12, 2020 at 19:57
0

How did you end up solving it, @bryan-boettcher?

I'm facing the same problem (including "multi-tenant with different roles and rulesets").

Here's what I'm considering (applying to your specific use case)

Application layer:

class SubscribeToMailingCommandHandler: ICommandHandler<SubscribeToMailingCommand> {
    Handle(SubscribeToMailingCommand command) {
        var mailing = _mailingRepo.GetById(command.mailingId);
        var account = _accountRepo.GetById(command.accountId);
        var tenant = _tenantRepo.GetById(account.tenantId);
        IRule[] rules = tenant.GetRulesForMailingSubscription();

        account.SubscribeToMailing(mailing, new MailingRulesetValidator(rules));
    }
}

Domain layer:

   interface IRule {
       bool IsBroken();
   }
  
   //domain service
   class MailingRulesetValidator {
       private IRule[] _rules;
       MailingRulesetValidator(IRule[] rules) {
          _rules = rules;
       }
       
       //returns broken rules
       public IRule[] Validate() {
           return rules.Where(r => r.IsBroken()).ToArray();
       }
    
       public bool IsAllowed() {
           return Validate().Length == 0;
       }
   }

   //domain entity
   class Account: Entity {
      void SubscribeToMailing(Mailing m, MailingRulesetValidator validator) {
         if (validator.IsAllowed()) {
           mailings.Add(m);
         }
      }
   }

this way, it's tenants responsibility to give me the rules. I have some of them static but some are just feature flags that can be turned on/off at runtime. Not sure if the MailingRulesetValidator is a Specification pattern or Strategy pattern but I saw in Evan's DDD book that he passes a BalanceCalculator into an Entity method in one of his examples so I guess that's "allowed"...

2
  • 1
    This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From Review
    – lennon310
    Commented Apr 14, 2022 at 1:06
  • This is what we did. I didn't realize it was acceptable to pass arguments like this to DDD verbs. Commented May 24, 2022 at 22:02
-1

DDD as practiced today by most, and described by most resources online and offline is about anemic models. The most I've seen people do is to have some trivial methods which work in complete isolation in the "domain objects" themselves, but that is basically it. For anything more there are "Services".

Just to be clear, there is a spectrum from anemic to rich objects. The more "Services" you have the more anemic your objects become, since you are taking behavior away from your model. In a completely "rich" object model there are no services whatsoever, and in a completely anemic model there are no methods in the "model" other than setters/getters.

So in short, if you want to have "rich" objects (i.e. with real behavior) you can not have DDD, or have to significantly re-interpret what DDD is.

7
  • I didn't think DDD prescribed an anemic or rich domain model. It was more about naming things and separating the major behaviors of an application into their own root objects so complex operations can be coordinated easier. From what I've read, DDD seems to lean towards a rich domain model more than anemic. That is, until you start applying other architectures on top of DDD, like Clean Architecture, Onion Architecture and Micro services. Then again DDD is not a kind of software architecture. I suppose the architecture you choose could drive you towards a rich or anemic domain model. Commented Sep 22, 2020 at 18:41
  • 2
    @GregBurghardt the DDD tactical patterns highly encourage rich domain models. DDD can also work nicely with the architectures you mentioned. Unfortunately many samples online are too trivial and don't contain any real business logic, others reading those articles/tutorials think that's the way it should be done, resulting in even more 'fake-DDD' projects.
    – Rik D
    Commented Sep 22, 2020 at 19:34
  • @RikD: The same calamity befalls most ASP.NET MVC example projects and tutorials, which people use to make more tutorials. It's a hot mess. Commented Sep 22, 2020 at 19:38
  • @GregBurghardt I didn't say it did. I've said it is widely interpreted/implemented as being mostly anemic. This includes projects I've seen, questions, articles, some books, like those from Vaughn Vernon, which are quite popular. Also, articles / examples about DDD with rich models are really rare. Whether it was intended this way I don't know, but this is where we are. Commented Sep 22, 2020 at 20:04
  • 1
    @RobertBräutigam The code doesn't contain much complex logic inside the domain, but it's not anemic. I did a quick scan and I didn't see services outside the domain that actually perform business logic. It is another example of sample code to illustrate an idea which is (in this case without the full context of the book) easy to misinterpret.
    – Rik D
    Commented Sep 23, 2020 at 7:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.