2

I am following the DDD principles and have these Aggregate root classes. The problem is that after some time my class contains 1400 lines of code which makes development experience a little bit worse.

Is there any recommended way by DDD how can I split this AggregateRoot class into few classes?

I know, it can be said that I should reduce my bounded context but that's not the case. All the methods and fields in class belongs where it should belong just as every VO is created by the root method it contains quite many different public and private methods.

10
  • 1
    How many public methods does this AR have? This many lines could be an indication that you're not following the tell don't ask principle, but without code to review that's just a hunch.
    – Rik D
    Commented Nov 30, 2020 at 13:41
  • 1
    If you have code that reads data from entities or value objects and does logic based on that data in the aggregate root, that's an example of a tell don't ask violation. You solve that by moving the logic to the entity that contains the data. If there is no logical entity for the specific behavior, you could create a domain service for the operation.
    – Rik D
    Commented Nov 30, 2020 at 13:45
  • 1
    "All the methods and fields in class belongs where it should belong" - that's kind of the wrong way to think about it. Presumably, that root method is calling these other public and private methods. What you did there is you decomposed the code into smaller bits that all do their own part; each method solves a smaller part of the problem. Well, imagine inlining all that code, and ending up with one or two giant methods. You could say "all that code belongs where it should belong", but that's not the point; the complexity of it makes the development experience quite a bit worse. Commented Nov 30, 2020 at 14:34
  • 1
    An aggregate root is, generally speaking, not an object standing on its own. It's a root (an access point) of an aggregate. The aggregate consists of several interconnected classes that somehow work together. These classes are not accessed by client code; clients always go through the root object - the point of the aggr. root is to encapsulate access to these other classes. Don't think of these as of utility or service classes; these would be business objects internal to the aggregate. Commented Dec 1, 2020 at 16:34
  • 1
    What I'm saying is, you originally conceptualized this as one class doing one thing, but since the class now is becoming too big - you can change that initial conception. Threat it now as a class that's responsible for providing this logic on a higher level, but delegates different tasks needed to accomplish that work to several simpler, task-specific objects behind the aggregate root. Commented Dec 1, 2020 at 16:44

2 Answers 2

3

Is there any recommended way by DDD how can I split this AggregateRoot class into few classes?

There are at least two different approaches that may help.

One possibility is to review your model, and think about whether your aggregate is really multiple domain entities acting in a coordinated fashion. Your "root" entity code may be reduced by delegating responsibilities to other subordinate entities.

Another possibility is to delegate your computations. For example, if we have complicated computations using values, it can make sense to move that computation of the entity and into the value itself (or possibly into a "domain service"), or just into a function that lives "somewhere else".

In the Cargo example developed by Citerus, Cargois an entity that plays the role of an "aggregate root". A lot of the important domain information that supports Cargo's responsibilities is actually managed by a Delivery value object, and that's where the code that computes those changes lives.

In all, the Cargo aggregate is implemented as a single entity, and eight different values of varying size. Delivery is by far the largest; measured in lines of code, it is larger than the Cargo entity.

which makes development experience a little bit worse.

Yes.

Keep in mind that the real goal here is to create a design that is easy to adapt; nobody is awarding prizes for "Certified 100% DDD compliant". If the patterns described by Eric Evans aren't making things better, then don't use those patterns.

2
  • What "cargo example?" Commented Nov 30, 2020 at 14:18
  • I had forgotten to include links, thanks for the hint. Commented Nov 30, 2020 at 14:30
0

The first thing to do is verify that business logic hasn't leaked into your repository. Any code that isn't talking to a data source or mapping data to a return object doesn't belong in a root. Another red flag is boolean values getting set in the root that aren't directly from the underlying data. Setting a name property to the combination of first and last names is the most complex thing that should exist in your root from a logic perspective.

The next thing would be to analyze the public interface, the thing to look for is that all return types are objects related to your entity. Primitive return types are a code smell here, that usually means your domain is anemic and these methods are thinly veiled business logic. Sometimes you may need a simple get username or account balance type method that just returns a string or number, but those are exceptional cases. More than two primitive returns in an aggregate is a good sign something is wrong, and your objects don't know enough to do anything. Additional returning Foos and Bars from your aggregate is another sign that an aggregate is doing too much and you really do have room to further reduce your context.

Finally if you still have a massive class that you desperately want to shrink the next step would be following a CQRS pattern. This means physically separating your root into read and write versions. This requires a lot of discipline for developers to maintain that separation, because if it gets broken you end up with a far bigger mess than just a big class. This isn't something that should be done first to make an aggregate smaller, because queries with side effects or updates doing business logic will instantly make spaghetti code.

4
  • How would leaking business logic into a repository lead to a lot of code in the aggregate root?
    – Rik D
    Commented Nov 30, 2020 at 14:52
  • "Any code that isn't talking to a data source or mapping data to a return object doesn't belong in a root" This double negative makes it hard to read or wrong imo.
    – Rik D
    Commented Nov 30, 2020 at 14:56
  • "Another red flag is boolean values getting set in the root that aren't directly from the underlying data." I'm not trying to be picky, but could you elaborate on this? I could go on with some more quotes from your answer, there might be some valuable information in your answer, but making generalized statements without explaining the principles behind them, leads to dogmatism imo.
    – Rik D
    Commented Nov 30, 2020 at 15:01
  • The domain layer should never perform read operations I.e Query part of CQRS. Domain layer is best for write operations as this is where business logic comes into play.
    – GETah
    Commented May 26 at 17:02

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.