C# – DDD – Factory or Service

cdesign-patternsdomain-driven-designfactoryservice

I'm new to DDD, and I'm confused at the very start of my DDD project. To setup a context, I'm building the user management part of my app, so I'm building my Account entity, which contains an Id, a username and a password.

public class Account: IEntity<string>
{
    public string Id { get; }
    public string Username { get; }
    public string Password { get; }

    public Account(string id, string username, string password)
    {
        Id = id;
        Username = username;
        Password = password;
    }
}

The IEntity interface just forces a type as id, like this:

public interface IEntity<out T>
{
    T Id { get; }
}

Now here comes my problem, when creating the account, stuff like checking the username to be unique which requires to hit the repository, and hashing the password, which uses a domain service IHashingPassword, comes up to my mind so creating the entity just by putting the logic in it doesn't seem right.

From what I've read, creating object with complexity goes into a factory, but the creation of this object isn't quite complex, it's just that some logic is there that isn't checkable by the entity itself ( I'm not injecting my repo into my entity ).

So I tend to think that checking for the username ( repo ) and hashing the password ( hashing service ) should be done in my domain service instead of in the factory, or is it OK to do that in my factory, like I do now:

public class AccountFactory: IAccountFactory
{
    private readonly IHashingService _hashingService;

    public AccountFactory(IHashingService hashingService)
    {
        _hashingService = hashingService;
    }

    public Entities.Account Create(string username, string password)
    {
        var id = IdentityBuilder.Build();
        var hashedPassword = _hashingService.HashPassword(password);

        return new Entities.Account(id, username, hashedPassword);
    }
}

I'm not checking the username yet, it's just to show my current situation. Should this ( the checking of the username & hashing the password ) be done in a domain service and should I scratch the factory, or should I do this in the factory?

Best Answer

I suggest you actually attempt to model the actions to which you are alluding above in your application layer. Namely RegisterAccount and Login, because I think it can help bring clarity to your situation. That is, by working backwards from the public surface you would like your domain to expose to your application layer, it can help inform us where responsibilities should (and should not) be given.

For example, you may have a RegisterAccount command handler that boils down to the following:

accountRepository.Add( new Account(cmd.UserName, cmd.Password) )

accountRepository.Save() // throws DuplicateUserName

and a Login command handler that boils down to:

account = accountRepository.FindByUserName( cmd.UserName ) // throws NotFound

account.Login( cmd.Password ) // throws InvalidPassword

The above represents just about the most declarative way to model each process. This is a good thing. It's important that your application can present a clear and concise "view" of each business process in terms of behavior. Of course, this is precisely what the application layer is for: coordinating your domain with as little logic as possible (i.e. rules) in a way to provide this view. So with the above in mind, let us move on to your specific questions.

Set validation is tricky. If there is a rule mandating that a UserName must be unique within a set of Account, it should be enforced by the set of Account. Is there a piece of your application which represents a collection of Account? Often, set validation belongs on your Repository. This is especially convenient because a Repository "knows" the single critical piece of infrastructure necessary to enforce this kind of invariant (the data store). In your case (and many others like it), I would recommend enforcing this as a constraint in your data store and let your AccountRepository throw an exception on Save if an Account with a duplicate UserName is added. Simple. Declarative.

I cannot recommend creating a separate service to check that adding an Account will succeed before it is added, because this represents a separation of data and behavior. The idea of validating that some process will succeed before it is attempted is a fundamentally procedural approach, not OOP, and certainly not DDD. This practice can also lead to all sorts of duplication and gotchas down the road when applied liberally to a system. Let the rules exits with the data, not around the data.

Moving on, we can see that your Account entity certainly requires a dependency on a hashing cohesive mechanism, as it must be able to internally hash and check hashes to allow for both the account registration and login processes. The question is whether or not this should exist as a domain service or something else. Truthfully, it doesn't really matter. The inner workings of an Entity are not important (hence the abstraction). What is important is that we focus on the behavior we would like to achieve, and let the data that supports this behavior be an implementation detail. This is the perspective that DDD seeks to provide.

That said, I understand hashing to be a cross-cutting concern. Unless your algorithm is specific to your domain, it would seem to me that hashing belongs as part of infrastructure. Don't let your domain become concerned with things that aren't truly business rules (hashing is more of a technical concern in most industries).

With regard to factories: A factory should be used when the creation of an Entity is so verbose that your model starts to lose focus. Importantly, NOT when you want to encapsulate rules. Whether or not a factory should be employed is up to you to decide.

EDIT: I'd like to touch on another idea which I kind of danced around in my answer above, but didn't explicitly state: the idea of delaying implementation until absolutely necessary. This can be an incredibly useful tactic when designing and implementing a system. In the book Clean Architecture by Robert Martin, he brings this idea up specifically in relation high-level decisions like data storage, but it is useful even in microcosms (such as modeling a workflow).

At it's core, it means to push rules (implementation) down the road as far as possible. In terms of the above system, it means enforcing a unique constraint at the lowest level it can be enforced or engaging in hashing where absolutely necessary. Simply being cognizant of this practice will lead you to naturally implement rules as close as possible to the behavior of which they govern. Of course, this is the core principal of OOP.