1

How exactly should AddSomething methods in aggregate roots look like? Should I construct the objects in a call to the method, or should they be constructed inside the method?:

For example:

public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1) (taken from here).

Versus:

public void AddOrderItem(OrderItem orderItem)

I see the second pattern more commonly and I wonder when should I prefer one over another.

  • 2
    The second signature using parameter object instead of N parameters are better in terms of code maintenance. – kayess Aug 14 '18 at 11:54
3

I wonder when should I prefer one over another.

There are two different tensions at work.

One of those is primitive obsession

Primitive Obsession is using primitive data types to represent domain ideas. For example, we use a String to represent a message, an Integer to represent an amount of money, or a Struct/Dictionary/Hash to represent a specific object.

One reason that's a bad idea: the logic for evaluating constraints on the data scatters across the application. If we need a valid unitPrice, then we prefer to check its validity in one place, and rely on the type system to act as a proof that the validation checks have already been applied.

A fairly common family of programmer errors can be discovered early by using this approach.

The other is boundaries

At the Boundaries, Applications are Not Object-Oriented

The data has to come from somewhere, and for the most part that means bytes, which may be interpreted as domain agnostic primitives.

And this tends to be true of working both ways across the boundary -- our input arrives as bytes, our output departs as bytes.

And so you need to think: are the aggregate roots part of the boundary, or are they within the boundary.

In most cases where domain modeling makes sense, you are trying to express your domain semantics. So you don't want that logic to get obscured by a bunch of incidental validation. We want the domain concerns isolated from the digital plumbing.

In short

when should I prefer one over another?

Once you hit the point where working with primitives is awkward, and you feel a need to create an OrderItem type, you should look toward deprecating the primitive interface

[Obsolete]
public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
    AddOrderItem(
        OrderItem.from(productId, productName, unitPrice, ...);
    );
}

public void AddOrderItem(OrderItem orderItem) {
    // pure business logic here
}

See also: From Primitive Obsession to Domain Modelling.

2

Both are OK, depends on the context.

You can use the first one (the one with primitive parameters), if there is no real "behavior" (i.e. business functionality) to be tied to those parameters. To be honest, most often, there is at least some functionality you can bind to this many parameters, but not always. If you can't, then it's ok to enumerate them as primitive types.

The second one (when you create an object for them) is when there is a functionality that you'd like to use associated with these parameters. Like calculateTotalPrice() or whatever.

The worst thing you can do is to just "group" the primitive values into a data object (sometimes called "value objects"), that have no function, just getters for the data.

  • Most programmers would argue that a value object is cleaner and that a method with 4+ parameters is doing too much (not that a value object instead of multiple parameters would change that fact, but it's easier to refactor). Case in point, the one comment under the question as well as the accepted answer advise to do so. I'm not saying you shouldn't be pragmatic and go against established know-how if you have a good reason, but you give us no reason at all. Why do you think a value object is "the worst thing"? – R. Schmitz Aug 15 '18 at 9:19
  • I agree that it's probably too many parameters and the method is doing too much and should be refactored. A pure data object hides the fact that there is a problem. It pretends that real objects are involved. That is why it's the worst thing you can do, because you basically acknowledge that there is a problem, and then you sweep it under the rug. As you said, it does not reduce the method's complexity nor its coupling to the data, it's a purely syntactical change for the worse. – Robert Bräutigam Aug 15 '18 at 14:23
  • Hm, I find a value object easier to refactor though, to get rid of the actual problem. And often they don't stay anemic, because they turn out to be the perfect place for some methods. I don't know, maybe I'm reading it wrong, but a rule "If a class will be passed as a parameter, it has to have methods" sounds too arbitrary for my taste. – R. Schmitz Aug 15 '18 at 16:11

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.