Table of Contents

Constructor MailOutbox

Namespace
MailFathom.Application.Mail.Delivery
Assembly
MailFathom.Application.dll

MailOutbox(IOutgoingEmailStore, IEmailContentStore, OptimisticConcurrencyRetryPolicy, MailOutboxSignal, IJobStore, IOutboxOperationStore, AccessAuthorization, OutgoingMailGovernor, TimeProvider)

Takes a message somebody authored and makes it durable before anything can try to send it.

public MailOutbox(IOutgoingEmailStore outgoingEmails, IEmailContentStore contentStore, OptimisticConcurrencyRetryPolicy retryPolicy, MailOutboxSignal signal, IJobStore jobs, IOutboxOperationStore outboxOperations, AccessAuthorization authorization, OutgoingMailGovernor governor, TimeProvider timeProvider)

Parameters

outgoingEmails IOutgoingEmailStore

Holds the durable record and its idempotency identity.

contentStore IEmailContentStore

Holds the composed MIME the record points at.

retryPolicy OptimisticConcurrencyRetryPolicy

Commits both writes together and resolves a lost race for the same identity.

signal MailOutboxSignal

Tells the delivery loop that this account has something to send.

jobs IJobStore

Carries the moment a held send becomes due, without a timer of this feature's own.

outboxOperations IOutboxOperationStore

Writes the withdrawal of a message that has not begun to leave, which is one transition however it was asked for.

authorization AccessAuthorization

Answers whether whoever reached this is admitted to ask for the send the request states it is.

governor OutgoingMailGovernor

Answers whether this deployment may send this message at all, whoever is asking.

timeProvider TimeProvider

Says whether a recorded send is due now or is being held for later.

Remarks

This is the one way into the outbox, and it exists because the two writes beneath it are one decision. A record whose message was never stored describes a send with nothing to transmit; a message stored under no record is bytes nothing will ever read. Both cross the same transaction here, so a crash between them leaves neither rather than half of a send.

Being the one way in is also what makes it the place the send grant is asked for a second time. A tool call was already refused at the transport if it could be, and the same question is put here with no transport in the picture, so nothing that reaches the outbox by another route reaches it ungoverned.

It is the place this deployment's own bounds on sending are asked for the same reason, and for one more: they are the operator's answer rather than the caller's, so they have to hold for work no caller requested. Whether sending is on for the account at all, who the deployment may write to, and how much may leave in a period are all decided before anything is written down, which is what makes a refusal cost nothing and leave nothing behind.

Enqueuing is idempotent by the identity the request carries. The same authored request arriving twice — a rule that ran again, a retried command, a client that resent a call — reads back the record the first one wrote and stores nothing further, so it produces one delivery. What decides that is the unique constraint under the store rather than any check here: two callers arriving together both reach the database, and the loser's retry finds the winner's row.

Nothing is sent by this. The record it leaves is at Recorded with every recipient unanswered, which is the state a delivery attempt reads and continues from.

What it does do, once the record is durable, is say so. The signal is what turns an authored act into a send that leaves in seconds rather than at the account's next synchronization run, and it is deliberately the last thing that happens: a signal raised before the commit would point a delivery pass at a record that does not exist yet, and a signal that is refused or lost costs the send the wait until that run rather than the send itself.

A send written for a later time says so differently, and this is the whole of what holding one costs. The record is already unclaimable until that instant, so what is missing is somebody to notice the instant arriving — and that is a job on the durable queue, made available at the time the message is due. No timer, no scheduler, and no queue of this feature's own: a message held until Monday rides the same lease, the same capacity bounds, and the same restart behaviour as every other piece of background work, and an instance that was down when the moment came finds the job claimable the second it starts.