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.

asked Nov 30, 2020 at 13:19
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. 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. 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.

answered Nov 30, 2020 at 14:17
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.

answered Nov 30, 2020 at 14:22
4
  • How would leaking business logic into a repository lead to a lot of code in the aggregate root? 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. 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. 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. Commented May 26, 2024 at 17:02

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.