1

I am employing DDD (including domain events) and CQRS (without Event Sourcing) in the design of a social network website.

I have aggregate roots like User, FriendRequest, Friendship. I also have domain events like UserAddressChanged, FriendRequestAccepted. Some of these events needs to be notified to concerning users. So I am thinking of having a Notification class, something like:

public enum NotificationReason
{
    IncomingFriendRequest = 1,

    OutgoingFriendRequestAccepted = 2,

    // and many more ...
}

public class Notification
{
    public long Id { get; set; }
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string AvatarUrl { get; set; }
    public DateTime Timestamp { get; set; }
    public NotificationReason Reason { get; set; }
    public bool Read { get; set; } //if user has read this notification.
}

But should I model the Notification class as an aggregate root? If yes, when the User aggregate changes address and it raises a UserAddressChanged domain event, in the corresponding event handler, a new aggregate Notification will be created and subsequently saved via a NotificationRepository. But creating new aggregates in event handlers sounds fishy to me.

Meanwhile, I also feel it is too heavy-weight for a simple class like Notification. I cannot decide if notification is a domain concern or infrastructural concern.

foresightyj
  • 2,006
  • 2
  • 26
  • 40
  • 3
    I believe that Notification is a domain concept in your case. However, you may have a notification bounded context for that and use transaction scripts rather than a domain model there. Querying notifications would just be a matter of querying the event stream and transforming entries into NotificationDTOs. Marking a notification as read would just be calling `notificationAppService.markNotificationAsRead(notificationId)` and you wouldn't have any aggregates. – plalx Feb 02 '16 at 20:07
  • @plalx I am adopting your suggestion to have a separate Bounded Context for Notifications. I integrate this BC to my original BC using a service bus. In the Notification BC, I have a process that listens to messages and saves them to a database. I also expose a restful api so the client can directly query from this BC.Thanks :-) – foresightyj Feb 23 '16 at 02:31

2 Answers2

1

From your description, it sounds like 'notifications' are the implementation of domain event emissions (and subscriptions), which suggests that while each kind of notification may be worthy of a domain object (a value object, since they're immutable), the transmission and reception of domain events is more likely an infrastructure concern than an aggregate+repository

Steven A. Lowe
  • 60,273
  • 18
  • 132
  • 202
  • Indeed they are very similar. But notifications are more than domain events. I need to keep track of whether a notification is read or not. Domain events are immutable objects. I cannot mark a domain event as read. – foresightyj Feb 17 '16 at 05:31
  • 1
    NotificationRead is another domain event :) – Steven A. Lowe Feb 17 '16 at 19:46
  • I totally understand what you are suggesting. But I still need a persistent model for this concept of `FriendRequestAccepted` with a **bit** indicating if it is read. When a `NotificationRead` event comes, I will set that bit. I will need something called **event storage** and a module to manage them. But from my understanding of DDD, only repositories have persistent concerns. Everything touches the database must go through a repository. – foresightyj Feb 23 '16 at 02:39
  • @foresightyj: think of your event storage module as a repository, and ask yourself if you might be wandering into CQRS/ES territory - http://martinfowler.com/bliki/CQRS.html – Steven A. Lowe Feb 23 '16 at 03:58
  • I know a little bit about ES, mainly from watching (Greg Young and Udi Dahan's youtube videos) and can appreciate its merits. But it is a big change in the mindset so I am still not very confident to apply ES to my current project. Currently I still feel comfortable to stay with the traditional DDD, with a bit flavor of domain events. I currently only have one repository per aggregate root. Allowing domain events to have a repository also seems a bit weird to me. – foresightyj Feb 23 '16 at 07:59
  • 1
    @foresightyj: difficult to offer sage counsel in a vacuum :) - try the simplest thing that could possibly work (for example, creating a notification-read record when a notification-read event is received, if that's the way it currently works) and let the code/environment guide the next steps. Good luck! – Steven A. Lowe Feb 23 '16 at 18:55
1

Every object in your domain should be an entity or a value object. Every object in your domain should exist within a single aggregate boundary.

Not every object in your application is an object in your domain.

Meanwhile, I also feel it is too heavy-weight for a simple class like Notification. I cannot decide if notification is a domain concern or infrastructural concern.

I'm inclined to agree; CRUD interface (are you really supporting modifying all of those properties? even id?), lack of methods, lack of business invariants. You said these are being created by an event handler -- does that mean most of these fields are just copies of the original event?

Notifications look more like an application concern than a domain concern. Push harder on the domain experts and the ubiquitous language to explore whether notifications are really part of the domain.

But creating new aggregates in event handlers sounds fishy to me.

Err... yes, but also no. You wouldn't expect an event handler to create a new aggregate, no. But it is completely reasonable that an event handler would schedule a command to create an aggregate.

This technique is commonly used for long running processes (search keywords: process managers, sagas). Event handlers observe messages, and pass them to a process, which updates its own state and runs commands on aggregates, round and round it goes.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • My team just started adopting DDD and I am worried not all are ready to accept the concept of a saga. What I am doing right now is write Command part of CQRS in the conventional way. Application Services and aggregate roots only publish domain events (synchronously in the same process). So there is no command and sagas. – foresightyj Feb 23 '16 at 02:23
  • I understand that if I create new aggregates in event handlers. That will make it similar to a saga. What I am planning to do is to create a separate Notification Bounded Context and let it manage all notifications in the system. In event handlers, I will publish **external** events (classes totally different from domain events) into a service bus. In the Notification BC, I will have a background process saving these events to database, and meanwhile expose a restful API so client can directly query for notifications from it. – foresightyj Feb 23 '16 at 02:29