Notification orchestration across multiple channels
| Tier | domain |
| Role | unclassified (baselined) |
| Path | crates/domain/notifications |
| Edition | 2021 |
| Targets | domain_notifications |
| Public items | 0 across 0 modules |
| Tests | 50 |
What it is for
domain-notifications: Notification orchestration across multiple channels
Overview
This crate provides a notification system that delivers messages about events through multiple channels. It handles notification creation, delivery tracking, user preferences, and templates.
Important: This is an orchestration crate, not an email crate. Actual delivery is handled by channel-specific adapters.
Layer
Domain - depends on: foundation-basemodels
Key Types
Notification- A notification to be deliveredNotificationTemplate- Template for consistent messagingNotificationPreference- User channel preferencesDeliveryAttempt- Record of a delivery attemptNotificationService- High-level business operationsDeliveryBackend- Trait for channel delivery adaptersInAppBackend- No-op backend for in-app notifications
Channels
- In-app (database + websocket)
- Email (adapter)
- SMS (adapter)
- Webhook
- Push notifications
Example
use domain_notifications::{
NotificationService, CreateNotificationRequest, NotificationChannel, EntityRef
};
let service = NotificationService::new(pool);
// Create a notification
let (notification, event) = service.create_notification(CreateNotificationRequest {
tenant_id,
recipient_party_id: user_id,
event_type: "comment.mentioned".to_string(),
title: "You were mentioned".to_string(),
body: "Someone mentioned you in a comment".to_string(),
channel: NotificationChannel::InApp,
entity_type: Some("Comment".to_string()),
entity_id: Some(comment_id),
data: None,
idempotency_key: Some(format!("mention-{}-{}", comment_id, user_id)),
}).await?;
// Check user preferences before delivery
let can_deliver = service.can_deliver(
user_id, tenant_id, NotificationChannel::Email, "comment.mentioned"
).await?;
// Mark as read
service.mark_read(notification.id).await?;
// Get unread count
let unread = service.count_unread(tenant_id, user_id).await?;
Capabilities
No public items.
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
No public modules: the crate root is its whole surface.
Public surface
No public items.
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
{DeliveryBackend,DeliveryResult,InAppBackend} | delivery::{DeliveryBackend,DeliveryResult,InAppBackend} |
Boundary
Reaches into foundation.
Shares tier domain with 41 other crates: domain-agreements, domain-ai-report, domain-billing, domain-catalog, domain-classify, domain-comments, domain-competitive-intel, domain-contact, … (41 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | domain |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/domain/notifications |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_domain["domain"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-basemodels` | foundation | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
chrono | ^0.4 | serde | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json | no | always |
thiserror | ^2 | — | no | always |
uuid | ^1 | v4, serde | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tokio | ^1 | full, test-util | no | always |
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_application_notifications["application-notifications"] -->|uses| SELF n_platform_privacy_scan_api["platform-privacy-scan-api"] -->|uses| SELF SELF["domain-notifications"] SELF -->|runtime| n_foundation_basemodels["foundation-basemodels"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | domain_notifications | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
2 workspace crates depend on this one: application-notifications, platform-privacy-scan-api.
Verification
| Kind | Count |
|---|---|
| Unit tests | 50 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
What the tests establish, by name:
test_delivery_result_chained—src/delivery.rstest_delivery_result_success—src/delivery.rstest_delivery_result_with_external_id—src/delivery.rstest_delivery_result_with_response—src/delivery.rstest_in_app_backend_channel—src/delivery.rstest_in_app_backend_deliver—src/delivery.rstest_in_app_backend_is_available—src/delivery.rstest_channel_disabled—src/errors.rstest_delivery_failed—src/errors.rstest_duplicate_notification—src/errors.rstest_in_quiet_hours—src/errors.rstest_invalid_status_transition—src/errors.rstest_notification_not_found—src/errors.rstest_template_not_found—src/errors.rstest_validation_error—src/errors.rstest_delivery_attempted_event—src/events.rstest_event_serialization—src/events.rstest_notification_created_event—src/events.rstest_notification_delivered_event—src/events.rstest_notification_failed_event—src/events.rstest_notification_read_event—src/events.rstest_preferences_updated_event—src/events.rstest_all_channels—src/lib.rstest_delivery_attempt_failure—src/lib.rstest_delivery_attempt_skipped—src/lib.rstest_delivery_attempt_success—src/lib.rstest_notification_channel_parsing—src/lib.rstest_notification_events—src/lib.rstest_notification_idempotency—src/lib.rstest_notification_mark_read—src/lib.rs- _… 20 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 0 | 0 |
Public modules with a //! block | 0 | 0 |
Metrics
| Metric | Value |
|---|---|
| Rust source files | 7 |
| Source lines | 2856 |
| Code lines | 2111 |
| Public API items | 0 |
| Public modules | 0 |
| Tests | 50 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 8 |
| Workspace reverse dependencies | 2 |
pie showData
title Rust source composition
"Code" : 2111
"Blank or comment" : 745
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.