diff --git a/.gitignore b/.gitignore index e05b688..d8748a3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,9 @@ coverage/ dist/ build/ .next/ -out/ +# Only ignore Next.js output directory, not all 'out' folders +/.next/out/ +/apps/frontend/out/ # Environment variables .env diff --git a/apps/backend/src/domain/ports/out/audit-log.repository.ts b/apps/backend/src/domain/ports/out/audit-log.repository.ts new file mode 100644 index 0000000..4212346 --- /dev/null +++ b/apps/backend/src/domain/ports/out/audit-log.repository.ts @@ -0,0 +1,59 @@ +/** + * Audit Log Repository Port + * + * Defines the interface for Audit Log persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { AuditLog } from '../../entities/audit-log.entity'; + +export const AUDIT_LOG_REPOSITORY = 'AuditLogRepository'; + +export interface AuditLogFilters { + userId?: string; + organizationId?: string; + action?: string[]; + resourceType?: string; + resourceId?: string; + dateFrom?: Date; + dateTo?: Date; + limit?: number; + offset?: number; +} + +export interface AuditLogRepository { + /** + * Save an audit log entry + */ + save(auditLog: AuditLog): Promise; + + /** + * Find audit log by ID + */ + findById(id: string): Promise; + + /** + * Find audit logs by filters + */ + findByFilters(filters: AuditLogFilters): Promise; + + /** + * Count audit logs matching filters + */ + count(filters: AuditLogFilters): Promise; + + /** + * Find audit logs for a specific resource + */ + findByResource(resourceType: string, resourceId: string): Promise; + + /** + * Find recent audit logs for an organization + */ + findRecentByOrganization(organizationId: string, limit: number): Promise; + + /** + * Find audit logs by user + */ + findByUser(userId: string, limit: number): Promise; +} diff --git a/apps/backend/src/domain/ports/out/booking.repository.ts b/apps/backend/src/domain/ports/out/booking.repository.ts new file mode 100644 index 0000000..6376928 --- /dev/null +++ b/apps/backend/src/domain/ports/out/booking.repository.ts @@ -0,0 +1,49 @@ +/** + * Booking Repository Port + * + * Defines the interface for Booking persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Booking } from '../../entities/booking.entity'; +import { BookingNumber } from '../../value-objects/booking-number.vo'; +import { BookingStatus } from '../../value-objects/booking-status.vo'; + +export const BOOKING_REPOSITORY = 'BookingRepository'; + +export interface BookingRepository { + /** + * Save a booking entity + */ + save(booking: Booking): Promise; + + /** + * Find booking by ID + */ + findById(id: string): Promise; + + /** + * Find booking by booking number + */ + findByBookingNumber(bookingNumber: BookingNumber): Promise; + + /** + * Find all bookings for a specific user + */ + findByUser(userId: string): Promise; + + /** + * Find all bookings for an organization + */ + findByOrganization(organizationId: string): Promise; + + /** + * Find all bookings with a specific status + */ + findByStatus(status: BookingStatus): Promise; + + /** + * Delete booking by ID + */ + delete(id: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/cache.port.ts b/apps/backend/src/domain/ports/out/cache.port.ts new file mode 100644 index 0000000..ae07254 --- /dev/null +++ b/apps/backend/src/domain/ports/out/cache.port.ts @@ -0,0 +1,60 @@ +/** + * Cache Port + * + * Defines the interface for caching operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +export const CACHE_PORT = 'CachePort'; + +export interface CachePort { + /** + * Get a value from cache + * Returns null if key doesn't exist + */ + get(key: string): Promise; + + /** + * Set a value in cache + * @param key - Cache key + * @param value - Value to store + * @param ttlSeconds - Time to live in seconds (optional) + */ + set(key: string, value: T, ttlSeconds?: number): Promise; + + /** + * Delete a key from cache + */ + delete(key: string): Promise; + + /** + * Delete multiple keys from cache + */ + deleteMany(keys: string[]): Promise; + + /** + * Check if a key exists in cache + */ + exists(key: string): Promise; + + /** + * Get time to live for a key (in seconds) + * Returns -2 if key doesn't exist, -1 if key has no expiration + */ + ttl(key: string): Promise; + + /** + * Clear all cache entries + */ + clear(): Promise; + + /** + * Get cache statistics + */ + getStats(): Promise<{ + hits: number; + misses: number; + hitRate: number; + keyCount: number; + }>; +} diff --git a/apps/backend/src/domain/ports/out/carrier-connector.port.ts b/apps/backend/src/domain/ports/out/carrier-connector.port.ts new file mode 100644 index 0000000..3955c4b --- /dev/null +++ b/apps/backend/src/domain/ports/out/carrier-connector.port.ts @@ -0,0 +1,64 @@ +/** + * Carrier Connector Port + * + * Defines the interface for carrier API integrations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { RateQuote } from '../../entities/rate-quote.entity'; + +export const CARRIER_CONNECTOR_PORT = 'CarrierConnectorPort'; + +export interface CarrierRateSearchInput { + origin: string; + destination: string; + containerType: string; + departureDate: Date; + quantity?: number; + mode?: string; // 'FCL' or 'LCL' + weight?: number; // Weight in kg + volume?: number; // Volume in CBM + isHazmat?: boolean; // Hazardous materials flag + imoClass?: string; // IMO class for hazmat + hazardous?: boolean; + reefer?: boolean; + commodityType?: string; +} + +export interface CarrierAvailabilityInput { + origin: string; + destination: string; + containerType: string; + startDate: Date; + endDate: Date; + departureDate?: Date; // Specific departure date + quantity?: number; // Number of containers +} + +export interface CarrierConnectorPort { + /** + * Get the carrier name + */ + getCarrierName(): string; + + /** + * Get the carrier code + */ + getCarrierCode(): string; + + /** + * Search for shipping rates + */ + searchRates(input: CarrierRateSearchInput): Promise; + + /** + * Check container availability + * Returns the number of available containers + */ + checkAvailability(input: CarrierAvailabilityInput): Promise; + + /** + * Health check to verify carrier API is accessible + */ + healthCheck(): Promise; +} diff --git a/apps/backend/src/domain/ports/out/carrier.repository.ts b/apps/backend/src/domain/ports/out/carrier.repository.ts new file mode 100644 index 0000000..b6855c7 --- /dev/null +++ b/apps/backend/src/domain/ports/out/carrier.repository.ts @@ -0,0 +1,62 @@ +/** + * Carrier Repository Port + * + * Defines the interface for Carrier persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Carrier } from '../../entities/carrier.entity'; + +export const CARRIER_REPOSITORY = 'CarrierRepository'; + +export interface CarrierRepository { + /** + * Save a carrier entity + */ + save(carrier: Carrier): Promise; + + /** + * Save multiple carrier entities + */ + saveMany(carriers: Carrier[]): Promise; + + /** + * Find carrier by ID + */ + findById(id: string): Promise; + + /** + * Find carrier by carrier code + */ + findByCode(code: string): Promise; + + /** + * Find carrier by SCAC (Standard Carrier Alpha Code) + */ + findByScac(scac: string): Promise; + + /** + * Find all active carriers + */ + findAllActive(): Promise; + + /** + * Find all carriers that support API integration + */ + findWithApiSupport(): Promise; + + /** + * Find all carriers (including inactive) + */ + findAll(): Promise; + + /** + * Update a carrier entity + */ + update(carrier: Carrier): Promise; + + /** + * Delete carrier by ID + */ + deleteById(id: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/csv-booking.repository.ts b/apps/backend/src/domain/ports/out/csv-booking.repository.ts new file mode 100644 index 0000000..d5b7dff --- /dev/null +++ b/apps/backend/src/domain/ports/out/csv-booking.repository.ts @@ -0,0 +1,87 @@ +import { CsvBooking } from '../../entities/csv-booking.entity'; + +/** + * CSV Booking Repository Port (Output Port) + * + * Interface for CSV booking persistence operations. + * Implemented by infrastructure layer. + * + * This port abstracts database operations from the domain layer. + */ +export interface CsvBookingRepositoryPort { + /** + * Create a new booking + * @param booking - Booking to create + * @returns Created booking with generated ID + */ + create(booking: CsvBooking): Promise; + + /** + * Find booking by ID + * @param id - Booking ID + * @returns Booking if found, null otherwise + */ + findById(id: string): Promise; + + /** + * Find booking by confirmation token + * @param token - Confirmation token from email link + * @returns Booking if found, null otherwise + */ + findByToken(token: string): Promise; + + /** + * Find all bookings for a user + * @param userId - User ID + * @returns Array of bookings + */ + findByUserId(userId: string): Promise; + + /** + * Find all bookings for an organization + * @param organizationId - Organization ID + * @returns Array of bookings + */ + findByOrganizationId(organizationId: string): Promise; + + /** + * Find bookings by status + * @param status - Booking status + * @returns Array of bookings with matching status + */ + findByStatus(status: string): Promise; + + /** + * Find pending bookings that are about to expire + * @param daysUntilExpiration - Number of days until expiration (e.g., 2) + * @returns Array of bookings expiring soon + */ + findExpiringSoon(daysUntilExpiration: number): Promise; + + /** + * Update existing booking + * @param booking - Booking with updated data + * @returns Updated booking + */ + update(booking: CsvBooking): Promise; + + /** + * Delete booking + * @param id - Booking ID + */ + delete(id: string): Promise; + + /** + * Count bookings by status for a user + * @param userId - User ID + * @returns Object with counts per status + */ + countByStatusForUser(userId: string): Promise>; + + /** + * Count bookings by status for an organization + * @param organizationId - Organization ID + * @returns Object with counts per status + */ + countByStatusForOrganization(organizationId: string): Promise>; +} diff --git a/apps/backend/src/domain/ports/out/csv-rate-loader.port.ts b/apps/backend/src/domain/ports/out/csv-rate-loader.port.ts new file mode 100644 index 0000000..e9a691a --- /dev/null +++ b/apps/backend/src/domain/ports/out/csv-rate-loader.port.ts @@ -0,0 +1,44 @@ +import { CsvRate } from '../../entities/csv-rate.entity'; + +/** + * CSV Rate Loader Port (Output Port) + * + * Interface for loading rates from CSV files. + * Implemented by infrastructure layer. + * + * This port abstracts CSV file operations from the domain layer. + */ +export interface CsvRateLoaderPort { + /** + * Load all rates from a CSV file + * @param filePath - Absolute or relative path to CSV file + * @param companyEmail - Email address for the company (stored in config metadata) + * @returns Array of CSV rates + * @throws Error if file cannot be read or parsed + */ + loadRatesFromCsv(filePath: string, companyEmail: string): Promise; + + /** + * Load rates for a specific company + * @param companyName - Name of the carrier company + * @returns Array of CSV rates for the company + */ + loadRatesByCompany(companyName: string): Promise; + + /** + * Validate CSV file structure without fully parsing + * @param filePath - Path to CSV file + * @returns Validation result with errors if any + */ + validateCsvFile(filePath: string): Promise<{ + valid: boolean; + errors: string[]; + rowCount?: number; + }>; + + /** + * Get list of all available CSV rate files + * @returns Array of file paths + */ + getAvailableCsvFiles(): Promise; +} diff --git a/apps/backend/src/domain/ports/out/email.port.ts b/apps/backend/src/domain/ports/out/email.port.ts new file mode 100644 index 0000000..f7885e1 --- /dev/null +++ b/apps/backend/src/domain/ports/out/email.port.ts @@ -0,0 +1,92 @@ +/** + * Email Port + * + * Defines the interface for email sending operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +export const EMAIL_PORT = 'EmailPort'; + +export interface EmailAttachment { + filename: string; + content: Buffer | string; + contentType?: string; +} + +export interface EmailOptions { + to: string | string[]; + cc?: string | string[]; + bcc?: string | string[]; + replyTo?: string; + subject: string; + html?: string; + text?: string; + attachments?: EmailAttachment[]; +} + +export interface EmailPort { + /** + * Send an email + */ + send(options: EmailOptions): Promise; + + /** + * Send booking confirmation email + */ + sendBookingConfirmation( + email: string, + bookingNumber: string, + bookingDetails: any, + pdfAttachment?: Buffer + ): Promise; + + /** + * Send email verification email + */ + sendVerificationEmail(email: string, token: string): Promise; + + /** + * Send password reset email + */ + sendPasswordResetEmail(email: string, token: string): Promise; + + /** + * Send welcome email + */ + sendWelcomeEmail(email: string, firstName: string): Promise; + + /** + * Send user invitation email + */ + sendUserInvitation( + email: string, + organizationName: string, + inviterName: string, + tempPassword: string + ): Promise; + + /** + * Send CSV booking request email to carrier + */ + sendCsvBookingRequest( + carrierEmail: string, + bookingDetails: { + bookingId: string; + origin: string; + destination: string; + volumeCBM: number; + weightKG: number; + palletCount: number; + priceUSD: number; + priceEUR: number; + primaryCurrency: string; + transitDays: number; + containerType: string; + documents: Array<{ + type: string; + fileName: string; + }>; + confirmationToken: string; + } + ): Promise; +} diff --git a/apps/backend/src/domain/ports/out/index.ts b/apps/backend/src/domain/ports/out/index.ts new file mode 100644 index 0000000..2f50b35 --- /dev/null +++ b/apps/backend/src/domain/ports/out/index.ts @@ -0,0 +1,25 @@ +/** + * Domain Ports (Output) - Barrel Export + * + * Exports all output port interfaces and tokens for easy importing. + */ + +// Repository Ports +export * from './user.repository'; +export * from './booking.repository'; +export * from './rate-quote.repository'; +export * from './organization.repository'; +export * from './port.repository'; +export * from './carrier.repository'; +export * from './notification.repository'; +export * from './audit-log.repository'; +export * from './webhook.repository'; +export * from './csv-booking.repository'; + +// Infrastructure Ports +export * from './cache.port'; +export * from './email.port'; +export * from './pdf.port'; +export * from './storage.port'; +export * from './carrier-connector.port'; +export * from './csv-rate-loader.port'; diff --git a/apps/backend/src/domain/ports/out/notification.repository.ts b/apps/backend/src/domain/ports/out/notification.repository.ts new file mode 100644 index 0000000..24ad7fe --- /dev/null +++ b/apps/backend/src/domain/ports/out/notification.repository.ts @@ -0,0 +1,80 @@ +/** + * Notification Repository Port + * + * Defines the interface for Notification persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Notification } from '../../entities/notification.entity'; + +export const NOTIFICATION_REPOSITORY = 'NotificationRepository'; + +export interface NotificationFilters { + userId?: string; + organizationId?: string; + type?: string[]; + read?: boolean; + priority?: string[]; + startDate?: Date; + endDate?: Date; + offset?: number; + limit?: number; +} + +export interface NotificationRepository { + /** + * Save a notification entity + */ + save(notification: Notification): Promise; + + /** + * Find notification by ID + */ + findById(id: string): Promise; + + /** + * Find notifications by filters + */ + findByFilters(filters: NotificationFilters): Promise; + + /** + * Count notifications matching filters + */ + count(filters: NotificationFilters): Promise; + + /** + * Find unread notifications for a user + */ + findUnreadByUser(userId: string, limit?: number): Promise; + + /** + * Count unread notifications for a user + */ + countUnreadByUser(userId: string): Promise; + + /** + * Find recent notifications for a user + */ + findRecentByUser(userId: string, limit?: number): Promise; + + /** + * Mark a notification as read + */ + markAsRead(id: string): Promise; + + /** + * Mark all notifications as read for a user + */ + markAllAsReadForUser(userId: string): Promise; + + /** + * Delete a notification + */ + delete(id: string): Promise; + + /** + * Delete old read notifications + * Returns the number of deleted records + */ + deleteOldReadNotifications(olderThanDays: number): Promise; +} diff --git a/apps/backend/src/domain/ports/out/organization.repository.ts b/apps/backend/src/domain/ports/out/organization.repository.ts new file mode 100644 index 0000000..b7a48b4 --- /dev/null +++ b/apps/backend/src/domain/ports/out/organization.repository.ts @@ -0,0 +1,62 @@ +/** + * Organization Repository Port + * + * Defines the interface for Organization persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Organization } from '../../entities/organization.entity'; + +export const ORGANIZATION_REPOSITORY = 'OrganizationRepository'; + +export interface OrganizationRepository { + /** + * Save an organization entity + */ + save(organization: Organization): Promise; + + /** + * Find organization by ID + */ + findById(id: string): Promise; + + /** + * Find organization by name + */ + findByName(name: string): Promise; + + /** + * Find organization by SCAC (Standard Carrier Alpha Code) + */ + findBySCAC(scac: string): Promise; + + /** + * Find all organizations + */ + findAll(): Promise; + + /** + * Find all active organizations + */ + findAllActive(): Promise; + + /** + * Find organizations by type (e.g., 'FREIGHT_FORWARDER', 'CARRIER') + */ + findByType(type: string): Promise; + + /** + * Update an organization entity + */ + update(organization: Organization): Promise; + + /** + * Delete organization by ID + */ + deleteById(id: string): Promise; + + /** + * Count total organizations + */ + count(): Promise; +} diff --git a/apps/backend/src/domain/ports/out/pdf.port.ts b/apps/backend/src/domain/ports/out/pdf.port.ts new file mode 100644 index 0000000..48ef315 --- /dev/null +++ b/apps/backend/src/domain/ports/out/pdf.port.ts @@ -0,0 +1,66 @@ +/** + * PDF Port + * + * Defines the interface for PDF generation operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +export const PDF_PORT = 'PdfPort'; + +export interface BookingPdfData { + bookingNumber: string; + bookingDate: Date; + origin: { + code: string; + name: string; + }; + destination: { + code: string; + name: string; + }; + carrier: { + name: string; + logo?: string; + }; + etd: Date; + eta: Date; + transitDays: number; + shipper: { + name: string; + address: string; + contact: string; + email: string; + phone: string; + }; + consignee: { + name: string; + address: string; + contact: string; + email: string; + phone: string; + }; + containers: Array<{ + type: string; + quantity: number; + containerNumber?: string; + sealNumber?: string; + }>; + cargoDescription: string; + specialInstructions?: string; + price: { + amount: number; + currency: string; + }; +} + +export interface PdfPort { + /** + * Generate booking confirmation PDF + */ + generateBookingConfirmation(data: BookingPdfData): Promise; + + /** + * Generate rate quote comparison PDF + */ + generateRateQuoteComparison(quotes: any[]): Promise; +} diff --git a/apps/backend/src/domain/ports/out/port.repository.ts b/apps/backend/src/domain/ports/out/port.repository.ts new file mode 100644 index 0000000..40ba3f5 --- /dev/null +++ b/apps/backend/src/domain/ports/out/port.repository.ts @@ -0,0 +1,58 @@ +/** + * Port Repository Port + * + * Defines the interface for Port (maritime port) persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Port } from '../../entities/port.entity'; + +export const PORT_REPOSITORY = 'PortRepository'; + +export interface PortRepository { + /** + * Save a port entity + */ + save(port: Port): Promise; + + /** + * Save multiple port entities + */ + saveMany(ports: Port[]): Promise; + + /** + * Find port by UN LOCODE + */ + findByCode(code: string): Promise; + + /** + * Find multiple ports by codes + */ + findByCodes(codes: string[]): Promise; + + /** + * Search ports by query string (name, city, or code) + * with optional country filter and limit + */ + search(query: string, limit?: number, countryFilter?: string): Promise; + + /** + * Find all active ports + */ + findAllActive(): Promise; + + /** + * Find all ports in a specific country + */ + findByCountry(countryCode: string): Promise; + + /** + * Count total ports + */ + count(): Promise; + + /** + * Delete port by code + */ + deleteByCode(code: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/rate-quote.repository.ts b/apps/backend/src/domain/ports/out/rate-quote.repository.ts new file mode 100644 index 0000000..d53f3a1 --- /dev/null +++ b/apps/backend/src/domain/ports/out/rate-quote.repository.ts @@ -0,0 +1,53 @@ +/** + * RateQuote Repository Port + * + * Defines the interface for RateQuote persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { RateQuote } from '../../entities/rate-quote.entity'; + +export const RATE_QUOTE_REPOSITORY = 'RateQuoteRepository'; + +export interface RateQuoteRepository { + /** + * Save a rate quote entity + */ + save(rateQuote: RateQuote): Promise; + + /** + * Save multiple rate quote entities + */ + saveMany(rateQuotes: RateQuote[]): Promise; + + /** + * Find rate quote by ID + */ + findById(id: string): Promise; + + /** + * Find rate quotes by search criteria + */ + findBySearchCriteria(criteria: { + origin: string; + destination: string; + containerType: string; + departureDate: Date; + }): Promise; + + /** + * Find all rate quotes for a specific carrier + */ + findByCarrier(carrierId: string): Promise; + + /** + * Delete expired rate quotes + * Returns the number of deleted records + */ + deleteExpired(): Promise; + + /** + * Delete rate quote by ID + */ + deleteById(id: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/storage.port.ts b/apps/backend/src/domain/ports/out/storage.port.ts new file mode 100644 index 0000000..75d13a0 --- /dev/null +++ b/apps/backend/src/domain/ports/out/storage.port.ts @@ -0,0 +1,69 @@ +/** + * Storage Port + * + * Defines the interface for object storage operations (S3, MinIO, etc.). + * This is a secondary port (output port) in hexagonal architecture. + */ + +export const STORAGE_PORT = 'StoragePort'; + +export interface UploadOptions { + bucket: string; + key: string; + body: Buffer | string; + contentType?: string; + metadata?: Record; + acl?: string; +} + +export interface DownloadOptions { + bucket: string; + key: string; +} + +export interface DeleteOptions { + bucket: string; + key: string; +} + +export interface StorageObject { + key: string; + url: string; + size: number; + contentType?: string; + lastModified?: Date; +} + +export interface StoragePort { + /** + * Upload a file to storage + */ + upload(options: UploadOptions): Promise; + + /** + * Download a file from storage + */ + download(options: DownloadOptions): Promise; + + /** + * Delete a file from storage + */ + delete(options: DeleteOptions): Promise; + + /** + * Get a signed URL for temporary access + * @param options - Download options + * @param expiresIn - URL expiration in seconds (default: 3600) + */ + getSignedUrl(options: DownloadOptions, expiresIn?: number): Promise; + + /** + * Check if a file exists in storage + */ + exists(options: DownloadOptions): Promise; + + /** + * List objects in a bucket + */ + list(bucket: string, prefix?: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/user.repository.ts b/apps/backend/src/domain/ports/out/user.repository.ts new file mode 100644 index 0000000..15499e8 --- /dev/null +++ b/apps/backend/src/domain/ports/out/user.repository.ts @@ -0,0 +1,62 @@ +/** + * User Repository Port + * + * Defines the interface for User persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { User } from '../../entities/user.entity'; + +export const USER_REPOSITORY = 'UserRepository'; + +export interface UserRepository { + /** + * Save a user entity + */ + save(user: User): Promise; + + /** + * Find user by ID + */ + findById(id: string): Promise; + + /** + * Find user by email address + */ + findByEmail(email: string): Promise; + + /** + * Find all users belonging to an organization + */ + findByOrganization(organizationId: string): Promise; + + /** + * Find all users with a specific role + */ + findByRole(role: string): Promise; + + /** + * Find all active users + */ + findAllActive(): Promise; + + /** + * Update a user entity + */ + update(user: User): Promise; + + /** + * Delete user by ID + */ + deleteById(id: string): Promise; + + /** + * Count users in an organization + */ + countByOrganization(organizationId: string): Promise; + + /** + * Check if email exists + */ + emailExists(email: string): Promise; +} diff --git a/apps/backend/src/domain/ports/out/webhook.repository.ts b/apps/backend/src/domain/ports/out/webhook.repository.ts new file mode 100644 index 0000000..38097ce --- /dev/null +++ b/apps/backend/src/domain/ports/out/webhook.repository.ts @@ -0,0 +1,53 @@ +/** + * Webhook Repository Port + * + * Defines the interface for Webhook persistence operations. + * This is a secondary port (output port) in hexagonal architecture. + */ + +import { Webhook, WebhookEvent } from '../../entities/webhook.entity'; + +export const WEBHOOK_REPOSITORY = 'WebhookRepository'; + +export interface WebhookFilters { + organizationId?: string; + status?: string[]; + event?: WebhookEvent; +} + +export interface WebhookRepository { + /** + * Save a webhook entity + */ + save(webhook: Webhook): Promise; + + /** + * Find webhook by ID + */ + findById(id: string): Promise; + + /** + * Find all webhooks for an organization + */ + findByOrganization(organizationId: string): Promise; + + /** + * Find active webhooks by event and organization + */ + findActiveByEvent(event: WebhookEvent, organizationId: string): Promise; + + /** + * Find webhooks by filters + */ + findByFilters(filters: WebhookFilters): Promise; + + /** + * Delete a webhook + */ + delete(id: string): Promise; + + /** + * Count webhooks for an organization + */ + countByOrganization(organizationId: string): Promise; +}