Is it normal for aggregate roots to contain services?

1,345 views
Skip to first unread message

Denny Crane

unread,
Mar 9, 2017, 6:40:58 PM3/9/17
to DDD/CQRS
So the aggregate root has the responsibility of enforcing invariants, so given the following:

A survey is generated. 
The business rules state that if a survey is generated then a notification must also be generated and sent
I have a notification service that reads account configurations (to determine how to send the notification) and sends the survey notification

Currently, I have a command handler (that receives commands from a service bus) that sort of controls this flow
- Generate a survey
- Save the survey
- Request the survey be delivered
- Track the notification on the survey
- Save the survey

I feel like the request for the notification living outside of the aggregate root makes it anemic. Should I refactor the Survey aggregate root to take an instance of ISurveyDelivery service (a domain service) and manage that? Or is it more proper to keep them separate?

Rickard Öberg

unread,
Mar 9, 2017, 8:17:04 PM3/9/17
to ddd...@googlegroups.com
Hey Denny,

To me, an aggregate is a function that takes a command and returns a
list of events. That's all it does. In my case, the thing that invokes
the aggregate is always a business use case context (in the DCI
sense). So, the context gets the command from the outside, sends it to
the aggregate, then invokes services and other things depending on
what events the aggregate produces. The aggregate is not anemic,
because it is what decides how a command translates to events, and
that's the crucial business logic. Wiring that to other services can
be done outside.

So, architecturally I have 3 different "things": I have aggregates for
command->event translaction, I have services for anything requiring
I/O or state, and I have contexts for tying it all together. If you
don't have contexts as a concept in your system, everything gets more
complicated, in my experience.

/Rickard
> --
> You received this message because you are subscribed to the Google Groups
> "DDD/CQRS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to dddcqrs+u...@googlegroups.com.
> Visit this group at https://groups.google.com/group/dddcqrs.
> For more options, visit https://groups.google.com/d/optout.

jarchin

unread,
Mar 10, 2017, 4:58:35 AM3/10/17
to DDD/CQRS
Hey Denny, and Rickard.

First, to Denny, I would say that an aggregate "contains" all it needs to produce events when given a command.

So, let's say you have a service that interacts with a third party. Then that service would - as we do at least - be injected into the aggregate, and used there.

Our aggregates are just classes with methods where the method signatures has arguments that correspond to command parameters.

In that sense, every method is its context. When it is called, then there is the context.

I would assume this is just a matter of how the code is organised, but I would be happy to see how it differs from using an explicit context like you do Rickard.
Do you prepare it in the command handler and pass it into the aggregate with each method call?

Best

Denny Crane

unread,
Mar 10, 2017, 11:53:17 AM3/10/17
to DDD/CQRS
I think where I am right now is closer to what Rickard is saying. My context is the command handler that responds to messages from the service bus (NSB in this case). I'm just having that cognitive dissonance because I feel like what jarchin is saying is more in the spirit of DDD because these events that it needs to generate include the notifications being sent. I'm hoping to get a few more people chiming in here.

So it sounds like there's another piece I may be missing...

So right now, I have 2 different concepts of "events": the events in the event store and events on the service bus. I've deliberately kept them very separate because one reflects what happens in the domain (the state of a domain object was updated) while the other reflects what has happened anywhere else in the system. The events in the event store are replayable but the NSB events are not. For that reason, the event handlers for NSB events only reside only in the command model so that they can be processed through the domain and event store events will be emitted. The query model will then write those changes to the appropriate stores.

With all that said, if I were to change my current aggregate root modeling to also cause domain events like I feel like it should "Survey was generated (event) caused a notification was sent event" but it doesn't necessarily tell the notification service to send the notification, but rather that event could be echoed within the domain so that another handler within the domain (a domain service) could pick it up and process. That sounds reasonable, but I have another problem with that. "Events" in general describe something that has already happened. If the aggregate root produces an event, hoping some other handler will process it, then it lied. So I see the only way to fix that is to have the service actually occur within the aggregate root and then raise the event. But doesn't overload the responsibility of the aggregate root? No, I don't think so. The aggregate is responsible for enforcing invariants; would this not be an example of an invariant? Surveys can't simply be generated, they have to be sent. But you can still reverse the thought process and argue that sending a survey is a service that both generates a survey and then sends it which then falls back into Rickard's description.

Hmm...

Rickard Öberg

unread,
Mar 10, 2017, 10:29:55 PM3/10/17
to ddd...@googlegroups.com
An aggregate is a function from command->events. The purpose is to
maintain the transactional boundary of the aggregate. Anything else
has to be done outside, especially if it involves I/O.

The business use case context (see
https://en.wikipedia.org/wiki/Data,_context_and_interaction) defines
the algorithm of the scenario that the command represents. This
includes calling the aggregate, as well as invoking services that
might do I/O.

In your case the creation of a survey would generate an event that
this happened (CreatedSurvey), and the context would react to that by
creating a notification (e.g. sending a CreateNotification to a new
NotificationAggregate), which would cause another event
(CreatedNotification), which would then be handled by the actual
notifier that needs to do some technical integration with an outside
service, and thus finally create either a NotificationSent or
NotificationFailed event. The business use case scenario (Interaction)
is always about the concepts, not about the technical nitty gritty
stuff. This means you can easily do unit tests for them to see what
happens in the happy and unhappy paths.

/Rickard

andho

unread,
Mar 13, 2017, 11:58:36 PM3/13/17
to DDD/CQRS
Adding to Ricard's post, to clarify, "Sending a Notification" is not an event but a Command, that is the result of the "SurveyCreated" event.

But for the NotificationAggregate, it should actually send the notification before it raises NotificationSent event.

jarchin

unread,
Mar 14, 2017, 6:03:04 AM3/14/17
to DDD/CQRS
Rickard, DCI makes a lot of sense.

I'm just wondering, about this part:

"So, architecturally I have 3 different "things": I have aggregates for 
command->event translaction, I have services for anything requiring 
I/O or state, and I have contexts for tying it all together."

I might have just misunderstood the meaning of your sentence, but:
Are you saying that a call to an external service should not be done within the aggregate, but rather outside it, within the context handling it?

Let's say the external service call, induces costs and/or other side effects. We want to make sure we are only calling it exactly the number of times required. This is something the aggregate would keep track of, right? So protecting that invariant while not calling the service from within the aggregate, how is that done?

Rickard Öberg

unread,
Mar 14, 2017, 6:12:46 AM3/14/17
to ddd...@googlegroups.com
2017-03-14 18:03 GMT+08:00 jarchin <jarch...@gmail.com>:
> Rickard, DCI makes a lot of sense.
>
> I'm just wondering, about this part:
>
> "So, architecturally I have 3 different "things": I have aggregates for
> command->event translaction, I have services for anything requiring
> I/O or state, and I have contexts for tying it all together."
>
> I might have just misunderstood the meaning of your sentence, but:
> Are you saying that a call to an external service should not be done within
> the aggregate, but rather outside it, within the context handling it?
>
> Let's say the external service call, induces costs and/or other side
> effects. We want to make sure we are only calling it exactly the number of
> times required. This is something the aggregate would keep track of, right?
> So protecting that invariant while not calling the service from within the
> aggregate, how is that done?

An aggregate is a function from command->events. If the aggregate
requires some state to determine the validity of applying the command,
other than the snapshot state it gets from events, then I would put
that into the command itself, and calculate it in the context by
calling a service. The command would therefore have information needed
to make the decision.

There's a thousand ways to slice this stuff, but for me, I try to keep
it so that an aggregate is very very simple (a function). Anything
requiring coordination belongs to the use case, therefore I put it in
my context class.

/Rickard

jarchin

unread,
Mar 14, 2017, 7:03:15 AM3/14/17
to DDD/CQRS
Cool. Function from command->event(s). That is clear.

If you don't mind, can I please just get a clear understanding how you see this:

The ExternalServiceA has a priced service from which we get information, used within a use case triggered by command EvaluteSomething.
The EvaluateSomething use case need information from ExternalServiceA to do the evaluation of Something (the evaluation takes place in the aggregate).
If ExternalServiceA has already been called before, the information is already present and the service shall not be called again.

1. In the command is present whether ExternalServiceA was called before or not.
We assume command has information that it was not called before.
2. In context, the service call is made. 
3. The result is passed into the aggregate.
4. The aggregate output events.

In order to actually get the information about the service call persisted, the resulting events probably include one like SomeInfoFromExternalServiceRetreived.

Is this more or less how you see it?

The reason I ask is because currently, aggregates are to us the exact opposite of very very simple.
Within a method in an aggregate, all needed to output events occur.
That means:
1. Validate that this aggregate did not do this call before.
2. Do the call if it didn't.
3. Parse results.
4. Produce events telling the world that the above just happened.

It would be great to know why the first approach is better than the second.

Denny Crane

unread,
Mar 14, 2017, 7:39:32 PM3/14/17
to DDD/CQRS
Correct. If the survey is created, then a notification must be sent. That is the business rule. My cognitive dissonance is around that rule being considered an invariant. It has to happen or else the survey is useless. So the business flow should be, at minimum:

SurveyCreatedCommand--[results in]-->SendNotificationCommand

Inside of the survey, the following events would build up: SurveyCreated and SurveyNotificationSent

Currently, my notification object is modeled as a value object that lives on the survey along with any other notifications that occur over the lifetime of the survey.

If the command to the notification service is made outside of the Survey aggregate, it feels anemic because that particular rule is enforced in the handler/context instead of the aggregate root that is supposed to enforce invariants like this.

Keeping in mind that we have 2 concept events in this system: the event store and the service bus -- I've toyed with the idea of having an event bus within the service/bounded context itself that would act as glue for domain events (vs. system events) but that seemed like overkill that would add unnecessary complexity, basically adding a 3rd concept of "event". So for the time being, the event store is the domain event source and NSB is the system event source.

I don't disagree that the context/handler makes sense to complete a service type operation which is what I'm currently doing, but it still seems to contradict the idea of a transactional boundary within the aggregate root. I'm still looking for that aha moment. Maybe we'll get lucky and Evans will chime in at some point.

Rickard Öberg

unread,
Mar 14, 2017, 9:47:22 PM3/14/17
to ddd...@googlegroups.com
Hey,

So without knowing the exact details of your situation, based on the
hypotheticals my first instinct would probably be to make an internal
proxied version of the priced service. You call it, it either gets you
a cached or updated price. Rather than putting that into the
aggregate. If you need to know in the command whether the price was
cached or "live", that would go into the command.

Hypotheticals are always tricky, because of lack of detail and
context, but just based on the below, that's how I would look at it.

cheers, Rickard

Danil Suits

unread,
Mar 14, 2017, 10:55:38 PM3/14/17
to DDD/CQRS
If the survey is created, then a notification must be sent. That is the business rule. My cognitive dissonance is around that rule being considered an invariant.

Yeah, I can see that would make a mess.  Here's my thinking (standard disclaimers apply).

Sending a notification is a write to some other system of record.  That's not a thing that your aggregate can enforce, because the effects are outside of your aggregate boundary -- indeed, the effects are outside of your system entirely, if I'm understanding correctly.

The authority of the aggregate, as far as enforcing/re-establishing the business invariant, ends at the aggregate boundary.  When we have a business rule that says that this change to an aggregate here should be followed by a change to the aggregate there, which is a pretty common thing, the command is generally dispatched to the second aggregate outside of the transaction that modifies the first.


Inside of the survey, the following events would build up: SurveyCreated and SurveyNotificationSent

I think some of your tangle here is that SurveyNotificationSent is being written in the wrong place.  The authority for whether or not a notification has actually been delivered is.. the whatever it is that accepts the write from your NotificationService.  In an idealized world, there would be a subscriber listening for SurveyCreated, and it would dispatch a command to the NotificationService, and if that write succeeded a NotificationSent response would acknowledge the command.  The combination of the SurveyCreated event and the NotificationSent commands would tell you that the flow had been satisfied.

My feeling is that, in the absence of a durable stream of events acknowledging successful notifications, your best course is to fake the existence of such a stream.  That is, you create an acknowledgment stream, and you write notification acknowledgements into that stream, and for your coordination you use that stream as a surrogate for the write that are happening in notification world.

it feels anemic because that particular rule is enforced in the handler/context instead of the aggregate root that is supposed to enforce invariants like this.

Right.  And the resolution of the dissonance is the understanding that the aggregate root is not supposed to enforce invariants like this.

Aggregates enforce state invariants, not coordination invariants, as far as I can tell.  Maybe some of the veterans have better language here?
 
 

Denny Crane

unread,
Mar 15, 2017, 12:16:14 AM3/15/17
to DDD/CQRS
I think some of your tangle here is that SurveyNotificationSent is being written in the wrong place.  The authority for whether or not a notification has actually been delivered is.. the whatever it is that accepts the write from your NotificationService.  

It does, actually! Right now I have a notification "service" in the domain that sends the message and then tells the survey that it was *sent* "NotificationSent". What it actually does is determine which notification command should be placed on the bus (sms, email, push, etc.). The notification microservice picks up the command and does its thing then places an event back on the bus stating that it was sent. When the event comes back from the actual notification service "NotificationDelivered" ,the handler in the survey service looks up the survey  aggregate and then tells it that it was delivered "NotificationDelivered". 

In an idealized world, there would be a subscriber listening for SurveyCreated

A subscriber within the domain/command-host? I feel like makes sense too (a reactive domain), but that's the one I was saying felt like overkill in terms of complexity. 

My feeling is that, in the absence of a durable stream of events acknowledging successful notifications, your best course is to fake the existence of such a stream.  

There is. I have two options: the event store stream, or the event bus. I'm currently using the event bus because it's there... but I'm not sure if that's the best option.

Right.  And the resolution of the dissonance is the understanding that the aggregate root is not supposed to enforce invariants like this.
Aggregates enforce state invariants, not coordination invariants, as far as I can tell.  Maybe some of the veterans have better language here?

Ahhh... I can buy into that fairly easily. I'd like to hear additional opinions/language on that as well!

Ben Kloosterman

unread,
Mar 15, 2017, 3:07:04 AM3/15/17
to ddd...@googlegroups.com
Yes its aneamic - why does that matter  ?  Most OO domains are rich and by rich  it normally means a small amount of business and mostly tons of data access code and plumbing helpers..

Remember CQRS / DDD is not a top level arch and most SOA services and nearly all Micro Services are aneamic. Logic like validation is scattered everywhere what matters is that the logic that mutates all your data  is of the most importance , is flexible , purely business ( eg not contaminated by Infrastructure/framerwork/ data access and hence treated specially. If the data is wrong your read will also be wrong.

Whether to make the service in or not depends on how reliable it is - if its not reliable i am sure there is business logic in how that is handled.
 

Regards,

Ben

jarchin

unread,
Mar 18, 2017, 11:24:28 AM3/18/17
to DDD/CQRS
Thanks Rickard.

All right, so there's at least no grand consensus on always placing call to external service outside of aggregates, it seems (?), but you'd rather place it outside as I take it.

(Sorry Denny for sort of sidetrack, the matter just caught my eye.)

Ben Kloosterman

unread,
Mar 19, 2017, 7:24:42 AM3/19/17
to ddd...@googlegroups.com
If its fetching than its probably better done outside most of the time eg the command handler ( its a coordinator) or on creating the command  .. If its an action  on another than it gets more tricky the more closely related logic the more id throw it inside.


Regards,

Ben

To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+unsubscribe@googlegroups.com.

Juan

unread,
Mar 19, 2017, 9:21:07 AM3/19/17
to DDD/CQRS
Hi. No. An aggregate should not contain services at all. Vaughn Vernon repeats it again and again in many talks... «don't do that»... refering to inject services into aggregates. Services should manage aggregates, and should do things that you cannot do inside an aggregate. But aggregates should not use services.

Ben Kloosterman

unread,
Mar 19, 2017, 5:44:25 PM3/19/17
to ddd...@googlegroups.com
DDD and CQRS are a bit different here..

Denny Crane

unread,
Mar 19, 2017, 11:03:57 PM3/19/17
to DDD/CQRS
I'm leaning towards no, myself, but the use case is still curious. For example, a good example of a domain doing its thing is a user factory method on the user object refusing to create a user if the username already exists. With that being said, it does sound better to have that as a domain service responsibility to validate the username and hash the password before calling the create method on User and User just has to trust that the information being fed to it is correct and only validate the items that it will own from that point forward.

http://lokad.github.io/lokad-cqrs/

This CQRS sample project, sort of violates that idea. The user aggregate takes a user uniqueness service and a password generator. I don't think I'll follow this practice, myself. I think I'm more comfortable with the command handler/context acting as a coordinator between domain services and aggregates.

Ben Kloosterman

unread,
Mar 20, 2017, 7:52:51 AM3/20/17
to ddd...@googlegroups.com

Im pretty much on the No camp but what happens when you have a lot of logic depending on the result  eg If it fails and the current state is blah ... Going to the readmodel outside you can do ( and is better architecturally in terms of coupling) but consider other cases eg

Add new state to the read model 
Eventual Consistency issues especially if this needs to run real fast with fast changing state.

Ben
Reply all
Reply to author
Forward
0 new messages