RE: Validating aggregate collections: when and whose job is it?

<[email protected]>
Newsgroups gmane.comp.programming.domain-driven-design
Message-ID <[email protected]>
Hi Michael,
 

 Thanks for taking the time to reply.
 

 >Your rules only need to be checked when the data changes or is created
 

 Yes, but when is this exactly?  Should we consider that this is when the object is created in memory or when it's actually persisted to the data store?  In the example I tried to explain, the rules to be verified here are unicity constraints that span all instances of two aggregates.  So if these are to be verified when the object is created in memory, then the factory method should have access to repositories; if they're to be verified when persisting the object, then the repository would gain some validation responsibilities and might be required to leverage other repositories.  The right timing isn't clear to me, and neither option feels right.
 

 >In this specific domain you seem to have over complicated things with the rules. It looks like you might have 2 states of a company with some simpler rules. Some rules always apply, some only apply when 'registered'. 
 

 I probably didn't explain the problem very well, so I'll just distill it down to its most basic form.  A Company can declare another Company to be a Customer.  The same Company cannot twice declare the same other Company to be a Customer of his.
 

 There is little behaviour here, although the vocabulary still leaves to be desired.  Based on this, I see two entities, Company and Customer, each being its own aggregate.  Corresponding repositories are to be implemented as well.  I'm not considering Event Sourcing or any other alternative modeling technique, just a plain domain model.  You lost me when you said you saw read and write models, I see just one :)
 

 This would translate to something like this:
 

 class Company {
   Customer declareAsCustomer(Company other) {
     // validation here?
     return new Customer(other.getId());
   }
 }
 

 class CustomerRepository {
   void add(Customer customer) {
     // validation here?
     entityManager.persist(customer);
   }
 }
 

 The "declare as Customer" method is clearly a Command method, as you mention.  Validating that the given Company hasn't already been declared as a Customer could be performed by the Company.declareAsCustomer, but it would require sending a CustomerRepository parameter to the method.  And what if validation required the usage of 2 repositories?  The Company API would feel even less clear.  An alternative would be to give the validation responsibility to the repository, but it seems to be violating the single-responsibility principle for the repository to implement both persistence and validation concerns.
 

 I'm writing this and thinking that maybe a Validator could be responsible for these validations, leveraging the necessary repositories, and be handed to the Company.declareAsCustomer method as a parameter?  Sending such an object would create a clearer and more meaningful method signature, while hiding the usage of repositories from the aggregate itself?
 
---In [email protected], <michaelrempel@...> wrote:

 A few observations.
Your rules only need to be checked when the data changes or is created. So having some kind of validation check timestamp or version is a good idea. The first rule you dont mention is that rules change. You should account for this. 

 In this specific domain you seem to have over complicated things with the rules. It looks like you might have 2 states of a company with some simpler rules. Some rules always apply, some only apply when 'registered'. 
 

 If you take an action or command approach to the domain rules, rather than a state approach I think you will see this more clearly. You express the rules as actions, but think about objects themselves as states. I have two models in mind myself. Read models, and write models. An object written to the database should then be read-refreshed anyway. This keeps your data sharding strategy as simple as possible. When read you can also check it's validation state and ask the user for updates as needed to keep it current, or to require new data for extended functionality. Classic DDD doesnt express this pattern, but the CQRS with Event store extension does sort of. Event store is overkill for projects that maintain relationships. It is more for high volume update situations. But thinking in terms of commands and events is very useful.

 

 On Sat, Feb 1, 2014 at 5:23 PM, <gbilodeau@... mailto:gbilodeau@...> wrote:
   Hi all,
 

 I would like to have your input on a relatively simple problem that has me looking everywere for guidelines.  Here it goes.
 

 Let's consider a relatively simple subset of the application on which I'm working.  Aside from other considerations, this application allows users to:
 - manage the inner structure of their company: head office, departments, employees, etc.
 - manage the relationships their company has with external customers
 

 If we focus on the relationship management part, we learn that a customer can be:
 - registered: referring to a company already registered in the application, or
 - virtual: not referring to any registered company (and basically a placeholder until the company actually registers)
 

 There are two main rules that must be respected:
 1. for a given company, no virtual company can be created with a given website if that website is already owned by an existing company (basically, don't create virtual customers when registered customers exist), and
 2. for a given company, no two customers, virtual or registered, must own the same website
 

 To clarify, considering two registered companies, company1 (website company1.com http://company1.com) and company2 (company2.com http://company2.com):
 1. company1 cannot add a virtual customer with website company2.com http://company2.com, instead it must add company2 as a registered customer
 2. company1 cannot add twice the virtual customer company3 with website company3.com http://company3.com
 

 Although we could get into a discussion of which contexts exist, for the moment let's consider a single "company management" context with two aggregates: 
 - Company { companyId, name, website }
 - Customer { customerId, ownerCompanyId, name, website, registeredCompanyId }
 

 There are two basic questions: when are the two above business rules validated and by whom? I see two options:
 

 1. The rules can be verified when instantiating the Customer aggregate or when saving it to the data store. Validating at instantiation time benefits us since we know that there can never be an invalid Customer in memory. Instantiation would be performed by the Company aggregate. That would require Company or its Company.createCustomer methods to have access reference to both a CompanyRepository and a CustomerRepository to lookup any Company or Customer by website. Introducing a dependency between Company and these repositories doesn't produce a clean and intuitive API, plus doesn't feel like a very healthy dependency.
 2. The rules can be verified when saving to the data store. This would be performed by the CustomerRepository implementation. This approach removes the Company - repository dependencies, and having an invalid Customer in memory isn't really an issue.  But is validation logic really part of a Repository's responsibilities? Also, this would require the CustomerRepository.add and CustomerRepository.update methods to have a reference to a CompanyRepository to lookup any Company website. Introducing a dependency between two repositories doesn't feel right: my understanding is that a repository should only manage instances of a given aggregate regardless of its environment, but maybe this is too short-sighted.
 

 What do you think? How do you usually model these kinds of business rules?
 

 Thanks a bunch!
 GB
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.