> Assumes that the code need to generate additional actions. The example we keep getting back to is sending email :-)
> How would this look like? Would the service know about this? Or is this handled in the entity?
The logic that says that now is the time to do X, that would be in the domain. The actual doing of X probably should be external to the domain.
On email…
Before getting into the DDD part of the answer, we need to understand failure scenarios.
Let’s say that when an order is received we need to send an email to the user – a common requirement.
Now, let’s consider what happens when the SMTP server is down – it happens.
If we were to try to send the email as a part of the code that performs the transaction of handling the order, we may timeout effectively loosing the order – not good.
Therefore, the sending of the email should be performed asynchronously with respect to the receipt of the order, that is, we can successfully receive the order and later, try (and try again) to send an email and be consistent with respect to the business requirements.
Back to the domain:
The domain can raise an event stating OrderAccepted – as I demonstrated in this blog post:
http://www.udidahan.com/2008/02/29/how-to-create-fully-encapsulated-domain-models/
In our service layer we would have an object responsible for the rule “when an order is accepted, need to send an email” that would subscribe to that event. When the event fires from the domain model, our SendEmailMessageWhenOrderAccepted object would use some kind of “bus” to send a EmailUserMessage with the relevant information.
Another process/server in the system would be responsible for handling EmailUserMessages and using the SMTP server to do so.
I have the code up that does the messaging and sending email stuff here:
http://www.udidahan.com/2008/06/06/web-scalability-slides-and-code/
I’ll also be putting up the thread-safe “events infrastructure” on my blog soon, so keep an eye out for that if you do go this route.
Does that help?
--
Udi Dahan - The Software Simplist
No virus
found in this incoming message.
Checked by AVG - http://www.avg.com
Version: 8.0.138 / Virus Database: 270.6.3/1610 - Release Date: 13/08/2008
16:14
That would introduce a dependency on the concept of messaging into the domain model. I would assert that the same domain model could be used with a regular web services service layer and one that uses messaging. In my opinion, separation of concerns is first and foremost about concepts and only after that about actual code dependencies.
The farthest I would go with this would be to have the domain raise an event NeedToEmailUser. However, the data in that event would need to be generic enough that it wouldn’t matter if we were doing HTML, RTF, or plain text emails – same goes for text messaging, or any other external communication.
Does that make sense?
Who said the calling code is the one that would necessarily subscribe to the event?
You could have one service layer class which handles messages, dispatching calls to the domain model. Another class, not necessarily even in the service layer subscribes to the NeedToSendEmail event and handles it.
> Knowing that a piece of code would raise an event, as an example, is a problem.
No more Button1_Clicked?
I might tighten up your assertion Ken:
The need to notify a user is a domain concern – sending an email is one implementation of that, which may depend on the users preferred contact settings.
So, while the domain should say WHEN a user needs to be notified (events are a good way to do that), why should it depend on the way that occurs (IEmailService)?
I find that the greatest value comes from having an entirely independent domain model – testability, maintainability, versionability, etc.
--
Udi Dahan - The Software Simplist
Then what happens when you get something that works on accounts and products? Another domain service? And then something that works on all three? Another domain service which “orchestrates” the others, or bypasses them? Is this not the path to procedural hell?
If you can’t tell, I’ve never liked those domain services J
--
Udi Dahan - The Software Simplist
Sample code – a singleton class which is brought up at service startup and subscribes to DomainEvents.NeedToNotifyUser. That’s it. No magic.
“A bunch of classes” – why didn’t you say so in the first place?
;-)
If “create account” appears on more than one class, that’s a violation of the DRY principle. If one service calls another, that’s the first step towards procedural hell. For larger systems, this can be the kiss of death. Smaller systems may not feel the pain as much.
> if we're denying our entities from our general development practices (like DI), then they're not POCOs anymore right?
How is it that if no dependencies need to be injected that the entities aren’t POCO anymore?
Actually more like this:
public static class DomainEvents
{
public static DomainEvent<NotificationArgs> NeedToNotifyUser = new DomainEvent<NotificationArgs>();
}
public class HandleNeedToNotifyUser
{
public HandleNeedToNotifyUser(IBus bus)
{
DomainEvents.NeedToNotifyUser.Subscribe(
bus.Send(new NotifyUserMessage( /*data from args*/)); //or SendEmailMessage if you like
);
That would create a dependency from the core business logic of the system to its external schema and the API for working with that schema. I prefer the flexibility of being able to independently vary each of them.
API = bus – the way we send messages.
Where’s publish?
Careful – keep going down this path and you’ll have collapsed your service layer and domain model together. While that may be OK for DDDD within a service boundary, its applicability is limited.
Handlers are the service layer. The question is, if your domain model can send messages, will it also reply?
> Can you think of a scenario when a reply is needed?
In my design, handlers reply. That’s because outside the service layer, nobody does messaging.
There are many cases where messaging interactions are still request/response – require a reply.
Re “contacting a separate system”, since the service layer can’t know which object in the domain model initiated the call, how would it know to route the response to that object?
I’ll let you in on a secret…
Most of this is moot.
All of this is handled by sagas.