Rule between Aggregate Roots

738 views
Skip to first unread message

Dim@sty

unread,
Feb 16, 2012, 6:59:12 AM2/16/12
to DDD/CQRS
Hi All

For example
I have two Aggregate roots

1AR

class Person : AggregateRoot
{
int Age {get;set;}
}

2 AR
class Department : AggregateRoot
{
List<Guid> persons;
RegisterPerson(Guid personId);
}

There is a business rule that says that a person may be registered in
the department, if its the age is than 25 years

My Question
How to implement this rule, if I have no reference to the person, but
only an ID
or implement a rule in the command handler

void Handle(RegisterPersonInDepartment cmd)
{
var person = domainRepository.Get<Person>(cmd.PersonId);
var department =
domainRepository.Get<Department>(cmd.DepartmentId)
if(person.Age > 25) department.RegisterPerson(person);
}

Is it correct ???

ShuriK

unread,
Feb 16, 2012, 7:03:08 AM2/16/12
to DDD/CQRS
Maybe:

void Handle(RegisterPersonInDepartment cmd)
{
var person = domainRepository.Get<Person>(cmd.PersonId);
var department =
domainRepository.Get<Department>(cmd.DepartmentId)
department.RegisterPerson(person);
}
void RegisterPerson(Person person)
{
// check is going here

Dim@sty

unread,
Feb 16, 2012, 7:19:53 AM2/16/12
to DDD/CQRS
Thanks !

If I use IPersonRegistrationStrategy, the strategy is also passed as
an argument?

void RegisterPerson(Person person, IPersonRegistrationStrategy
strategy)

ShuriK

unread,
Feb 16, 2012, 7:26:39 AM2/16/12
to DDD/CQRS
As for me, absolutely true. I wouldn't like to see any
Container.Resolve or Repository.Resolve inside the domain object.

Dim@sty

unread,
Feb 16, 2012, 7:29:15 AM2/16/12
to DDD/CQRS
ShuriK Thank you

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 10:23:01 AM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 5:59 AM, Dim@sty <Dim_...@mail.ru> wrote:
Hi All

For example
I have two Aggregate roots

1AR

class Person : AggregateRoot
{
       int Age {get;set;}
}

2 AR
class Department : AggregateRoot
{
        List<Guid> persons;
        RegisterPerson(Guid personId);
}

There is a business rule that says that a person may be registered in
the department, if its the age is than 25 years

My Question
How to implement this rule, if I have no reference to the person, but
only an ID

I would think that this should be implemented as a Saga. You should be able to look up the age from the read model and pass anything required to Department, i.e. the Person ID and age.
 
or implement a rule in the command handler

void Handle(RegisterPersonInDepartment cmd)
{
      var person = domainRepository.Get<Person>(cmd.PersonId);
      var department =
domainRepository.Get<Department>(cmd.DepartmentId)
      if(person.Age > 25) department.RegisterPerson(person);
}

This seems to violate CQRS (and encapsulation), because you're using the write-model as a read-model.

ShuriK

unread,
Feb 16, 2012, 10:36:41 AM2/16/12
to DDD/CQRS
> I would think that this should be implemented as a Saga. You should be able
> to look up the age from the read model and pass anything required to
> Department, i.e. the Person ID and age.

Why we should use a read model if we have a Person AR with the age in
it?
Why we need a saga if we have only one operation with the one AR?

On 16 фев, 18:23, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Nick Peeples

unread,
Feb 16, 2012, 11:01:27 AM2/16/12
to ddd...@googlegroups.com
Who or what is deciding how to send that command in the first place? Could this simple validation rule be someone else's responsibility? Do departments have variable age requirements?

I think Nils primary reasoning behind suggesting a saga (or even a simpler event handler) is that these objects are typically allowed to query read-stores, as they are making decisions/taking actions based on the facts already established, as opposed to decisions based on commands, or requests. Another way of saying that, is it is ok to give sagas (or event handlers) access to the read stores.

- Nick

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 11:07:58 AM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 9:36 AM, ShuriK <alex....@gmail.com> wrote:
> I would think that this should be implemented as a Saga. You should be able
> to look up the age from the read model and pass anything required to
> Department, i.e. the Person ID and age.

Why we should use a read model if we have a Person AR with the age in
it?

What do you need the Person AR for? To execute commands on, or to read data? If the latter, you're exposing read data on your write-model.
 
Why we need a saga if we have only one operation with the one AR?

Correct, I misinterpreted the original question.

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 11:10:03 AM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 10:01 AM, Nick Peeples <nick.p...@gmail.com> wrote:
Who or what is deciding how to send that command in the first place? Could this simple validation rule be someone else's responsibility? Do departments have variable age requirements?

I think Nils primary reasoning behind suggesting a saga (or even a simpler event handler) is that these objects are typically allowed to query read-stores, as they are making decisions/taking actions based on the facts already established, as opposed to decisions based on commands, or requests. Another way of saying that, is it is ok to give sagas (or event handlers) access to the read stores.

I misinterpreted the question as a Create Person AR then register with Department AR. Which is was obviously not, so no need for a saga.

 


- Nick

Petr Snobelt

unread,
Feb 16, 2012, 11:18:42 AM2/16/12
to DDD/CQRS
IMHO This is a domain logic. Department should be responsible to
testing if person can be added to it.
You issue command and if person's age is under 25 it fails and command
is rejected

similar question http://stackoverflow.com/questions/5398675/business-rule-validators-and-command-handler-in-cqrs

P.

ShuriK

unread,
Feb 16, 2012, 11:20:24 AM2/16/12
to DDD/CQRS
I understand that a Saga or CommandHandler can get access to the read
side, but I don't like to use it, if all necessary information exists
in the write side, e.g. in the Person and Department ARs. I think that
write side should never get data from the read side if it possible in
other way.

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 12:31:08 PM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 10:20 AM, ShuriK <alex....@gmail.com> wrote:
I understand that a Saga or CommandHandler can get access to the read
side, but I don't like to use it, if all necessary information exists
in the write side, e.g. in the Person and Department ARs. I think that
write side should never get data from the read side if it possible in
other way.

It means that you have to break encapsulation and expose all data outside the class, making an anemic domain model more likely.

But if that's an acceptable trade-off, then who am I to argue?


Nick Peeples

unread,
Feb 16, 2012, 1:06:58 PM2/16/12
to ddd...@googlegroups.com
I like this point. If you want to break the rule that aggregates are not dependent on each-other, hence the reason they are aggregates, go for it. Nils is correct though, encapsulation is destroyed and now you can't change the person without affecting the department. What if Age is moved somewhere else?

- Nick

ShuriK

unread,
Feb 16, 2012, 4:13:30 PM2/16/12
to DDD/CQRS
Ok... I was understand my mistake.. My solution is really anemic...
Can we have the age in the command and pass it with the person id
into the department method?
> else? Ok... I was understand my mistake.. My solution really anemic... Can we have the age in the command and pass it with the person id to the department?
>
> - Nick

@yreynhout

unread,
Feb 16, 2012, 5:50:55 PM2/16/12
to DDD/CQRS
Yuk. Read models equal last resort in my book. Most of the time they
are not even optimised for these type of queries anyways. There's not
even A read model.
Objects should collaborate to get things done, otherwise you'd end up
with an anemic, primitive type obsessed model all over again.

On 16 feb, 16:23, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

@yreynhout

unread,
Feb 16, 2012, 5:53:35 PM2/16/12
to DDD/CQRS
Sagas querying read stores? They coordinate, remember, react and
timeout, but they dont go out to fetch state to take a decision.
That's a sign of a missed message.

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 6:00:40 PM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 4:53 PM, @yreynhout <yves.r...@gmail.com> wrote:
Sagas querying read stores? They coordinate, remember, react and
timeout, but they dont go out to fetch state to take a decision.
That's a sign of a missed message.

It depends, I guess, on the definition of "read-store". Can internal saga memory be considered a read-store? Or do you feel a read-store is only for UI projections?

@yreynhout

unread,
Feb 16, 2012, 6:06:28 PM2/16/12
to DDD/CQRS
void RegisterPerson(Person person) {
If(!person.IsOlderThan(25.OfAge())) {
throw Up();
}
//...
}

On 16 feb, 18:31, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 6:06:57 PM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 4:50 PM, @yreynhout <yves.r...@gmail.com> wrote:
Yuk. Read models equal last resort in my book.

That's fine. I find internal state exposure to be yucky.
 
Most of the time they
are not even optimised for these type of queries anyways.

Most of the time? They are, by definition, optimized to their usage.
 
There's not
even A read model.
Objects should collaborate to get things done, otherwise you'd end up
with an anemic, primitive type obsessed model all over again.

My models are fully encapsulated, which forces all logic into the AR. Not sure how that's anemic. Any other information they need are provided to them by the command/handler, which may use a dedicated read-model for exactly such a purpose.
It was my impression that CQRS is explicitly about separating the read model and the write model. Sounds like you both read and write from the same model.

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 6:08:04 PM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 5:06 PM, @yreynhout <yves.r...@gmail.com> wrote:
void RegisterPerson(Person person) {
 If(!person.IsOlderThan(25.OfAge())) {
   throw Up();
 }
 //...
}

That definitely one perfectly valid way to do it.

@yreynhout

unread,
Feb 16, 2012, 6:09:52 PM2/16/12
to DDD/CQRS
Whirlpool to the rescue ... Challenge your model as things change.

@yreynhout

unread,
Feb 16, 2012, 6:37:07 PM2/16/12
to DDD/CQRS
- I meant reusing a UI read model for answering write model queries.
If you have a dedicated read model for these, than fine. Although one
should not expect full consistency unless you're doing fully
consistent writes to this read model (and even then it ends up being
an eventual consistent read unless your doing a consistent one). BTW,
the same eventual consistency is to be expected from the other
aggregates that participate in a behavior.
- as for state exposure, dont do it, use TDA (but since we both
dislike state exposure I presume you're already doing that)
- no, reading and writing are not done from the same model, but I've
done that in the past ;) and yes it's still seek-your-ass

On 17 feb, 00:06, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Nils Kilden-Pedersen

unread,
Feb 16, 2012, 7:48:48 PM2/16/12
to ddd...@googlegroups.com
On Thu, Feb 16, 2012 at 5:37 PM, @yreynhout <yves.r...@gmail.com> wrote:
- I meant reusing a UI read model for answering write model queries.
If you have a dedicated read model for these, than fine. Although one
should not expect full consistency

I'm using ES, so there is, by definition, never full consistency between ARs, even if I were to read of the write-model.
 
- as for state exposure, dont do it, use TDA (but since we both
dislike state exposure I presume you're already doing that)

Haven't heard about it before. Is it a formal approach? I certainly do analysis of data transitions :-)
 
- no, reading and writing are not done from the same model, but I've
done that in the past ;)

Sure, it's all about trade-offs.

@yreynhout

unread,
Feb 17, 2012, 3:16:22 AM2/17/12
to DDD/CQRS
TDA: http://pragprog.com/articles/tell-dont-ask

On 17 feb, 01:48, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

satish venkatakrishnan

unread,
Feb 17, 2012, 4:53:37 AM2/17/12
to ddd...@googlegroups.com
Age less than 25 , to me it sounds like a simple validation before coming to command, why not do that way rather than Complicating with the Aggregate,saga.. 

ShuriK

unread,
Feb 17, 2012, 5:12:04 AM2/17/12
to DDD/CQRS
It's a business rule so it should be in the domain, other way your
logic will live in the client, command or at pice of paper..

satish venkatakrishnan

unread,
Feb 17, 2012, 5:36:37 AM2/17/12
to ddd...@googlegroups.com
Then Command has to be sync which makes my client to wait until the process to be done.. 

In this case what if i inform the client since your age is more than 25 we dont register you.

@yreynhout

unread,
Feb 17, 2012, 5:37:27 AM2/17/12
to DDD/CQRS
It's not static validation (it depends on the person involved in the
behavior), as such it's not all that bad a place to put. But I agree,
from a usability perspective I'd block a user from sending the command
in the first place (it's not like the person is suddenly changing age
to 25 at that particular millisecond).

@yreynhout

unread,
Feb 17, 2012, 5:38:06 AM2/17/12
to DDD/CQRS
It's fine for such logic to be hosted multiple places.

ShuriK

unread,
Feb 17, 2012, 6:02:16 AM2/17/12
to DDD/CQRS
I agree that we can and should use validation on the UI before send
the command.
So we need:
1. UI validation rule(e.g. Attribute at the ViewModel to affect UI
directly).
2. Command validation rule (simple validation inside the command
because not all clients can use specific validation from the point
1.)
3. Domain validation rule because we do not trust the sender, but here
we can use validation from from the command.

So we have three places for the code duplication and a business rule
lives in the command and at the client, not in domain... I'm
confused...

Werner Clausen

unread,
Feb 17, 2012, 7:07:42 AM2/17/12
to ddd...@googlegroups.com
I second that. In general "if condition then fail" is not business logic, that is pure validation which should not even get to your business logic (e.g. command handlers).
 

Roy

unread,
Feb 17, 2012, 7:48:35 AM2/17/12
to ddd...@googlegroups.com
Command handlers coordinate actions of the domain, but the business logic of those actions is handle by the domain. That means using if statements. To minimize stress on the system, the client should perform simple validations that it is aware of. Are you implying that business logic should be handled by the command handler instead of the domain? That's how I interpreted it.

satish venkatakrishnan

unread,
Feb 17, 2012, 8:01:54 AM2/17/12
to ddd...@googlegroups.com
IMHO , Domain model will have Business rules where as Validation is a seperated component which is hosted in mulitple places.

Business Rules are if somthing then what is next .. where as validations are if something stop it ..  Business rules will always have a compensating options.

@yreynhout

unread,
Feb 17, 2012, 8:25:34 AM2/17/12
to DDD/CQRS
I'm afraid this is a slippery slope ... it's crucial to consult domain
experts and ask them if this "condition" will change over time.
Also consider that I can bypass your client and send a command for an
18yo person. If your reasoning is that I should
- send along the age, then you're inviting me to fill in whatever I
like.
- not verify at the server, than I'm able to bypass that "validation".
- query the read model (dedicated or otherwise), please point out to
me how that's easier to implement and how you're not leaking the
"domain" into an application service.
- query the read model (dedicated or otherwise) and pass that
information into the domain to do the check, my response would be:
why, I have that derived (from a birth date) information already.

Bottom line for me is that this is not static validation (if that is
what you are suggesting and I think you are).

@yreynhout

unread,
Feb 17, 2012, 8:25:58 AM2/17/12
to DDD/CQRS
Me too.

@yreynhout

unread,
Feb 17, 2012, 8:41:36 AM2/17/12
to DDD/CQRS
Who said anything about code duplication? I'll bet you, if I have a
HTML/Javascript client, the code for enhancing the usability will look
different from what I have on the server. Maybe I'll show a message
stating the action can not be performed for persons below the age of
25, maybe I'll disable the button based on the current age of the
person with a message next to it, etc. Command validation will not
validate this "rule" because it doesn't even have the required state
to begin with. As for dumb clients, maybe you should put something
between them and the command handling, that prevents them from
submitting commands that will fail. To me it's an invariant of a
behavior/aggregate, I don't care if it's simple at this point. It
tells me something about the domain "You can't do this if you're under
25".

perpigne

unread,
Feb 17, 2012, 10:14:02 AM2/17/12
to ddd...@googlegroups.com
All the discussions around validations vs. business rules may be an indication that there is a concept that is not explicit yet in your model. Maybe a Registration AR will give you a place to put these validation/business rules in your domain model and also will probably evolve to hold things such as registration status, date of renewal, etc... With that AR your model would look like:
 
class Person : AggregateRoot
{
   int Age;
   public bool IsOlderThen(int expectedAge);

}

2 AR
class Department : AggregateRoot
{
     int numberOfRegistrations;
     int maxNumberOfRegistrations;
     public bool CanAcceptRegistration();
     public void IncreaseRegistrations();
}
 
3 AR
class Registration : AggreateRoot
{
     Guid personID;
     Guid departmentId;
     RegistrationStatus status;
 
     public Registration(Person person, Department department)
     {
           if (person.IsOlderThen(25))
              throw new SomeDomainException(...);
   
           if (!department.CanAcceptRegistration())
              throw new SomeDomainException(...);
 
           personID = person.ID;
           departmentID = department.ID;
           status = RegistrationStatus.Active;
 
           department.IncreaseRegistrations();
 
     }
 
}
 
 
The command handler would look like
 
void Handle(RegisterPersonInDepartment cmd)
{
       var person = repository.Get<Person>(cmd.PersonId);
       var department = repository.Get<Department>(cmd.DepartmentId);
 
       var registration = new Registration(person, department);
       repository.Add(registration);
}
 
The benefits I see in this:
 
  1) Command Handler has no business logic and only coordinates the domain entities
  2) Business logic is still in Domain, testable
  3) Registration rules may evolve either with strategy or subclasses depending on future needs
 
That being said, I would surely create a set of validation rules at the UI level as well as defined by the usability requirements of the application.
 
What do you think ?
 
Ernst

@yreynhout

unread,
Feb 17, 2012, 10:17:40 AM2/17/12
to DDD/CQRS
What I've been saying all along ...

@yreynhout

unread,
Feb 17, 2012, 10:18:33 AM2/17/12
to DDD/CQRS
Although, I'd choose one of the other ARs to be the factory, TBH.

On 17 feb, 16:14, perpigne <perpi...@gmail.com> wrote:

satish venkatakrishnan

unread,
Feb 17, 2012, 10:23:25 AM2/17/12
to ddd...@googlegroups.com
ya well that make sense.

Just to summarize the decisions

1) We will have a Validation rules in UI depends on the Usability requirements
2) We may also have the same rule in the Aggregate but this condition comes only when the Rule is changing often.

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 11:09:47 AM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 2:16 AM, @yreynhout <yves.r...@gmail.com> wrote:
TDA: http://pragprog.com/articles/tell-dont-ask

Ahh, yes, of course I practice that.

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 11:18:31 AM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 9:23 AM, satish venkatakrishnan <sati...@gmail.com> wrote:
ya well that make sense.

Just to summarize the decisions

1) We will have a Validation rules in UI depends on the Usability requirements
2) We may also have the same rule in the Aggregate but this condition comes only when the Rule is changing often.

What's interesting about this, from my perspective, is that this proves that the read-model (not a dedicated Command read-model, but a UI read-model) must support what I've been claiming all along, which again should mean that you don't need to add such AR interdependent methods such as isOlderThan. In other words, the UI is probably already doing most of the same validation as the AR eventually ends up doing, so it's very likely that the read-model is already in place.

perpigne

unread,
Feb 17, 2012, 11:32:14 AM2/17/12
to ddd...@googlegroups.com
Well... it depends. If those validations are important part of your domain and not just only for this particular application, you want your design to reflect that in your domain model as well. Especially if you are using DDD.
 
You might also have several applications as sources of commands. All of these applications might not have the same usability requirements. Thus you need that validation in your domain.
 
 

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 11:37:17 AM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 10:32 AM, perpigne <perp...@gmail.com> wrote:
Well... it depends. If those validations are important part of your domain and not just only for this particular application, you want your design to reflect that in your domain model as well. Especially if you are using DDD.
 
You might also have several applications as sources of commands. All of these applications might not have the same usability requirements. Thus you need that validation in your domain.

I'm not at all saying that the domain shouldn't do validation. I'm stating that the read-model in many cases is already in place for the data needed in the domain model, because the validation, to some extent, is duplicated in the UI. Which was my original point, that an AR should not be using another AR for read purposes.

@yreynhout

unread,
Feb 17, 2012, 2:59:03 PM2/17/12
to DDD/CQRS
This assumes noone will bypass your client. I'm not saying you can't
trust the client, but you better build in security if you're going to.

On 17 feb, 17:18, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
> On Fri, Feb 17, 2012 at 9:23 AM, satish venkatakrishnan <satish...@gmail.com

@yreynhout

unread,
Feb 17, 2012, 3:09:39 PM2/17/12
to DDD/CQRS
Which comes back to my statement, why do it with a read model, when
you could express it using readable, encapsulated domain behavior. I'm
not saying the read model query won't be readable, but you'll be
putting "You can't do this behavior under the age of 25" in the
command handler (the application service). Even if you put it behind
the facade of a repo, my point remains. Also notice how Age is
constantly changing as Now progresses.
Long story short, what kind of "coupling" are you scared of within the
same Bounded Context between aggregates?

On 17 feb, 17:37, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Anton.Martynenko

unread,
Feb 17, 2012, 3:16:11 PM2/17/12
to DDD/CQRS
I think You need only one aggregate. Person should be entity which is
used by Department Aggregate.
In this case Aggregate will be able to check condition using it's own
internal data.
If you are using existing Person aggregate - you can use it's
projection of events which will tell you the age of person. But there
is an issue: projections are eventually consisted. Just keep that in
mind =)

On Feb 16, 1:59 pm, "Dim@sty" <Dim_a...@mail.ru> wrote:
> Hi All
>
> For example
> I have two Aggregate roots
>
> 1AR
>
> class Person : AggregateRoot
> {
>         int Age {get;set;}
>
> }
>
> 2 AR
> class Department : AggregateRoot
> {
>          List<Guid> persons;
>          RegisterPerson(Guid personId);
>
> }
>
> There is a business rule that says that a person may be registered in
> the department, if its the age is than 25 years
>
> My Question
> How to implement this rule, if I have no reference to the person, but
> only an ID
> or implement a rule in the command handler
>
> void Handle(RegisterPersonInDepartment cmd)
> {
>        var person = domainRepository.Get<Person>(cmd.PersonId);
>        var department =
> domainRepository.Get<Department>(cmd.DepartmentId)
>        if(person.Age > 25) department.RegisterPerson(person);
>
> }
>
> Is it correct ???

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 3:43:25 PM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 1:59 PM, @yreynhout <yves.r...@gmail.com> wrote:
This assumes noone will bypass your client. I'm not saying you can't
trust the client, but you better build in security if you're going to.

I assume you read my next post that clarified that I was not talking about having the domain rely on UI validation, so I won't need to respond to this.

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 3:46:59 PM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 2:09 PM, @yreynhout <yves.r...@gmail.com> wrote:
Which comes back to my statement, why do it with a read model, when
you could express it using readable, encapsulated domain behavior. I'm
not saying the read model query won't be readable, but you'll be
putting "You can't do this behavior under the age of 25" in the
command handler (the application service).

No. I would model it something like this:

RegisterPerson(Guid personId, Int age)

or

RegisterPerson(ReadModelPerson person)


@yreynhout

unread,
Feb 17, 2012, 4:08:57 PM2/17/12
to DDD/CQRS
So, I'm to conclude you rather go out to a readmodel to get the
birthdate, calculate the age, pass it into RegisterPerson (what AR is
that method on?), and have the compare in there? All, that to avoid
having the Person AR track its own birthdate(ergo derived age) and
have it collaborate with the Department to produce a Registration.
Where do you get the idea that ARs can't collaborate?

On 17 feb, 21:46, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Nils Kilden-Pedersen

unread,
Feb 17, 2012, 4:45:32 PM2/17/12
to ddd...@googlegroups.com
On Fri, Feb 17, 2012 at 3:08 PM, @yreynhout <yves.r...@gmail.com> wrote:
So, I'm to conclude you rather go out to a readmodel to get the
birthdate, calculate the age, pass it into RegisterPerson (what AR is
that method on?),

It's on Department, as indicated by the OP.
 
and have the compare in there? All, that

Why do you describe it as "all that"? I see no additional effort over what you describe, so please explain.
 
to avoid
having the Person AR track its own birthdate(ergo derived age) and
have it collaborate with the Department to produce a Registration.

It's not to avoid having the Person AR track anything, but to avoid it slowly becoming a read-model.
 
Where do you get the idea that ARs can't collaborate?

I'm not ruling out collaboration, just that in this example, the Person AR is used strictly for reading, which in light of CQRS, I consider a code smell.

Both approaches will work, I'm just disinclined towards yours, not religiously opposed.

Nick Peeples

unread,
Feb 17, 2012, 5:42:32 PM2/17/12
to ddd...@googlegroups.com
It's not to avoid having the Person AR track anything, but to avoid it slowly becoming a read-model.
 
Where do you get the idea that ARs can't collaborate?

I'm not ruling out collaboration, just that in this example, the Person AR is used strictly for reading, which in light of CQRS, I consider a code smell.

I agree on the read-only collaboration code smell. I stated earlier, if you allow the DeptAR access to even a read property on the Person AR, you are coupling these Roots. Yes, they are within the same bounded context, but the smell is definitely in the reference from Dept. back to Person. I would much rather give the Register function on Dept. the calculated age, or a reference to an abstraction/interface that can allow it to obtain the age for a particular person. This would allow both the Roots to evolve on their own, which is completely my intention in creating two roots in the first place. It also helps avoid the whole anemic problem.

- Nick

@yreynhout

unread,
Feb 18, 2012, 4:33:29 AM2/18/12
to DDD/CQRS
It's safe to say I disagree with the both of you, because to me this
is breaking encapsulation (if we can agree that Age belongs to
Person). Side note: Person could expose a role interface for this
specific collaboration.
I'm interested in how you guys perceive that this is a smell (and it
should be obvious that I'm not exposing Age as a property on Person,
I'm merely asking a question in UL terms (IsOlderThan which could be
internal)). What's so bad about having Person and Department
collaborate in this behavior/use case? How am I hindering evolution of
both Roots? I'd like to get to the bottom of this :-) Is this gut
feeling, did somebody tell you to do it this way, have you been bitten
by this sort of coupling?
As far as I can see, you both seem to be very willing to get the
derived Age property from the read model as to not couple the ARs. Yet
the coupling is indirect (the read model got the birthdate from the
Person AR in the first place), the coupling is with the read model.
But as you stated, that doesn't seem to bother you, as long as the ARs
are not relying on each others methods. I'm pretty sure in traditional
DDD, this reliance is not a problem. Nevertheless I'll verify this
with other DDD practitioners (cross posting to DDD yahoo group).

@yreynhout

unread,
Feb 18, 2012, 4:37:07 AM2/18/12
to DDD/CQRS
Remembering the birthdate of the person seems easier than going to the
read model, that's all.
As for using another AR for reading purposes, this is always going to
be the case when multiple ARs are collaborating. Afterall, we should
only be affecting one AR at a time.

On 17 feb, 22:45, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Philip Jander

unread,
Feb 18, 2012, 6:32:44 AM2/18/12
to ddd...@googlegroups.com
Let me chime in on this.

This set of problems naturally arises whenever one develops a
non-trivial domain model. It is obvious, that there is no immediate
"easy" solution.

Here is my take on this: the problem simply stems from the fact that we
are considering a collaboration. There are three entities involved:
Person, Department and Registration. So the possible points of contact are:

a) have Person expose its Age (to be used by Department or Registration)
b) have Person expose an IsOlderThan guard method/function (to be used
by Department or Registration)
c) move the rule into Person
d) use the readmodel to inject the age via command to the Department or
Registration
e) have person provide the age

If we are within one context, I personally rule out option d. The reason
is that coupling via readmodel is even worse than exposing a property.
It still couples but the coupling is hidden. This is a bit different if
we are talking about separate contexts, there the age information would
simple become part of the external interface. But within one context,
using the readmodel kindof defeats the idea of having a domain model.

So I am left with three options within the domain model. All of them
couple Person and Department or Registration. Which I consider perfectly
ok, what would be the point of having a domain model if the objects may
not interact?
Hence, I will consider different principles in deciding what to choose.
First, I like to look up Nicola et al's Streamlined object modeling for
such questions. The relevant passage is:

"A validation rule verifies a property value against a standard that is
not dependent on the properties of the other potential collaborators.
The object with the clearest access to the standard is the owner of the
collaboration rule." [SOM p55]
This seems to be either the Department or the Registration, depending on
formulation of requirements. At any rate this immediately rules out
option c. Person must not know about rules of the department.

Now SOM make heavy use of getters and setters. They would go for option
a. Since I instead intend to follow TDA, I cannot use a, which leaves me
with b or e.
But, actually IsOlderThan is a getter, too. And here I differ from
Ernst's implementation. So I play the TDA card again and dismiss option
b as just a copy of a.

Which leaves me with my desired strategy "have person provide the age".
But to whom? Now the registration is the entity mediating the
collaboration, therefore I suggest that Person checks its own rules,
department checks its own rules and registration checks the rules
governing the collaboration. Since they are potentially complex, I
actually move them into a factory.

In (pseudo)code:

Department{
RegisterPerson(Person person){
GuardIfICanRegisterAtAll();

var registration = new RegistrationFactory();
registration.SetDepartment(this);

person.RequestRegistration(registration);

registration.Do();
}
}

Person{
RequestRegistration(RegistrationFactory registration){
GuardIfICanRegisterAtAll();
registration.SetPerson(this, this._age);
}
}

RegistrationFactory{
SetDepartment(Department department){
_department = department.Id;
}
SetPerson(Person person, Age age){
_person = person.Id;
_age = age;
}

Do(){
EnsurePreconditions();
AgeRule();
new Registration(RegistrationHandle.Create(), _department,
_person); // which in turn emits the appropriate event which is
collected by a UoW.
}

EnsurePreconditions(){
if (_department==null) throw new
RegistrationFailedDueToInternalError("Department not provided");
if (_person==null) throw new
RegistrationFailedDueToInternalError("Person not provided");
if (_age==null) throw new
RegistrationFailedDueToInternalError("Age not provided");
}

AgeRule(){
if (_age < Age.FromYears(25)) throw new
RegistrationFailedPersonTooYoung(); // <-- here is the thingy
}
}

and the command handler

Handle(RegisterPersonInDepartment cmd)
{
var person = repository.Get<Person>(cmd.PersonId);
var department = repository.Get<Department>(cmd.DepartmentId);

department.RegisterPerson(person);
}

(using cqrs/event sourcing with if-it-doesn't-throw-it-succeeds
semantics and auto-registration of the dirty aggregate)

This way, encapsulation is preserved, TDA is observed and the rules are
with their respective owners. Requiring person to provide the age to the
registration factory is fine - this simply is part of the factory's
contract. Person is of course free to deny the collaboration.

My 2 cts. What do you think?

Cheers
Phil

@yreynhout

unread,
Feb 18, 2012, 9:43:14 AM2/18/12
to DDD/CQRS
+1. From a "being pure" perspective, you're spot on. Personallly, I
have no problem with b) because it's expressed in the UL (there's a
limit as to far I go with TDA, but that's kinda personal). I wouldn't
model a registration factory explicitly, as I would have Department
play that role (but again that's personal taste, not a matter of wrong
or right), and add the registration to its repo in the handler.
I think a lot has to do with if you are taking on behavior invocation
in an Actor style or a more traditional DDD style. I'm in the latter
camp, but understand there's a place and time for the first too.
I'm glad to hear I do not stand alone wrt taking the read model on as
a dependency. My faith in common sense was shaken for a moment there.
Yet I do respect that others have a different view on these things. We
all need to bump in to our own walls before being able to see the
light. And those walls might not be the same for all of us.

Regards,
Yves.

Nils Kilden-Pedersen

unread,
Feb 18, 2012, 12:44:04 PM2/18/12
to ddd...@googlegroups.com
On Sat, Feb 18, 2012 at 3:37 AM, @yreynhout <yves.r...@gmail.com> wrote:
Remembering the birthdate of the person seems easier than going to the
read model, that's all.

I guess I still don't get why reading a Person out of the repository is any easier than getting the person (or its attributes) out of a read-model.
 
As for using another AR for reading purposes, this is always going to
be the case when multiple ARs are collaborating.

Yes, when using your approach. Not when using a read-model.
 
Afterall, we should
only be affecting one AR at a time.

Indeed.

Nils Kilden-Pedersen

unread,
Feb 18, 2012, 12:47:40 PM2/18/12
to ddd...@googlegroups.com
On Sat, Feb 18, 2012 at 3:33 AM, @yreynhout <yves.r...@gmail.com> wrote:
It's safe to say I disagree with the both of you, because to me this
is breaking encapsulation

How so? Other than a read-model by definition breaks any encapsulation, but that's kind of the point.
 
I'm interested in how you guys perceive that this is a smell (and it
should be obvious that I'm not exposing Age as a property on Person,
I'm merely asking a question in UL terms (IsOlderThan which could be
internal)).

IsOlderThan is for asking, not telling.

Nils Kilden-Pedersen

unread,
Feb 18, 2012, 1:05:51 PM2/18/12
to ddd...@googlegroups.com
On Sat, Feb 18, 2012 at 5:32 AM, Philip Jander <jan...@janso.de> wrote:
a) have Person expose its Age (to be used by Department or Registration)
b) have Person expose an IsOlderThan guard method/function (to be used by Department or Registration)
c) move the rule into Person
d) use the readmodel to inject the age via command to the Department or Registration
e) have person provide the age

If we are within one context, I personally rule out option d. The reason is that coupling via readmodel is even worse than exposing a property.

Why?

 
It still couples but the coupling is hidden.

Coupling is explicit. It's explicitly required by RegisterPerson, just like it's explicit on your RegistrationFactory.
 
This is a bit different if we are talking about separate contexts, there the age information would simple become part of the external interface. But within one context, using the readmodel kindof defeats the idea of having a domain model.

A domain model's purpose is to encapsulate business logic. The choice between using a read-model or another AR doesn't change that.
 
   EnsurePreconditions(){
       if (_department==null) throw new RegistrationFailedDueToInternalError("Department not provided");
       if (_person==null) throw new RegistrationFailedDueToInternalError("Person not provided");
       if (_age==null) throw new RegistrationFailedDueToInternalError("Age not provided");
   }

Huge code smell :-)
 
My 2 cts. What do you think?

Seems fine. I would have chosen a class design that would avoid having invalid states, to avoid EnsurePreconditions, but beyond that the approach seems reasonable, but perhaps overkill? I'm surprised Yves doesn't call it "all that" :-)

@yreynhout

unread,
Feb 18, 2012, 1:14:07 PM2/18/12
to DDD/CQRS
Well, the projection into the read model isn't free (dedicated read
model). The query mechanics are duplicated as far as I can see. Not
difficult code, but other code nonetheless. And again, you're coupling
to the read model. I also feel this doesn't answer my questions.
Why are you so willingly to break encapsulation? And again, where does
this wisdom of using a readmodel is better than having objects
collaborate come from?

On 18 feb, 18:44, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

@yreynhout

unread,
Feb 18, 2012, 1:25:00 PM2/18/12
to DDD/CQRS
So, it's a query and all queries belong to the readmodel, is that it?
By that definition all usage of state is a query. But I am assuming
that this advice applies to cross aggregate state querying. I could
turn things around using double dispatch and TDA, but it'd still be a
mild form of Query.

I find the coupling reasons the wrong one, but respect your choice :)

On 18 feb, 18:47, Nils Kilden-Pedersen <nil...@gmail.com> wrote:

Colin Yates

unread,
Feb 18, 2012, 1:55:02 PM2/18/12
to ddd...@googlegroups.com
newbie flying by here, a Java one no less, but I just wanted to say that these in-depth discussions about seemingly simple examples are incredibly refreshing and enlightening.  Nice to see people still consider these things important enough to take the time over, it kills me inside how many of my colleagues would say "don't worry, as long as it works..."!.

Again, for emphasis, nice explanation - very illuminating!

Philip Jander

unread,
Feb 18, 2012, 2:21:41 PM2/18/12
to ddd...@googlegroups.com
Hi Yves,

> Personallly, I
> have no problem with b) because it's expressed in the UL (there's a
> limit as to far I go with TDA, but that's kinda personal).
I completely agree here. This is a matter of style, there is no right or
wrong with respect to TDA or more "normal" DDD-style.

> I wouldn't model a registration factory explicitly, as I would have Department
> play that role
Yes, that is probably overkill for such a simple scenario and hence
again a matter of taste.

> I'm glad to hear I do not stand alone wrt taking the read model on as
> a dependency. My faith in common sense was shaken for a moment there.
> Yet I do respect that others have a different view on these things. We
> all need to bump in to our own walls before being able to see the
> light. And those walls might not be the same for all of us.
True, but I have bumped heavily into that particular wall so I have a
stronger opinion here by now.
I'll post my issues in reply to Nils to keep the threads intact ;)

Cheers
Phil


@yreynhout

unread,
Feb 18, 2012, 2:28:20 PM2/18/12
to DDD/CQRS
I'm already glad someone follows my reasoning :) no, seriously, I'd
code it differently, but the spirit would be the same as Philip's.

On 18 feb, 19:05, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
> On Sat, Feb 18, 2012 at 5:32 AM, Philip Jander <jan...@janso.de> wrote:
> > a) have Person expose its Age (to be used by Department or Registration)
> > b) have Person expose an IsOlderThan guard method/function (to be used by
> > Department or Registration)
> > c) move the rule into Person
> > d) use the readmodel to inject the age via command to the Department or
> > Registration
> > e) have person provide the age
>
> > If we are within one context, I personally rule out option d. The reason
> > is that coupling via readmodel is even worse than exposing a property.
>
> Why?
>
> > It still couples but the coupling is hidden.
>
> Coupling is explicit. It's explicitly required by RegisterPerson, just like
> it's explicit on your RegistrationFactory.
>
> > This is a bit different if we are talking about separate contexts, there
> > the age information would simple become part of the external interface. But
> > within one context, using the readmodel kindof defeats the idea of having a
> > domain model.
>
> A domain model's purpose is to encapsulate business logic. The choice
> between using a read-model or another AR doesn't change that.
>
>    EnsurePreconditions(){
>
> >        if (_department==null) throw new
> > RegistrationFailedDueToInternalError("Department not provided");
> >        if (_person==null) throw new RegistrationFailedDueToInterna**lError("Person
> > not provided");
> >        if (_age==null) throw new RegistrationFailedDueToInterna**lError("Age

@yreynhout

unread,
Feb 18, 2012, 2:28:38 PM2/18/12
to DDD/CQRS
:)

Philip Jander

unread,
Feb 18, 2012, 2:47:00 PM2/18/12
to ddd...@googlegroups.com
Hi Nils,

If we are within one context, I personally rule out option d. The reason is that coupling via readmodel is even worse than exposing a property.

Why?
Glad you asked :)

I have five issues with using the readmodel.

#1 is that I believe the business logic of one bounded context using a domain model should be fully encapsulated in that domain model. That includes the interactions and collaborations. Having a path - even it is only for data access - outside of the domain model for the sole purpose of not explicitly accessing that data within the model doesn't make a lot of sense to me.

#2 is testing. I want to be able to test any domain by itself. And that includes the collaborations which are usually an important part of the model's behavior. But I do *not* want to have my read model projections be a part of that.

#3: I once learned that all dependencies in which a domain model participates should point inward to the domain model. This includes both afferent and efferent ones. Having to depend on the readmodel violates that.

#4: Let me ask, what is the reason to invoke the readmodel? Usually I hear "aggregates should not depend on each other". Frankly, that is nonsense. A domain model consists of classes that of course have interdependencies. While good model design tries to reduce and qualify these dependencies, having *none* is not of value by itself. An aggregate's fence doesn't say 'no access' but rather 'take me as a unit or not at all' to other aggregates.

#5 is that I did it. That part of the system became a black hole pulling more and more of first data and then logic away from the domain model. Over the course of two years it gradually shifted from "nice shortcut" to "nightmare".

Of course, one can do it, and it will work. But successfully only for some time or for non-complex systems or for systems that do not change. I.o.w. for systems that do not really call for a domain model :)



A domain model's purpose is to encapsulate business logic. The choice between using a read-model or another AR doesn't change that.
I feel that this statement is incorrect. "Using a read-model" breaks the encapsulation of the domain model as the readmodel is outside of its boundaries. "Another AR" is within these boundaries.





Seems fine. I would have chosen a class design that would avoid having invalid states, to avoid EnsurePreconditions, but beyond that the approach seems reasonable, but perhaps overkill? I'm surprised Yves doesn't call it "all that" :-)
Yes, honestly I didn't put much thought to that factory. There are possibly ways to do that better :)

Cheers
Phil

Yevhen Bobrov

unread,
Feb 18, 2012, 2:52:02 PM2/18/12
to ddd...@googlegroups.com
+1

That's a real OO/DDD approach.

It doesn't break encapsulation. It's clean, easy testable (without
involving any infrastructure), and pragmatic (in all senses).
Basically it's collaboration between objects due to application of Tell
Don't Ask and Information Owner.

Also it has the same degree of consistency as asking read store, thus
using read store in this case have no benefits, it will only make domain
objects more dumb and command handlers more fat, and will likely end up
as just a reminiscence of Transaction Script (Controller-like style).

All data required to fulfill business behavior (rule) should be
available on write side, inside of domain objects which represent
business entities \ processes. I'll second Yves on that as well.

Shifting all information retrieval logic on a client, is a shift of
system (domain) intelligence, IMHO. As a knowledge of where to get
particular information (or whom to ask as in Yves' sample) is intrinsic
part of it.

The model should be cohesive. It is built with that purpose (in DDD).
And it usually highly cohesive within single Bounded Context.

Yevhen

17.02.2012 1:06, @yreynhout пишет:
> void RegisterPerson(Person person) {
> If(!person.IsOlderThan(25.OfAge())) {
> throw Up();
> }
> //...
> }
>
> On 16 feb, 18:31, Nils Kilden-Pedersen<nil...@gmail.com> wrote:
>> On Thu, Feb 16, 2012 at 10:20 AM, ShuriK<alex.ziz...@gmail.com> wrote:
>>> I understand that a Saga or CommandHandler can get access to the read
>>> side, but I don't like to use it, if all necessary information exists
>>> in the write side, e.g. in the Person and Department ARs. I think that
>>> write side should never get data from the read side if it possible in
>>> other way.
>> It means that you have to break encapsulation and expose all data outside
>> the class, making an anemic domain model more likely.
>>
>> But if that's an acceptable trade-off, then who am I to argue?

CraigCav

unread,
Feb 19, 2012, 10:51:23 AM2/19/12
to DDD/CQRS
I'd like to add my 2 cent to the discussion.

Although I have no problem with the approach Philip and Yves mention,
I would like re-propose the saga alternative, but for a different
reason; handling a long-running business process.

Generally when businesses handle this kind of collaboration, the
person is required to complete a registration form of some kind
(someone had previously mentioned the registration aggregate). The
person usually fills out this registration form with the relevant form
(including the department identifier, their DOB, etc). At some later
point, the department will process this application request - ensuring
that the person is the correct age to be registered.

I'd propose then that the person AR fills in the registration, raising
an event RegistrationSubmitted, which in turn is relayed to the
department AR by a saga coordinating this communication. The
registration contains all the necessary person details for the
department to make its decisions upon.

We respect TDA, and model the business process, and do not need to
fall back on calling into read models.

Craig

belitre

unread,
Feb 19, 2012, 11:04:48 AM2/19/12
to DDD/CQRS
It sounds great for most business rules.

But, What happen's with just "existence" business rules? I mean, you
sometines want to ensure that a country, currency or many other
"master data" exists when a command is received with a couple of ID's.
I think a trade-off is needed in those cases, and perhaps you need to
repeat the master data in each bounded context that needs it (and
maybe to check the id existence through a read model within the same
bounded context).

Anything but going back to infinite RDBMS foreign keys ;-)

@yreynhout

unread,
Feb 19, 2012, 11:48:31 AM2/19/12
to DDD/CQRS
+1. Agreed, it depends on how this process works in real life, i.e.
how much automation is going on and what the role of time is in the
process.

@yreynhout

unread,
Feb 19, 2012, 11:58:47 AM2/19/12
to DDD/CQRS
Most of the master data you mention doesn't need a surrogate key; just
use an ISO code and treat it as a value object. Granted, this will not
cover every scenario. Master data usually serves as a list for the
user to pick from while assembling data for a given command.

Yevhen Bobrov

unread,
Feb 19, 2012, 2:39:25 PM2/19/12
to ddd...@googlegroups.com
Countries and currencies are static data, just value objects. I never
model them as entities. Such lists are just hardcoded.

We were discussing different case: checking that person is older than 25
years is a business rule and should be expressed on a domain level.

But checking that person id, passed in a command, is indeed refer to an
existing entity, is an application level validation. You can't do it on
a domain level, cause domain is not aware of persistence (and should not
be). Domain operates on already provided references (objects), so you
usually don't have something like department.RegisterEmployee(Guid
employeeId) but rather department.Register(Employee employee).

BTW, in Yves' example, that validation of id existence is done in a
right place - in a command handler, which is an application level mediator.

var department = repository.GetById<Department>(cmd.DepartmentId);
var employee = repository.GetById<Employee>(cmd.EmployeeId);

The natural approach here is to add existence checking and blow up if
you can't obtain aggregate by a given id.

if (employee == null)
throw EmployeNotFoundException("Can't find employee with..");

What we did is just added a simple Guard Clause on a Boundary. We just
asserted whether we can further proceed with the operation at all.

Yevhen

19 О©╫О©╫О©╫О©╫. 2012, О©╫ 18:04, belitre <bel...@gmail.com> О©╫О©╫О©╫О©╫О©╫О©╫О©╫(О©╫):

>> 17.02.2012 1:06, @yreynhout О©╫О©╫О©╫О©╫О©╫:

Aaron Navarro

unread,
Feb 19, 2012, 7:08:21 PM2/19/12
to DDD/CQRS
Yves,
Not sure if I am understanding you here, are you saying not to have a
country aggregate, but to have a "countries" aggregate and then store
all the countries as value objects, that way you could pass the
countries object to another aggregate to verify something like
uniqueness?

Which brings about one of my long-standing questions which is how to
deal with validation which requires many aggregates of the same type
without the readmodel. I have been using a combination of both
readmodel and domain validations, as primarily expressed above,
because of the situations where I want validation like "no more than
10 persons over the age of 25 can be registered at one time".

If what you mean regarding master data is correct, (and i do something
like that already) that only works for master data not entities like
Person. My domain repos can only load aggregates by id anyways so I
have no easy way of getting all existing "persons over 25" (perhaps
other have set based queries??).

These type of validations seem common to me, not edge cases, so I am
hesitant to accept either approach as the more appropriate way if it
does not work in these cases.
Thoughts?

belitre

unread,
Feb 20, 2012, 2:27:50 AM2/20/12
to DDD/CQRS

@yreynhout, for me an ISO code is just another id (natural). The same
problem applies. My business layer team feels really uncomfortable
with "Client friendship", I mean, asuming commands are built properly.
Hardcoded or Stored, we are going to need existence and cardinality
checks(thanks @Aaron for the example).

Regards.

Yevhen Bobrov

unread,
Feb 20, 2012, 9:50:47 AM2/20/12
to ddd...@googlegroups.com
Hi Aaron,

> Which brings about one of my long-standing questions which is how to
> deal with validation which requires many aggregates of the same type
> without the readmodel. I have been using a combination of both
> readmodel and domain validations, as primarily expressed above,
> because of the situations where I want validation like "no more than
> 10 persons over the age of 25 can be registered at one time".

No, it doesn't require read model. You either missing an aggregate or
need to use Sagas.

In you example if creation and registration of person are two
independent actions, then
you can encapsulate this business logic in an aggregate:

class Department : Aggregate
{
const int MaxNumberOfEmployessOverAge25 = 10;

List<Employee> employees;
int numberOfEmployessOverAge25;

void Register(Employee emp)
{
if (emp.IsOlderThan(25)
{
if (IsLimitReached())
throw new RegistrationLimitReached(...);

numberOfEmployessOverAge25++;
}

employees.Add(emp);
}

bool IsLimitReached()
{
return numberOfEmployessOverAge25 = MaxNumberOfEmployessOverAge25;
}
}

If both creation and registration should be done atomically, then you
need to use a Saga,
which will make a multi-step process, look like an atomic one.

So under the hood you will still have 2 discrete actions: create and
register.
Te saga will compensate creation (by issuing delete) if registration fails.

Yevhen

20.02.2012 2:08, Aaron Navarro пишет:

Yevhen Bobrov

unread,
Feb 20, 2012, 11:30:03 AM2/20/12
to ddd...@googlegroups.com
> Hi, Yevhen:
>
> You mentioned that "Domain is not aware of Persistence", and that also
> "lists are hardcoded".
>
> If your lists are hardcoded, your domain can be aware of allowed
> elements, so checking integrity within the domain. If you need
> repositories, your application would do.
>
> Does it make sense for you?
>
> Best regards,

No, it doesn't ;)

Checking that passed 3-letter ISO code resolves to a valid currency
is still an application level validation.

class InvoiceHandler
{
void Handle(SubmitInvoice cmd)
{
...
var currency = Currency.From(cmd.Currency);
if (currency == null)
throw new BadInputException("Bad currency ISO code");

if (cmd.Amount <= 0)
throw new BadInputException("Amount should be > 0");

var amount = new Money(cmd.Amount, currency);

supplier.SubmitInvoice(.., amount);
}
}

When domain is about to be invoked, everything should be already determined
and references are retrieved and checked.

I never do checks like IsValid or IsExists in domain.
Domain is always in a valid and deterministic state.

The only checks that Domain objects might have are argument checks,
like if arg is null or empty string. And people don't even bother to
have this
because an input is already pre-validated on a boundary.

If you have such problems it could be a sign of Primitive Obsession smell.

CQRS doesn't force you to have only primitive entities with primitive
fields.
On opposite, by introducing another layer of indirection like Commands,
it allows you to evolve your domain without being concerned about contract
breaking issues. So while you command feature primitive data, when it comes
to application, data is converted into a set of more sophisticated objects,
collaborating to complete a specific use-case. Command handlers are in fact
classic use-case controllers.

IMHO, the power of DDD lies in all those small and useful objects like
Money (where you can encapsulate your rounding and allocation rules),
Currency, DateRange etc. You can find many of those in EAP, Analysis
Patterns,
and Archetypes. And Eric's example of share pie math is what completely
blown me away and inspired to learn, grasp and apply DDD for many years.

To return to our discussion about relative cohesion of model elements:
If you open Analysis Patterns, you will see that in any of given models, all
elements are highly cohesive - they tightly collaborate to complete a
specific
computation or enforce a concrete business rule.

Hope it helps,
Yevhen

20.02.2012 9:27, belitre пишет:

Nuno Lopes

unread,
Feb 20, 2012, 12:00:34 PM2/20/12
to ddd...@googlegroups.com
Hi,

Why not: 

person.RegisterInDepartment(departmentId).

Why do we even have a person?

PersonDepartrmentRegistration registration = new PersonRegistration(name, age, ....., departmentId, ....)
AllRegistrations.Add(registration).

If we have a person already why not:

PersonDepartrmentRegistration registration = new PersonRegistration(personId, age, departmentId, ....)
AllRegistrations.Add(registration).

In sum, what properties of a department impose invariants across all registrations made on it? To they even reside in a Department aggregate?

....

Nuno

On Feb 16, 2012, at 11:59 AM, Dim@sty wrote:

void Handle(RegisterPersonInDepartment cmd)
{
      var person = domainRepository.Get<Person>(cmd.PersonId);
      var department =
domainRepository.Get<Department>(cmd.DepartmentId)
      if(person.Age > 25) department.RegisterPerson(person);
}

@yreynhout

unread,
Feb 20, 2012, 12:58:55 PM2/20/12
to DDD/CQRS
No, that's not what I'm saying. I'd just provide the country iso code
as part of the command, such as NewDumpSiteCommand { CountryCode = new
CountryCode { IsoCode = "BEL" } }.
Then, on the client/server I'd perform static validation against a
list of wellknown countrycodes (how often does this list change?),
either hardcoded or as some sort of "initialized once" resource.
Usually this is part of command validation as a whole.
If this test passes, you venture into the actual domain behavior (or
send it to the server, depending on where you are at that point
(client)).
You could craft a value object in the domain to represent the
CountryCode and give it the desired behavior (a matter of primitive
obsession and how comfortable you are with the idea of having lots of
small classes).
The application service (the command handler) could use the
CountryCode value object as a parameter of the behavior to invoke:
organisation.NewDumpSite(...,
AdaptCountryCode(command.CountryCode), ...); where AdaptCountryCode
takes care of the conversion into a value object (other/better ways of
doing this, obviously).

The point is - in case of CountryCode - I don't see a need for it to
become an Aggregate/Entity despite it having some form of identity.
There's no mutable state in there, no invariants. If you're seeking
referential integrity, see Yevhen Bobrov's response (this one:
http://groups.google.com/group/dddcqrs/msg/75e168f027ea4665), but IMO
it doesn't apply to CountryCode. It's just copied and tracked into
whatever behavior needs it.

I believe Yevhen Bobrov answered your second question.

HTH,
Yves.

On 20 feb, 01:08, Aaron Navarro <anavarro9...@gmail.com> wrote:

@yreynhout

unread,
Feb 20, 2012, 1:00:33 PM2/20/12
to DDD/CQRS
You're stuck in a referential integrity world, my friend (no offense).
It's overrated, especially for things that don't mutate over time.

belitre

unread,
Feb 20, 2012, 4:59:19 PM2/20/12
to DDD/CQRS
Thanks for your answers, @Yevhen and @yreynhout.

Nils Kilden-Pedersen

unread,
Feb 20, 2012, 5:15:03 PM2/20/12
to ddd...@googlegroups.com
On Sat, Feb 18, 2012 at 1:47 PM, Philip Jander <jan...@janso.de> wrote:
I have five issues with using the readmodel.

#1 is that I believe the business logic of one bounded context using a domain model should be fully encapsulated in that domain model. That includes the interactions and collaborations. Having a path - even it is only for data access - outside of the domain model for the sole purpose of not explicitly accessing that data within the model doesn't make a lot of sense to me.

Do you consider command handlers separate from the domain? I do, and that's where the dependencies are, not inside the domain model.
 
#2 is testing. I want to be able to test any domain by itself. And that includes the collaborations which are usually an important part of the model's behavior. But I do *not* want to have my read model projections be a part of that.

Again, it depends on the scope of your domain. I have no read model dependencies in my domain model (because I don't consider command handlers part of the domain, you may differ).
 
#3: I once learned that all dependencies in which a domain model participates should point inward to the domain model. This includes both afferent and efferent ones. Having to depend on the readmodel violates that.

Same answer.
 
#4: Let me ask, what is the reason to invoke the readmodel? Usually I hear "aggregates should not depend on each other". Frankly, that is nonsense. A domain model consists of classes that of course have interdependencies. While good model design tries to reduce and qualify these dependencies, having *none* is not of value by itself. An aggregate's fence doesn't say 'no access' but rather 'take me as a unit or not at all' to other aggregates.

I guess we'll have to remain disagreeing on this.
 
#5 is that I did it. That part of the system became a black hole pulling more and more of first data and then logic away from the domain model. Over the course of two years it gradually shifted from "nice shortcut" to "nightmare".

It sounds like we're talking about two different things, because I cannot recognize what you describe at all. Having a fully encapsulated AR I feel well insulated against logic creeping away from the domain model. Exposing the data would lead to that, so I wonder if we are talking past each other?
 
Of course, one can do it, and it will work.

Indeed, it does.
 
But successfully only for some time or for non-complex systems or for systems that do not change. I.o.w. for systems that do not really call for a domain model :)

We'll see. :-)

Julian Dominguez

unread,
Feb 20, 2012, 7:17:39 PM2/20/12
to ddd...@googlegroups.com
I mostly agree with Yves, on trying to avoid to couple the domain
model with the read model. If both ARs are in the same bounded
context, they are allowed to collaborate. You should avoid relying on
transactional consistency between them, but that doesn't mean they are
not aware nor can't collaborate with each other.
On the other hand, and this was mentioned lightly but I think it's
VERY important (and will render my previous point unnecessary in this
situation), you will probably have a task based UI, where you will not
even allow sending a command like this. So I assume this is validation
that should not be encapsulated deep in the domain model. That said,
and assuming a web app for simplicity, then your web server will
already render the UI according to this rule. If for some reason, a
hacker tries to send a registration for a user that is not 25, then
the web server will do input validation and throw right there and then
(by looking at the read model, the same thing it used to render the UI
in the first place). If input validation passes (the user is older
than 25 and this is not a fake command), then it generates a proper
command that will be handled by the command handler. At this point you
CAN TRUST the sender, as your sender is the web server, not the client
browser.
At one point I was in the position that the command handler should
distrust EVERY command, and perform validation there too. This was
something Greg once made me realize shouldn't be the case... if you
can't trust your own assets then you have bigger problems. You should
instead add infrastructure security for example, to only allow certain
clients (web servers) to be able to put commands in a queue (in the
case of a distributed scenario).

My 2 cents,
Julian

Reply all
Reply to author
Forward
0 new messages