1

I've started reading Domain Driven Design.

I've got a situation where an Aggregate Root has a child object that needs DB interaction in order to perform it's business logic because one of the constants it uses is saved in the DB.

Example:

  • User creates a Customer
  • User creates a Review on that Customer
  • Review might need approval if the Customer's age is below a cutoffAge
  • The cutoffAge constant used in the Review business logic is saved in the DB
class Customer {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.review = null;
  }

  createReview() {
    this.review = new Review(this.age);
  }
}


class Review {
  constructor(age) {
    // Where `approvalAgeCutoff` value should really come from the DB
    this.approvalAgeCutoff = 10;
    this.needsApproval = false;

    if (age < this.approvalAgeCutoff) {
      this.needsApproval = true;
    }
  }
}


// Usage

const customer = new Customer('John Doe', 15); 
customer.createReview();

What's a recommended way of handling this without adding DB interaction code in the Review child object?

4

I would pass all the required information down as a method parameter in order to keep the Aggregate as clean as possible.

class Customer {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.review = null;
  }

  createReview(approvalAgeCutoff) {
    this.review = new Review(this.age, approvalAgeCutoff);
  }
}


class Review {
  constructor(age, approvalAgeCutoff) {
    this.needsApproval = false;

    if (age < approvalAgeCutoff) {
      this.needsApproval = true;
    }
  }
}


// Usage, in an Application service

const approvalAgeCutoff = someDb.loadApprovalAgeCutoff();
const customer = new Customer('John Doe', 15); 
customer.createReview(approvalAgeCutoff);

Then, when using it, the client code (most probable an Application service) would load that value from the DB and pass it to customer.createReview.

In this way you keep the Aggregate clean and testable without using a mock.

3

What's a recommended way of handling this without adding DB interaction code in the Review child object?

That sounds to me like a ReviewPolicy, or some such, with state that lives outside the Customer aggregate.

As such, the usual way of querying the policy within a command that updates the user would be to use a domain service. Most commonly, you would pass the domain service to the aggregate as an argument in the command; and the entities within the aggregate would invoke the query when they need it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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