Skip to main content

Entries

A Ledger Entry is the fundamental record stored by Ledgerly.
Each entry represents a single, immutable business event that occurred in your application.

Examples:

  • An invoice was created
  • A payment was approved
  • A user logged in
  • A subscription was canceled

Entries are designed to answer:

  • What happened?
  • Who did it?
  • What was affected?
  • When did it happen?
  • What changed?

Structure of an Entry

Each Ledger Entry contains structured fields:

FieldDescription
idUnique identifier
actionStructured action name
actor_type / actor_idWho performed the action
target_type / target_idWhat was affected
diffAttribute changes (optional)
metadataAdditional structured data
severityImportance level (optional)
created_atTimestamp

Entries are stored in the database and exposed through the LedgerEntry model.


Immutability

Ledger entries are immutable.

Once recorded:

  • They cannot be updated
  • They cannot be deleted
  • Their contents never change

This ensures:

  • Audit reliability
  • Compliance readiness
  • Trustworthy history

If a correction is needed, a new entry should be logged rather than modifying an existing one.


Action Names

Every entry must include an action:

ledgerly()->action('billing.invoice.created')->log();

Action names follow a structured format:

<domain>.<resource>.<verb>

Examples:

billing.invoice.created
billing.invoice.updated
auth.user.logged_in
billing.payment.approved

Structured actions make filtering and reporting easier.

More details are covered in the Actions section.


Actors

The actor represents who performed the action.

Example:

ledgerly()
->actor(auth()->user())
->action('billing.invoice.created')
->log();

Actors are stored as polymorphic relationships, so they can reference any model.

Actors are optional; some events may be system-generated.


Targets

The target represents what was affected.

Example:

ledgerly()
->actor(auth()->user())
->target($invoice)
->action('billing.invoice.updated')
->log();

Targets are also polymorphic and optional.


Diff Tracking

Entries can record what changed:

ledgerly()
->action('billing.invoice.updated')
->withDiff([
'amount' => [100, 150],
'status' => ['draft', 'sent'],
])
->log();

Each diff value is stored as:

[field => [before, after]]

Diffs allow building activity timelines and change histories.


Metadata

Metadata provides additional structured information about the event.

Examples:

  • Request details
  • Environment
  • Correlation identifiers
  • Custom application data

Metadata can be added manually:

ledgerly()
->action('billing.invoice.sent')
->withMetadata([
'channel' => 'email',
])
->log();

Ledgerly also collects metadata automatically using resolvers.

More details are covered in the Metadata section.


Severity

Entries may include a severity level:

ledgerly()
->severity('warning')
->action('billing.invoice.overdue')
->log();

Severity can be used in:

  • dashboards
  • alerts
  • filtering
  • UI timelines

Severity is optional.


Correlation

Entries can be grouped together using transactions:

ledgerly()->transaction(function () {

ledgerly()->action('billing.invoice.created')->log();
ledgerly()->action('billing.invoice.sent')->log();

});

Grouped entries share a correlation identifier, allowing reconstruction of workflows.

More details are covered in the Transactions section.


Exporting Entries

Entries can be exported:

$entry->toArray();
$entry->toJson();

Exports preserve:

  • metadata
  • diffs
  • severity
  • correlation

This makes Ledgerly suitable for:

  • compliance exports
  • analytics
  • backups

Best Practices

Good entries are:

  • specific
  • structured
  • consistent in naming
  • rich in metadata when needed

Avoid:

  • generic action names like updated
  • storing unstructured text logs
  • modifying existing entries

Next Step

Continue to:

➡️ Actions