2

Suppose I want this on my User entity:

user.createNewSecurityToken();

That means:

public void createNewSecurityToken() {
  var buffer = new byte[32];
  new RNGCryptoServiceProvider().GetBytes(buffer);  // <--- here's the issue
  this.Token = Convert.ToBase64String(buffer);
}

This is not a DI dependency, just a class from the infrastructure. But does that "break" DDD?

The alternative is this:

user.setSecurityToken(token);   // pass it in (probably from a domain service)

But that leads to anemic entities.

Which is the preferred DDD approach, and what considerations should I take into account when faced with this sort of design decision?

UPDATE

Based on comments below, this leads to a more general question: what can I / can't I use in an entity? Domain services, application services, infrastructure services, framework classes, etc. - I know I can't inject DI-based services, or use infrastructure services, but what about the rest? What should I not use inside an entity?

  • RNGCryptoServiceProvider just looks like an implementation detail. I don't think it is of any interest for the DDD view of the design. – Lutz Horn May 23 at 8:29
  • @LutzHorn So I understand you correctly, you mean it's "okay" to use it in a DDD entity? – lonix May 23 at 8:47
  • @LutzHorn Also that raises the question what is the dividing line between items that are okay to use and those that aren't? – lonix May 23 at 8:49
  • 1
    Yes. The domain entity would be User. How the internal behaviour of this entity is implemented should not matter. – Lutz Horn May 23 at 8:49
  • @LutzHorn That makes sense now, thanks. But... why not use domain/application/infrastructure services inside an entity - I could use the same argument that it's an internal behavior? – lonix May 23 at 8:58
4

Think of it this way, which one represents something meaningful in your domain? Something that somebody familiar with the domain (but not necessarily the technology) would understand. I.e. which one is more likely to be part of the Ubiquitous Language?

Option #1: setSecurityToken()? Seems technical, it's a setter.

Option #2: createNewSecurityToken()? Seems better, similar words may be in your requirements somewhere? Still it is a bit technical, but might be ok for your domain.

In general what you "can or can not do in an Entity" depends on who you ask. Some people prefer purely anemic entities containing "pure" data with services containing all the logic. Others, me included, prefer a model based on behavior strongly mirroring the Ubiquitous Language, with no anemic objects (i.e. data) nor services (i.e. procedures).

To be honest, most of the examples on the 'net and books (Vaughn Vernon et al.) prefer the anemic object/service (i.e. data/procedure) approach, with some minimal behavior sometimes sprinkled in that can not require any dependencies. This is probably what would be most familiar to other people.

If you want to avoid anemic entities, i.e. embrace object-orientation, you will have to think for yourself, as there is very little about it online. Here is a presentation of mine on the topic.

  • So you are saying that it's okay to use the infrastructure's crypto library inside the entity, in order to have option 2? Okay, that makes my life simpler! :-) But then, what do you believe I can/can't use inside an entity - where do I draw the line? – lonix May 23 at 9:37
  • Yes, entities can use whatever they need for their logic. Basic design practices apply however. If it gets more than a couple of lines, I would extract that code to a new object (perhaps a value object) SecurityToken, etc. As with other objects it should be kept rather small and focused, but it can use any collaborator object it needs. – Robert Bräutigam May 23 at 9:44
  • "...but it can use any collaborator object it needs" and I assume that of course we mean something "light" (e.g. new Foo().doSomething()), not a service dependency that must go in via DI. – lonix May 23 at 10:38
  • I do mean anything. However, if you model the business consistently and avoid anemic things, I doubt there will be any "Services" left to write. It will be just another object coming through the constructor like everything else. – Robert Bräutigam May 23 at 11:45
3

With a DDD approach you are trying to match your Entities with the 'Domain Language'.

I find it hard to believe that the business says "A User creates a new security token". If they do then sure, the User object should have that method and the required logic for doing the job.

But I think it would be more usual for the Business to say "The User logs in" and for the devs to say "The Auth service creates a token for users".

So now you have User.LogIn() and AuthService.GenerateToken() If they are in the same Domain then you can have one reference the other, but that seems unlikely. in which case the AuthService is a Domain Service.

Which you could either inject into user or User's Aggregate Root to enable the LogIn() method.

Or, you could re think the Domain language and say 'does a user log in? or does the AuthService authenticate the User? allowing you to remove the LogIn() method from user and follow what is now a pretty standard approach of moving all the Auth concerns out of the Domain.

  • Thanks, I didn't consider that! – lonix May 23 at 10:23
  • I disagree with this approach for two reasons. First, AuthService is not a domain thing (usually). If I write my banking app, none of my business counterparts would know what "AuthService" is and why it's there. It's a technical thing. Second, User.createNewSecurityToken() does not imply that the User in real-life should create the security token. Objects in general do not mirror real-life. They are a model of the business concepts and behavior, a slight but crucial difference. – Robert Bräutigam May 23 at 10:23
  • But I do agree, that the behavior we are trying to model here is probably login() or something similar, and not createNewSecurityToken(). – Robert Bräutigam May 23 at 10:25

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.