1

We've got an aggregate root that has a position value so it can be ordered among the other aggregate roots; relative position is something that can change over time. We'd like to have a method that's "Move X before Y". Where should that method live?

I see a few places that could have that method:

  1. Aggregate Root: X.MoveAfter(Y). I'm not a fan of this because it'd require one aggregate root to have access to the repository that owns the aggregate roots to potentially change the order of other things

  2. Repository: Repository.MoveAfter(X, Y). The repository at least has access to all the data it'd need.

  3. Domain Service: Service.MoveAfter(X, Y). I lean towards this pattern, as it seems like the right place to make changes to multiple aggregate roots at the same time.

Which of these is correct? Is there another option I'm not thinking of?

1
  • I would personally go for the option 3. – Sylvain Lecoy Jan 30 '20 at 14:49
2

I would like to put another option on the table:

It sounds like there is another concept hidden in your domain that could be named and represented as an aggregate itself.

Eg. if you are dealing with Containers in a shipping domain, the arrangement of containers where the position is relevant could be a ContainerArrangment or ContainerCluster etc., which could be modeled as an aggregate, holding references to other aggregates and managing positional changes (which are likely to have their own logic and invariants).

This is now where the business logic should reside.

Other than that, but in the same direction: you seem sure that your entites are aggregate roots. again - if positioning is relevant and calls for invariants violation checks - maybe your AR is not actually the root. judgement is up to you!

1

Where should you put behavior?

Your first choice should usually be in a value object, an entity or aggregate root (AR).

It’s the client that calls behavior on the AR. A client can call the repository twice with different id’s, to get two instances (x and y) of the AR. The client can then call the behavior on X and pass Y -> x.MoveAfter(y).

The implementation of the MoveAfter method would be something like:

class Foo
{

  ...

  public void MoveAfter(Foo other)
  {
    var oldOrder = this.order;

    this.SetOrder(other.order);
    other.SetOrder(oldOrder);
  }

  internal void SetOrder(Order newOrder)
  {
    this.order = newOrder;
  }

}

After invoking the domain functionality on the AR, the client instructs the repository to persist the changes on both objects.

A domain service can be created if you have behavior that doesn't belong to any value object or entity. These should be rare, as most behavior belongs to something.

Putting behavior in the repository is never a good idea. It moves business logic out of the domain where it belongs.

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.