I'm working on a project at the moment and I'm using this project to try and apply DDD. However, there are parts of my implementation that I just can't get my head around or don't feel right. I'll try to describe how I currently have my application setup;

In my domain layer, I have my aggregates, value objects and a service class for each bounded context. As an example, I have a bounded context 'Account' with the following;

Account - account aggregate root

Profile - value object

EmailAddress - value object

AccountValidator - validates an accounts state

ProfileValidator - validates a profiles state

AccountRepository - interface describing the methods available on the repository

AccountService - at the minute this has a single method createNewAccount(), it accepts a number of primitive values and creates the account and returns it. Before creating the account it first checks to make sure the selected username doesn't already exist. If the username exists, it throws an Exception. (Should this rule be in the domain service?)

In my application layer I have;

AccountRepositoryImp - an implementation of the accountRepository in the domain class, this is a standard Spring Data interface that implements accountRepository.

AccountFacade - basically, another account service that provides methods such as findById(), findByUsername(), createNewAccount(), sendVerificationEmail(), verifyAccount.

AccountController - standard Spring controller that handles all account related actions

I have a number of questions/issues with my approach;

  1. I have two service classes for each bounded content, one for the domain and one for the application, and while I understand the domain service class should be used only to apply behaviour or business rules that can't be done on the aggregate itself (i.e. need db access), I'm struggling to determine what's an application rule and what's a business rule. For example, an account cannot be created if the username is already in use, I've put this in my domain service, but I'm starting to wonder if it should belong in the createNewAccount() method in the application service. Another rule, "An account can only be deleted by an admin or the account hold themselves", is this application or business?
  2. As I add more and more methods to my application service, it's going to grow pretty big. This leads me to thinking about implementing CQRS and having a class/method for each command/query. This breaks things down and makes it easier to manage, but I can't help but feel this will bring a lot of duplication to it. For example, In the AccountRepository I can retrieve an account by it's username, and if the account doesn't exist it returns null. Rather than returning null, I wrote a getByUsername() method in the application service so that it throws an exception if the account doesn't exist, rather than a null. This means I can use this method from other methods and I don't need to duplicate throwing the exception. I feel like if I use CQRS, I'm still going to have to keep the application service so it can hold methods that are used by multiple commands/queries to reduce duplicate. I'll end up with yet another layer controller -> command/query -> applicationservice -> domainservice it feels like I'm making the application more complicated than it needs to be
  3. If I use CQRS, is the command/query or controller responsible for building ViewModels? I'm inclined to thing the command/query can build them, as they'll be use case based and can be re-used if I was to add REST endpoints to the system in the future. If I allowed the controller to make multiple command/queries and build the ViewModel, then if I implement REST at a later date, I'll have to duplicate that code.
  4. Rules - as mentioned above I'm struggling to assign rules to the application or domain. I'll list a few rules below and if I could have some pointer on where they might belong, it would be useful to train my brain;

    • An account cannot be created without an email, username, firstname and lastname (I've put this on the domain)
    • An account cannot be created if the username is in use (I've put it on the domain)
    • A user cannot comment on a project if they are not a project member.
    • A comment cannot be left on a closed project
    • Only the project owner can edit/manage/administer the project

I do apologise if this question is a little unstructured and my thoughts do appear a bit random. I've been so used to using an anemic model that I've got so many thoughts in my head that I can't structure (hence the question).

  • 1
    This is something I'm struggling with myself so I'm really looking forward to reading answers to this question. – mrsmn Nov 9 at 9:37
  • think twice, DDD is a tool to implement business requirements not simple 'tool' code. To manage Accounts the best way is to make facade and simple services focusing on proper OOP, then if you have REST controllers then they should only use facade. DDD works well for complex business. And it's completely waste of time if you are trying to solve a too simple cases with it. Same with CQRS, I don't now maybe it is a this year fashion, but packing CRUD to CQRS. om.g. Think twice if you really need it. – shutdown -h now Nov 9 at 10:15
  • 1
    You have too much going on here. Every method you list on your AccountFacade seems to naturally fall under the purview of AccountRepository or AccountService. The latter should just be moved into your Application Layer. Extra layers of indirection only help muddy the waters. Similarly, AccountRepository should just throw an exception is an Account is not found. Why return null? So you can check if the return value is null and throw an exception? Get rid of all the middlemen! – king-side-slide Nov 9 at 21:29
  • Regarding your question about where rules should be enforced. Think of it like this: If the rule is, "Does ABC have permission to do XYZ?", then it's a question of authorization/authentication, and not really a business rule. Auth is a cross-cutting concern that involves all sorts of otherwise unrelated pieces of your application. Attempting to model it in your domain creates incredible complexity and unnecessary coupling. Put another way, if your rule needs to know more about who/what generated the message than just it's identity, the rule should be enforced in your Application Layer. – king-side-slide Nov 9 at 21:35
  • Lastly, I'm unsure about the inclusion of Validator classes as they are almost always a sign of an anemic model. In DDD, your entities should validation themselves. Separating data (Account) from behavior (AccountValidator) is fundamentally procedural in nature, not OOP, and certainly not DDD. – king-side-slide Nov 9 at 21:37

Off the top

1) If you are having trouble modeling your domain, CQRS is unlikely to help.

2) Your "unique email constraint" is a common example of Set Validation. Trying to ensure uniqueness of an element in a distributed set with multiple writers is a Hard Problem.

In some domains, you can use the email address as the unique identifier for the account itself (either the email address itself, or some hash/uuid value computed from the email address). That's easier than set validation, but has implications on use cases where an email address may change.

Email addresses themselves are something of a special case. Your domain probably isn't the authority for an email address; the address was assigned somewhere else, and you are just receiving a copy of it. So you may want to consider carefully what is really possible with email validation.

Domain services don't normally have domain logic -- computing changes of state belongs in the aggregates, not in the services. Most domain services are either queries to give the aggregate read access to a cached copy of data that lives somewhere else, or to give the aggregate access to some capability outside of the model (like sending an email). The domain service itself may be little more than an interface, with all of the details of the implementation defined in the application/infrastructure components that provide the capability.

Finally - most domains don't need a bespoke solution for account management. Are you sure you shouldn't be using something off the shelf to solve this part of the problem?

am I right in saying that you wouldn't inject the domain service into the aggregate? i.e., you'd call the domain service first?

No - I pass the domain service to the aggregate as an argument, and let the business logic in the aggregate method determine whether or not to invoke the domain service capabilities.

  • If a domain service is to give aggregates access to external capabilities, am I right in saying that you wouldn't inject the domain service into the aggregate? i.e., you'd call the domain service first? – SheppardDigital Nov 9 at 12:30

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.