Create a Notification with the Actor as the Application

With a Notification model, how would you model the Sender/Actor relationship to allow for system notifications, where the application is the sender? i.e “Your export completed”, “Your trial is about to end”, “Welcome to the application”. A good frame of reference is the notification menu in the top left corner of discuss.rubyonrails.org

Here is an explanation for some of the fields for the above model:

recipient_id: Represents the user in your application which will receive the notification.

actor_id: Represents the user in your application which triggered the notification.

read_at: The time when the notification was read. A value of nil is used for unread notifications.

notifiable_id: The object that represents this notification (a post, comment, etc). This will be a polymorphic association.

notifiable_type: Type of the notifiable object. This is usually represented by a humanized (and optionally, internationalized) string form of the object’s class.

My first thought was to make the Actor(able) polymorphic, but that wouldn’t allow for a DEFAULT_APPLICATION that references the application, because it’s not in the database and therefore not referable. Extending on the idea of making the Actor polymorphic, the actor could be the @export and current_user.subscription, respectively. A third option would be to model the Notification to allow for a nil Actor and leave the actor nil.

# Your export completed. Download your export.
# Your trial is ending soon. Update your payment information.
Notification.create(recipient: current_user, actor: ?, action: "completed", notifiable: @export)
Notification.create(recipient: current_user, actor: ?, action: "ending", notifiable: current_user.subscription)

# Polymorphic Actor
Notification.create(recipient: current_user, actor: @export, action: "completed", notifiable: @export)
Notification.create(recipient: current_user, actor: current_user.subscription, action: "ending", notifiable: current_user.subscription)

# nil
Notification.create(recipient: current_user, actor: nil, action: "completed", notifiable: @export)
Notification.create(recipient: current_user, actor: nil, action: "ending", notifiable: current_user.subscription)

Still curious if anyone has insight around how they would approach this.

I also bumped into this question, with one extra concern: what about application notifications that apply to many or even all users (e.g. feature announcements)? It seems impractical to replicate the message for every single user every time such a message needs to be sent, knowing that potentially many users may be inactive and will not read it. I would rather only create DB rows for users only when required, i.e. when read_at needs to be set. At the same time it would be great to handle these notifications the same way as notifications targeting a single or a few users.

Think this is a question of how you define your domain.

Sample definition: A message always has a sender, content and a recipient. A notification always has a recipient and content. Notifications are triggered upon events, and should contain as much data as needed to present the event (somebody sent you a parcel, phone battery running low).

My concrete implementation would then follow my definition of the domain. It could be as simple as recording the event as plain text when generating the notification:

Notification.new(recipient, content: "#{event.user} did something")

If I need to generate a link as part of presenting the notification, I’d store the necessary resource IDs as metadata

Notification.new(recipient, event: :message_received, metadata: { message_id: event.message.id, sender_id: event.sender.id })

This would naturally cover other types of events:

Notification.new(recipient, event: :subscription_expired, metadata: { subscription_id: event.subscription.id })