Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD aggregate root is it correct to have an static method that create the object

Is it correct to have a Create method like this. Or should I create the User inside a service instead. Does this destroy the DDD concept?

What are best practices for situations like this?

Note: Im using DI too.

  public class User : HistoryBase, IAggregateRoot
    {
        private IEnumerable<Role> _roles = new List<Role>();
        public string Name { get; protected set; }
        public string Lastname { get; protected set; }
        public string Email { get; protected set; }
        public string Password { get; protected set; }
        public string EmployeeNumber { get; protected set; }
        public bool Active { get; protected set; }
        public int SiteID { get; protected set; }
        public IEnumerable<Role> Roles { get { return _roles; } }

        public static User Create(string name, string lastname, string email, string password, string employeeNumber, Site site)
        {
            var user = new User()
            {
                Name = name,
                Lastname = lastname,
                Email = email,
                Password = password,
                EmployeeNumber = employeeNumber,
                SiteID = site.ID
            };

            return user;
        }
    }
like image 445
Tan Avatar asked Mar 07 '23 11:03

Tan


2 Answers

DDD is mostly about the naming of your classes and their properties so that they accurately express the part of the business domain that your software is supposed to represent.

It really doesn't dictate any of the technical implementation details such as how things are instantiated. The point is that the properties and methods on a class have meaning in the domain.

A static .Create() method doesn't have meaning from the perspective of the domain, but neither does a new ..() call to the constructor.

You can make the decision that the method of construction for your domain entities/aggregates shall be Class.Create() instead of new Class().

It's not "wrong"

If, for example, the frameworks/libraries you use require a public parameterless default constructor (common with things like EntityFramework and some serialization libraries) and you want your "standard" method of instantiation to be unambiguous (can only be called with all needed parameters), this is a perfectly valid way of accomplishing that.

Just be consistent

If you do this, you should do it for all your entities/aggregates. It becomes a convention and it will be documented as a technical implementation detail (just like the constructor is by default).

Use CommandHandlers, not services (or factories)

Services are not really for instantiating classes (that's what factories are for).

However, your code seems to hint that you're also applying EventSourcing or some variation thereof. Assuming you're then also doing CQRS, you have Create commands ending up in your CommandHandlers Create methods. That's where you would place things like validation logic if you need it.

So those CommandHandlers kind of are your factories already, you don't need an additional layer for that.

Lastly:

Using static instantiation methods like that is usually for implementing a Fluent API of some sort, for example with the Builder pattern. It offers syntactic sugar for chaining calls.

So it may give other developers / consumers the false impression that the call can be chained (which in your example it cannot).

You'll probably still want to define the default public constructor so you can have a documentation comment to NOT use that one, and have one on .Create() that that should be used as the constructor.

All these things considered, is it still better than just using the constructor? Then by all means do it. It doesn't affect the "DDD-ness" as far as I know.

like image 77
Fred Kleuver Avatar answered Apr 07 '23 08:04

Fred Kleuver


Is it correct to have a Create method like this.

In general, that's fine.

It has been noted by Udi Dahan that "create" and "new" aren't usually part of the domain language, so he offered the advice Don't Create Aggregate Roots

Customers don’t just appear out of thin air.

Which is to say, your domain behaviors should accurately describe how new information is being introduced to the system.

An extra layer of indirection between the constructor and the consuming code can reduce unnecessary coupling - named constructors, factory methods, and builders that return interfaces give you the flexibility to change or decorate the object that you return. But that's a general modularity / separation of concerns principle, and not at all specific to domain driven design.

like image 42
VoiceOfUnreason Avatar answered Apr 07 '23 10:04

VoiceOfUnreason