Background
Suppose I am tasked with building a system in the domain of notification sending using Domain Driven Design (DDD). One of the key requirements of this system is that it needs to support various "types" of notifications, such as SMS, email, etc.
After several iterations on developing the domain model, I continue to land on having a Notification base class as an entity, with subclasses SMSNotification, EmailNotification, etc. as child classes (each being an entity as well).
Notification
public abstract class Notification extends Entity<UUID> {
//...fields...
public abstract void send();
}
SMSNotification
public class SMSNotification extends Notification {
public void send(){
//logic for sending the SMS notification using an infrastructure service.
}
}
EmailNotification
public class EmailNotification extends Notification {
public void send(){
//logic for sending the email notification using an infrastructure service.
}
}
Problem(s)
- With this current design approach, each subclass of
Notificationis interacting with an infrastructure service, where the infrastructure is tasked with interfacing with some external system.
Eric Evans dedicates a little page space about this on page 107 in his book Domain-Driven Design when introducing the concept of domain services:
..., in most development systems, it is awkward to make a direct interface between a domain object and external resources. We can dress up such external services with a facade that takes inputs in terms of the model, ... but whatever intermediaries we may have, and even though they don't belong to us, those services are carrying out the domain responsibility...
- If instead, I procure a
SendNotificationServicein my domain model using Evans' advice instead of having asendmethod on each subclass ofNotification, I am not sure how I can avoid the need for knowing what type of notification was provided, so that the appropriate infrastructure action can be taken:
SendNotificationService (Domain Service)
public class SendNotificationService {
public void send(Notification notification){
//if notification is an SMS notification...
// utilize infrastructure services for SMS sending.
//if notification is an email notification...
// utilize infrastructure services for email sending.
//
//(╯°□°)╯︵ ┻━┻)
}
}
What am I missing here?
- Object oriented design principles are pushing me in favor of having the model first suggested, with the
Notification,SMSNotification, andEmailNotificationclasses. Implementing thesendmethod on each subclass ofNotificationmakes sense, as all notifications need to be sent (justifies its placement inNotification) and each "type" or subclass ofNotificationwill have specialized behavior in how the notification is sent (justifies makingsendabstract inNotification). This approach also honors Open/Closed Principle (OCP), since theNotificationclass will be closed to modification, and as new notification types are supported, a new subclass ofNotificationcan be created to extend functionality. Regardless, there seems to be consensus on not having entities interface with external services, as well as not having subclasses of entities at all in DDD. - If the behavior of sending notifications is removed from
Notification, then where it is placed must be aware of the "type" of notification, and act accordingly, which I can only conceptualize as chain ofif...else...statements, which directly contradicts OCP.
DocumentTemplatehas some state but most behaviors are technical. I've wondered if I should do something likedocTemplate.generate(service),service.generate(docTemplate)or even having adocTemplatehold a reference to aTemplateFileinterface implemented in the infrastructure where there's one for every kind of template (direct service reference)? If I'm not modeling a domain model at all, should I just use plain result sets and all logic into services? Can't think without domain models anymore... - plalxDocumentGeneratora "service"? If the template management part is very simple, couldn't you model it as CRUD and implement the technical generation part as plain old non-DDD objects? If the template part is complex, why not two BC's - one for templates and one for generation? - guillaume31